本篇之所以起这样一个名字,是因为重点并非如何自定义控件,不涉及创建CustomControl和UserControl使用的Template和XAML概念。而是通过继承的方法来扩展一个现有的类,在继承的子类中增加属性和扩展行为。

  我们在《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 = ; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
children[i] = child;
if (child is T)
return (T)child;
} for (int i = ; 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 < )
{
_scrollViewer.ChangeView(null, , null);
await Task.Delay();
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绑定到ListViewEx的GoBottomVisibility属性上就收工了。

  

    <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

最新文章

  1. Windows Live Writer 在线安装失败的解决方法。
  2. 【原】iOS动态性(四):一行代码实现iOS序列化与反序列化(runtime)
  3. CSS3与页面布局学习总结(四)——页面布局大全BFC、定位、浮动、7种垂直居中方法
  4. Pycharm使用问题# 快捷键设置
  5. IE Unknown runtime error
  6. CodeForces 589A Email Aliases (匹配,水题)
  7. python 调试
  8. J2SE知识点摘记(二十一)
  9. php学习笔记——基础知识(1)
  10. IOS 播放视频 MPMoviePlayerController
  11. java 基础知识五 数组
  12. 拦截器的四种拦截方式以及Filter的执行顺序(17/4/8)
  13. 笔记evernote
  14. 关于Java JDK中 URLDecoder.decode 方法
  15. 机器学习--------SVM
  16. Python开发【第一篇】基础题目一
  17. width() 、 height() 方法;innerWidth() 、innerHeight() 方法;outerWidth() 、 outerHeight() 方法的区别
  18. [xdoj]1299&amp;1300朱神的烦恼 朱神的序列
  19. 包(package),继承
  20. Confluence 6 为站点禁用匿名用户访问

热门文章

  1. django-创建表的字段属性,表关系
  2. asp.net webapi 参数绑定总结
  3. 新手C#string类常用函数的学习2018.08.04
  4. Hadoop之MapReduce学习笔记(二)
  5. CMD-SVN查看版本修改记录
  6. 如何在eclipse中添加android ADT(转)
  7. Unity3D 游戏在 iOS 上因为 trampolines 闪退的原因与解决办法
  8. rsync同步常用命令[转载]
  9. 2016年,你读过的最好的IT技术书有哪几本?
  10. Chrome Command Line API 参考