【问题标题】:Two-Way Binding Of VerticalOffset Property on ScrollViewer?ScrollViewer 上 VerticalOffset 属性的双向绑定?
【发布时间】:2010-01-19 18:52:18
【问题描述】:

我在 Silverlight 3.0 中有一个 View 和一个 ViewModel。

视图包含一个标准的 ScrollViewer,其中包含动态内容。

根据 ScrollViewer 中的内容,用户可能已经向下滚动了一半内容,然后执行了导致 ScrollViewer 加载新内容的操作,但 ScrollViewer 不会自动滚动到顶部。

我希望能够绑定到 VerticalOffset 属性,但它是只读的。关于可附加行为的任何想法? 有什么想法吗?

谢谢。

【问题讨论】:

  • 您想在 ViewModel 上公开一个属性,指示 ScrollViewer 应该在哪里?不清楚您要将 VerticalOffset 绑定到什么?

标签: c# silverlight xaml


【解决方案1】:

以下博客文章提供了一个附加行为,该行为公开了滚动查看器的垂直/水平偏移量,以便您可以绑定到它们,或在代码中设置它们:

http://blog.scottlogic.com/2010/07/21/exposing-and-binding-to-a-silverlight-scrollviewers-scrollbars.html

这允许以下标记:

<ScrollViewer 
    local:ScrollViewerBinding.VerticalOffset="{Binding YPosition, Mode=TwoWay}"
    local:ScrollViewerBinding.HorizontalOffset="{Binding XPosition, Mode=TwoWay}">
    <!-- Big content goes here! -->
</ScrollViewer>

【讨论】:

  • 我想看一篇文章(我需要wpf解决方案来绑定scrollviewer),但是链接错误。
  • 好文章,我只用事件scrollViewer.ScrollChanged,不需要找ScrollBar
【解决方案2】:

由于您使用的是 ViewModel,我认为“导致 ScrollViewer 加载新内容的操作”是在 ViewModel 内部或对 ViewModel 进行更改的结果。在这种情况下,我会向 ViewModel 添加一个事件,每次发生此类更改时都会触发该事件。

您的视图可以在此事件上添加一个处理程序,并在其触发时在 ScrollViewer 上调用 ScrollToVerticalPosition。

【讨论】:

    【解决方案3】:

    我已经简化了@ColinE 的解决方案。我没有挂钩ScrollBar.ValueChanged 事件,而是挂钩ScrollViewer.ScrollChanged 事件。因此,1. 没有必要在可视化树中找到 ScrollBar 和 2. ScrollBar.ValueChangedScrollViewer 的内容发生变化时在某些转换状态中被调用,我不想捕捉这些状态。

    我发布了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>
    

    【讨论】:

      【解决方案4】:

      我从这个开始,但注意到没有清理阶段,所以这里是完整的实现,水平和垂直偏移均可绑定:

      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;
              }
          }
      }
      

      【讨论】:

      • 这很好,但您需要对您的属性更改功能进行 NAN 检查,例如 if (d is not ScrollViewer scrollViewer || double.IsNaN((double)e.NewValue))
      • 根据@user3190036 的建议修复
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-20
      • 2012-12-17
      • 2011-02-20
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 2016-04-09
      相关资源
      最近更新 更多