UWP開發入門(十)——通過繼承來擴展ListView


  本篇之所以起這樣一個名字,是因為重點並非如何自定義控件,不涉及創建CustomControlUserControl使用的TemplateXAML概念。而是通過繼承的方法來擴展一個現有的類,在繼承的子類中增加屬性和擴展行為。

  我們在《UWP開發入門(七)——下拉刷新》中提到過嵌套ScrollViewer的實現思路,本篇我們對ListView的第一個擴展行為,即是摒棄嵌套的做法,而是通過訪問ListView內部的ScrollViewer控件,來監聽ViewChanged事件。

  訪問ListView內部的ScrollViewer,必定離不開VisualTreeHelper類中的以下兩個方法:  

public static DependencyObject GetChild(DependencyObject reference, System.Int32 childIndex);

public static System.Int32 GetChildrenCount(DependencyObject reference);

  可以將這兩個方法進一步組合得到:

        static T FindFirstChild<T>(FrameworkElement element) where T : FrameworkElement
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(element);
            var children = new FrameworkElement[childrenCount];

            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
                children[i] = child;
                if (child is T)
                    return (T)child;
            }

            for (int i = 0; i < childrenCount; i++)
                if (children[i] != null)
                {
                    var subChild = FindFirstChild<T>(children[i]);
                    if (subChild != null)
                        return subChild;
                }

            return null;
        }

  該方法通過遞歸來遍歷FrameworkElement內部的元素,並返回第一個符合類型的元素。ListViewEx第一個擴展如下:

    public class ListViewEx : ListView, INotifyPropertyChanged
    {
        private ScrollViewer _scrollViewer;

        public event EventHandler LoadHistoryEvent;
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnProperyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public ListViewEx()
        {
            this.Loaded += ListViewEx_Loaded;
            this.Unloaded += ListViewEx_Unloaded;
        }

        private void ListViewEx_Unloaded(object sender, RoutedEventArgs e)
        {
            this.Unloaded -= ListViewEx_Unloaded;
            if (_scrollViewer != null)
            {
                _scrollViewer.ViewChanged -= Sv_ViewChanged;
            }
        }

        private void ListViewEx_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            this.Loaded -= ListViewEx_Loaded;
            _scrollViewer = FindFirstChild<ScrollViewer>(this);
            if (_scrollViewer != null)
            {
                _scrollViewer.ViewChanged += Sv_ViewChanged;
            }
        }

        private async void Sv_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
        {
            if (e.IsIntermediate == false && _scrollViewer.VerticalOffset < 1)
            {
                _scrollViewer.ChangeView(null, 50, null);
                await Task.Delay(10);
                LoadHistoryEvent?.Invoke(this, EventArgs.Empty);
            }
        }

  嗯嗯,可以看到優雅的 -= event和恰到好處的null check,啊啊!忍不住想點個贊!在ViewChanged事件監測到滾動條到達頂部后,果斷觸發ListViewEx內定義的LoadHistoryEvent來通知更新數據。

  本篇如果僅增加一個LoadHistoryEvent,又會被人非議是在補完前篇,一篇拆成兩篇寫……那好吧,我們再給ListViewEx增加第二個擴展GoBottomVisiblity屬性,顧名思義即是ListViewEx向上滾動幾屏以后,顯示一個GoBottom的按鈕。

  在ListViewEx的類中,首先定義屬性GoBottomVisibility,然后同樣是在ViewChanged事件中,計算是否顯示GoBottom按鈕。

        public Visibility GoBottomVisiblity
        {
            get { return _goBottomVisiblity; }
            set
            {
                _goBottomVisiblity = value;
                this.OnProperyChanged();
            }
        }
        private void Sv_ViewChanged2(object sender, ScrollViewerViewChangedEventArgs e)
        {
            if (e.IsIntermediate == false)
            {
                CheckGoBottomVisibility();
            }
        }

        private void CheckGoBottomVisibility()
        {
            if (_scrollViewer.VerticalOffset + _scrollViewer.ViewportHeight < _scrollViewer.ScrollableHeight)
            {
                GoBottomVisiblity = Visibility.Visible;
            }
            else
            {
                GoBottomVisiblity = Visibility.Collapsed;
            }
        }

  代碼沒法全部貼上來,一個是太長了顯得啰嗦,二是會被管理員說沒內涵從首頁刪掉……

  大體上本篇就是給ListView擴展了LoadHistoryEvent事件和GoBottomVisibility屬性。最后說說怎么用,XAML里使用ListViewEx代替默認的ListView,會發現多出一個LoadHistoryEvent,掛上加載數據的事件就OK了。然后在列表的下部畫一個三角箭頭,Visibility綁定到ListViewExGoBottomVisibility屬性上就收工了。

  

    <Grid>
        <local:ListViewEx x:Name="listViewEx" ItemsSource="{x:Bind Items}" LoadHistoryEvent="ListViewEx_LoadHistoryEvent"></local:ListViewEx>
        <Grid Margin="10" VerticalAlignment="Bottom" HorizontalAlignment="Center" 
              Tapped="Grid_Tapped" Visibility="{x:Bind listViewEx.GoBottomVisiblity,Mode=OneWay}">
            <Ellipse Fill="LightGray" Width="30" Height="30"></Ellipse>
            <Polyline Stroke="White" Points="10,10 15,20 20,10"></Polyline>
        </Grid>
    </Grid>

  完整代碼放在GayHub上,地址:https://github.com/manupstairs/UWPSamples

  非常感謝各位捧場,能夠點開頁面看到這里,拜謝了!Orz


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM