[英]Two-Way Binding Of VerticalOffset Property on ScrollViewer?
我在 Silverlight 3.0 中有一个视图和一个视图模型。
该视图包含一个标准的 ScrollViewer,它包含动态内容。
根据 ScrollViewer 中的内容,用户可能已经滚动到内容的一半,然后执行导致 ScrollViewer 加载新内容的操作,但 ScrollViewer 不会自动滚动到顶部。
我希望能够绑定到 VerticalOffset 属性,但它是只读的。 关于可附加行为的任何想法? 有任何想法吗?
谢谢。
以下博客文章提供了一个附加行为,该行为公开了滚动查看器的垂直/水平偏移,以便您可以绑定到它们,或在代码中设置它们:
这允许以下标记:
<ScrollViewer
local:ScrollViewerBinding.VerticalOffset="{Binding YPosition, Mode=TwoWay}"
local:ScrollViewerBinding.HorizontalOffset="{Binding XPosition, Mode=TwoWay}">
<!-- Big content goes here! -->
</ScrollViewer>
由于您使用的是 ViewModel,我认为“导致 ScrollViewer 加载新内容的操作”是内部或对 ViewModel 所做更改的结果。 在这种情况下,我会向 ViewModel 添加一个事件,每次发生此类更改时都会触发该事件。
您的视图可以在此事件上添加处理程序,并在触发时在 ScrollViewer 上调用 ScrollToVerticalPosition。
我已经简化了@ColinE 的解决方案。 我没有挂钩到ScrollBar.ValueChanged
事件,而是挂钩到ScrollViewer.ScrollChanged
事件。 所以,1. 没有必要在可视化树中找到ScrollBar
和 2. ScrollBar.ValueChanged
当ScrollViewer
的内容发生变化并且我不想捕捉这些状态时,会在某些过渡状态中调用。
我发布了VerticalOffset
代码, HorizontalOffset
是类似的:
/// <summary>
/// VerticalOffset attached property
/// </summary>
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset", typeof(double),
typeof(ScrollViewerBinding), new FrameworkPropertyMetadata(double.NaN,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnVerticalOffsetPropertyChanged));
/// <summary>
/// Just a flag that the binding has been applied.
/// </summary>
private static readonly DependencyProperty VerticalScrollBindingProperty =
DependencyProperty.RegisterAttached("VerticalScrollBinding", typeof(bool?), typeof(ScrollViewerBinding));
public static double GetVerticalOffset(DependencyObject depObj)
{
return (double)depObj.GetValue(VerticalOffsetProperty);
}
public static void SetVerticalOffset(DependencyObject depObj, double value)
{
depObj.SetValue(VerticalOffsetProperty, value);
}
private static void OnVerticalOffsetPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if (scrollViewer == null)
return;
BindVerticalOffset(scrollViewer);
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
public static void BindVerticalOffset(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(VerticalScrollBindingProperty) != null)
return;
scrollViewer.SetValue(VerticalScrollBindingProperty, true);
scrollViewer.ScrollChanged += (s, se) =>
{
if (se.VerticalChange == 0)
return;
SetVerticalOffset(scrollViewer, se.VerticalOffset);
};
}
并在 XAML 中使用它:
<ScrollViewer local:ScrollViewerBinding.VerticalOffset="{Binding ScrollVertical}">
<!-- content ... -->
</ScrollViewer>
我从这个开始,但注意到没有清理阶段,所以这里是水平和垂直偏移可绑定的完整实现:
using System.Windows;
using System.Windows.Controls;
namespace Test
{
public static class ScrollPositionBehavior
{
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached(
"HorizontalOffset",
typeof(double),
typeof(ScrollPositionBehavior),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnHorizontalOffsetPropertyChanged));
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached(
"VerticalOffset",
typeof(double),
typeof(ScrollPositionBehavior),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnVerticalOffsetPropertyChanged));
private static readonly DependencyProperty IsScrollPositionBoundProperty =
DependencyProperty.RegisterAttached("IsScrollPositionBound", typeof(bool?), typeof(ScrollPositionBehavior));
public static void BindOffset(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(IsScrollPositionBoundProperty) is true)
return;
scrollViewer.SetValue(IsScrollPositionBoundProperty, true);
scrollViewer.Loaded += ScrollViewer_Loaded;
scrollViewer.Unloaded += ScrollViewer_Unloaded;
}
public static double GetHorizontalOffset(DependencyObject depObj)
{
return (double)depObj.GetValue(HorizontalOffsetProperty);
}
public static double GetVerticalOffset(DependencyObject depObj)
{
return (double)depObj.GetValue(VerticalOffsetProperty);
}
public static void SetHorizontalOffset(DependencyObject depObj, double value)
{
depObj.SetValue(HorizontalOffsetProperty, value);
}
public static void SetVerticalOffset(DependencyObject depObj, double value)
{
depObj.SetValue(VerticalOffsetProperty, value);
}
private static void OnHorizontalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if (scrollViewer == null || double.IsNaN((double)e.NewValue))
return;
BindOffset(scrollViewer);
scrollViewer.ScrollToHorizontalOffset((double)e.NewValue);
}
private static void OnVerticalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if (scrollViewer == null || double.IsNaN((double)e.NewValue))
return;
BindOffset(scrollViewer);
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
private static void ScrollChanged(object s, ScrollChangedEventArgs se)
{
if (se.VerticalChange != 0)
SetVerticalOffset(s as ScrollViewer, se.VerticalOffset);
if (se.HorizontalChange != 0)
SetHorizontalOffset(s as ScrollViewer, se.HorizontalOffset);
}
private static void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
scrollViewer.ScrollChanged += ScrollChanged;
}
private static void ScrollViewer_Unloaded(object sender, RoutedEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
scrollViewer.SetValue(IsScrollPositionBoundProperty, false);
scrollViewer.ScrollChanged -= ScrollChanged;
scrollViewer.Loaded -= ScrollViewer_Loaded;
scrollViewer.Unloaded -= ScrollViewer_Unloaded;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.