ListBox 滑動到底部自動加載數據

jopen 12年前發布 | 25K 次閱讀 ListBox Windows Phone開發 移動開發

概述

在Windows Phone開發應用程序的過程中,常常使用ListBox來顯示列表數據。但是有的時候從數據源獲取過來的數據量過大,而且用戶有可能關心的只是前面幾條數據。在往深入了想,甚至完全不必要每次都把大量數據齊刷刷的全部從服務器端獲取到客戶端來,無論是從流量還是效率上面的考慮都不該。這個時候,從服務器獲取數據需要分頁,在客戶端顯示數據也需要在必要的時候才去顯示另一頁的數據。

聯系到使用ListBox來顯示數據,我們可能希望初始狀態下顯示少量數據,當ListBox滑動到最低端的時候,或者當ListBox滑動到最頂端的位置的時候,才去加載更多數據。

 

原理

         ListBox中有一個ScrollViewer,所以才能上下滑動的去查看ListBox中的數據。首先,獲取到這個ScrollViewer的狀態;然后在ScrollViewer狀態改變的時候,依據ScrollViewerExtentHeightVerticalOffsetViewportHeight這三個屬性值,判斷是否已經到達ListBox的頂端或者底端。

ExtentHeight,獲取ScrollViewer中所有內容的垂直大小;VerticalOffset,滾動內容的垂直偏移量;ViewportHeight,可見內容的垂直大小。如果VerticalOffset的值為0,則表示滾動到了ListBox的頂端。如果ExtentHeight-VerticalOffset<=ViewportHeight,表示滾動到了ListBox的底端。

實現

獲取ListBox中的ScrollViewer

        /// <summary>
        /// 查找容器內的所有元素,并返回第一個是T類型的元素。
        /// </summary>
        /// <typeparam name="T">返回元素類型</typeparam>
        /// <param name="container">容器</param>
        /// <returns>第一個是T類型的元素</returns>
        private T FindVisualElement<T>(DependencyObject container) where T : DependencyObject
        {
            var childQueue = new Queue<DependencyObject>();

            childQueue.Enqueue(container);

            while (childQueue.Count > 0)
            {
                var current = childQueue.Dequeue();
                T result = current as T;
                if (result != null && result != container)
                {
                    return result;
                }

                int childCount = VisualTreeHelper.GetChildrenCount(current);

                for (int childIndex = 0; childIndex < childCount; childIndex++)
                {
                    childQueue.Enqueue(VisualTreeHelper.GetChild(current, childIndex));
                }
            }

            return null;
        }

獲取ScrollViewer的狀態

        /// <summary>
        /// 獲取指定元素的指定視圖狀態組。
        /// </summary>
        /// <param name="element">目標元素</param>
        /// <param name="name">視圖狀態名</param>
        /// <returns>視圖狀態組</returns>
        private VisualStateGroup FindVisualState(FrameworkElement element, string name)
        {
            if (element == null)
            {
                return null;
            }

            var groups = VisualStateManager.GetVisualStateGroups(element);
            foreach (VisualStateGroup group in groups)
            {
                if (group.Name == name)
                {
                    return group;
                }
            }

            return null;
        }

ScrollViewer狀態改變事件

        void visualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
        {
            var visualState = e.NewState.Name;

            if (visualState == "NotScrolling")
            {
                var v1 = _scrollViewer.ExtentHeight - _scrollViewer.VerticalOffset;
                var v2 = _scrollViewer.ViewportHeight;

                if (v1 <= v2)
                {
                    // TODO: 在此處加載新數據
                }
            }
        }

頁面的Loaded事件

            _scrollViewer = FindVisualElement<ScrollViewer>(lstBoxOne);

            if (_isHookedScrollEvent)
            {
                return;
            }

            if (_scrollViewer != null)
            {
                _isHookedScrollEvent = true;

                FrameworkElement element = VisualTreeHelper.GetChild(_scrollViewer, 0) as FrameworkElement;
                if (element != null)
                {
                    VisualStateGroup visualStateGroup = FindVisualState(element, "ScrollStates");
                    visualStateGroup.CurrentStateChanged += new EventHandler<VisualStateChangedEventArgs>(visualStateGroup_CurrentStateChanged);

                }
            }

 

備注:此實例只實現了在ListBox底端刷新數據。若要實現向上滾動到頂端刷新數據,只需要按照原理中的描述,修改ScrollViewer狀態改變事件中的邏輯就可以了。

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!