• 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

  • 相关阅读:
    北京清北 综合强化班 Day1
    Noip2015 提高组 Day1
    Noip2016 提高组 Day2 T1 组合数问题
    2017.9.23 NOIP2017 金秋杯系列模拟赛 day1 T1
    [51NOD1103] N的倍数(鸽笼原理)
    [51NOD1420] 数袋鼠好有趣(贪心)
    [CF808A] Lucky Year(规律)
    [CF808B] Average Sleep Time([强行]树状数组,数学)
    [CF808C] Tea Party(贪心)
    [CF808D] Array Division(暴力,枚举)
  • 原文地址:https://www.cnblogs.com/manupstairs/p/5321390.html
Copyright © 2020-2023  润新知