1. 程式人生 > >WPF: 實現 ScrollViewer 滾動到指定控件處

WPF: 實現 ScrollViewer 滾動到指定控件處

section 使用 -a int pre ans eve 移位 div

原文:WPF: 實現 ScrollViewer 滾動到指定控件處

在前端 UI 開發中,有時,我們會遇到這樣的需求:在一個 ScrollViewer 中有很多內容,而我們需要實現在執行某個操作後能夠定位到其中指定的控件處;這很像在 HTML 頁面中點擊一個鏈接後定位到當前網頁上的某個 anchor。

要實現它,首先我們需要看 ScrollViewer 為我們提供的 API,其中並沒有類似於 ScrollToControl 這樣的方法;在它的幾個以 ScrollTo 開頭的方法中,最合適的就是 ScrollToVerticalOffset 這個方法了,這個方法接受一個參數,即縱向的偏移位置。那麽,很重要的問題:我們怎麽能得到要定位的那個控件在 ScrollViewer 中的位置呢?

在我之前寫的這篇文章中:XAML: 獲取元素的位置,有如何獲到元素相對位置的介紹,建議大家先了解一下,其中使用了 Visual.TransformToVisual 方法等。當你理解了這篇文章後,再回過頭來看本文後面的內容,就很容易了。

接下來,我們使用以下代碼,即可實現上述需求:

           // 獲取要定位之前 ScrollViewer 目前的滾動位置
            var currentScrollPosition = ScrollViewer.VerticalOffset;
            var point = new Point(0, currentScrollPosition);

            
// 計算出目標位置並滾動 var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point); ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);

另外,由於通常情況下,我們會采用 MVVM 模式,因此我們可以將上述代碼封裝成一個 Action,而避免在 Code-Behind 代碼文件中添加上述代碼。

新創建的名為 ScrollToControlAction 的 Action,在其中定義兩個依賴屬性 ScrollViewer 和 TargetControl,分別表示指定的要操作的 ScrollViewer 和要定位到的控件,然後將上述代碼放到其 Invoke 方法中即可。由於 Action 並非本文主題,所以這裏並不會展開太多的講解,可以參考以下代碼或本文後提供的 Demo 作進一步了解。

技術分享圖片
namespace ScrollTest
{
    /// <summary>
    /// 在 ScrollViewer 中定位到指定的控件
    /// 說明:目前支持的是垂直滾動
    /// </summary>
    public class ScrollToControlAction : TriggerAction<FrameworkElement>
    {
        public static readonly DependencyProperty ScrollViewerProperty =
            DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollToControlAction), new PropertyMetadata(null));

        public static readonly DependencyProperty TargetControlProperty =
            DependencyProperty.Register("TargetControl", typeof(FrameworkElement), typeof(ScrollToControlAction), new PropertyMetadata(null));

        /// <summary>
        /// 目標 ScrollViewer
        /// </summary>
        public ScrollViewer ScrollViewer
        {
            get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
            set { SetValue(ScrollViewerProperty, value); }
        }

        /// <summary>
        /// 要定位的到的控件
        /// </summary>
        public FrameworkElement TargetControl
        {
            get { return (FrameworkElement)GetValue(TargetControlProperty); }
            set { SetValue(TargetControlProperty, value); }
        }

        protected override void Invoke(object parameter)
        {
            if (TargetControl == null || ScrollViewer == null)
            {
                throw new ArgumentNullException($"{ScrollViewer} or {TargetControl} cannot be null");
            }

            // 檢查指定的控件是否在指定的 ScrollViewer 中
            // TODO: 這裏只是指定離它最近的 ScrollViewer,並沒有繼續向上找
            var container = TargetControl.FindParent<ScrollViewer>();
            if (container == null || container != ScrollViewer)
            {
                throw new Exception("The TargetControl is not in the target ScrollViewer");
            }

            // 獲取要定位之前 ScrollViewer 目前的滾動位置
            var currentScrollPosition = ScrollViewer.VerticalOffset;
            var point = new Point(0, currentScrollPosition);

            // 計算出目標位置並滾動
            var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
            ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);
        }
    }
}
ScrollToControlAction.cs

其使用方法如下:

<Button>
    <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <local:ScrollToControlAction ScrollViewer="{Binding ElementName=s}" TargetControl="{Binding ElementName=txtSectionC}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
</Button>

至此,結合 Action,我們以非常靈活的方式實現了本文所提出的需求。

源碼下載

WPF: 實現 ScrollViewer 滾動到指定控件處