簡體   English   中英

ScrollViewer 上 VerticalOffset 屬性的雙向綁定?

[英]Two-Way Binding Of VerticalOffset Property on ScrollViewer?

我在 Silverlight 3.0 中有一個視圖和一個視圖模型。

該視圖包含一個標准的 ScrollViewer,它包含動態內容。

根據 ScrollViewer 中的內容,用戶可能已經滾動到內容的一半,然后執行導致 ScrollViewer 加載新內容的操作,但 ScrollViewer 不會自動滾動到頂部。

我希望能夠綁定到 VerticalOffset 屬性,但它是只讀的。 關於可附加行為的任何想法? 有任何想法嗎?

謝謝。

以下博客文章提供了一個附加行為,該行為公開了滾動查看器的垂直/水平偏移,以便您可以綁定到它們,或在代碼中設置它們:

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>

由於您使用的是 ViewModel,我認為“導致 ScrollViewer 加載新內容的操作”是內部或對 ViewModel 所做更改的結果。 在這種情況下,我會向 ViewModel 添加一個事件,每次發生此類更改時都會觸發該事件。

您的視圖可以在此事件上添加處理程序,並在觸發時在 ScrollViewer 上調用 ScrollToVerticalPosition。

我已經簡化了@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>

我從這個開始,但注意到沒有清理階段,所以這里是水平和垂直偏移可綁定的完整實現:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM