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

  本篇之因此起這樣一個名字,是由於重點並不是如何自定義控件,不涉及建立CustomControlUserControl使用的TemplateXAML概念。而是經過繼承的方法來擴展一個現有的類,在繼承的子類中增長屬性和擴展行爲。git

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

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

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

public static System.Int32 GetChildrenCount(DependencyObject reference);

  能夠將這兩個方法進一步組合獲得:async

        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第一個擴展以下:this

    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來通知更新數據。spa

  本篇若是僅增長一個LoadHistoryEvent,又會被人非議是在補完前篇,一篇拆成兩篇寫……那好吧,咱們再給ListViewEx增長第二個擴展GoBottomVisiblity屬性,顧名思義便是ListViewEx向上滾動幾屏之後,顯示一個GoBottom的按鈕。code

  在ListViewEx的類中,首先定義屬性GoBottomVisibility,而後一樣是在ViewChanged事件中,計算是否顯示GoBottom按鈕。blog

        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

相關文章
相關標籤/搜索