热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[置顶]滚动条滑到底完美解决方案(适合任何带滚动条或ScrollBar控件

看见很多人在求滚动条滑倒底部自动加载数据的解决方案,各种各样的方案很多,但令人满意的确没几个。在这里我分享一个我的自认为满意的解决方案。首先说下大致原理

  看见很多人在求滚动条滑倒底部自动加载数据的解决方案,各种各样的方案很多,但令人满意的确没几个。在这里我分享一个我的自认为满意的解决方案。

首先说下大致原理:监视滚动条坐标的变化,在达到底部时触发自己的处理事件。
原理很简单,但实现起来可没这么容易,先上代码,边看边说。

public class ScrollViewerTrigger:TriggerBase{ScrollViewer ScrollView;public static readonly DependencyProperty DirectionTypeProperty = DependencyProperty.Register("DirectionType", typeof(DirectionType), typeof(ScrollViewerTrigger), new PropertyMetadata(DirectionType.Bottom));public DirectionType DirectionType{get{return (DirectionType)base.GetValue(ScrollViewerTrigger.DirectionTypeProperty);}set{base.SetValue(ScrollViewerTrigger.DirectionTypeProperty, value);}}public event EventHandler ScrollTrigger;public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register("VerticalOffset", typeof(double), typeof(ScrollViewerTrigger), new PropertyMetadata(0.0, new PropertyChangedCallback(VerticalOffsetPropertyChanged)));public double VerticalOffset{get{return (double)base.GetValue(ScrollViewerTrigger.VerticalOffsetProperty);}set{base.SetValue(ScrollViewerTrigger.VerticalOffsetProperty, value);}}public static void VerticalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var behavior = d as ScrollViewerTrigger;if (behavior != null)behavior.OnVerticalOffsetChanged();}protected override void OnAttached(){base.OnAttached();if (this.AssociatedObject != null && this.AssociatedObject is FrameworkElement){(this.AssociatedObject as FrameworkElement).SizeChanged += control_SizeChanged;}}void control_SizeChanged(object sender, SizeChangedEventArgs e){if (this.AssociatedObject == null || !(this.AssociatedObject is FrameworkElement))return;ScrollViewer Scroll = this.AssociatedObject.GetFirstDescendantOfType();if (Scroll != null){AttachedScroll(Scroll);(this.AssociatedObject as FrameworkElement).SizeChanged -= control_SizeChanged;}}void AttachedScroll(ScrollViewer Scroll){if (Scroll == null)return;ScrollView = Scroll;Binding binding = new Binding();binding.Source = Scroll;binding.Path = new PropertyPath("VerticalOffset");BindingOperations.SetBinding(this, ScrollViewerTrigger.VerticalOffsetProperty, binding);}void OnVerticalOffsetChanged(){ScrollViewer Scroll = ScrollView;if (Scroll == null)return;switch (DirectionType){case DirectionType.Top:{if (Scroll.ScrollableHeight double.Epsilon)return;}break;case DirectionType.Bottom:{if (Scroll.ScrollableHeight double.Epsilon)return;}break;case DirectionType.Right:{if (Scroll.ScrollableWidth

   整个实现是Trigger的扩展,这样灵活性和通用性非常高,不管是什么控件。因为Trigger本身是附加元素,对于任何控件都可以附加上去,不要要改动原有代码就可以实现功能扩展。Trigger自带的Actions则有很强的灵活性,动作触发后可以执行多个Action.

DirectionType是触发方向,分上下左右4个方向,默认是在底部触发,想做下拉刷新功能时把DirectionType改成Top就可以了了。
VerticalOffset则是实现滚动条监视的关键,什么作用稍后说明。
OnAttached()是整个Trigger的入口,在这里面我监控了一个通用事件SizeChanged,主要是通过这个事件查找控件内的ScrollViewer控件。这里不能使用loaded事件,因为有些带滚动条的控件是在加载元素时才建立ScrollViewer控件, loaded事件触发时内部元素不一定会被加载,而   SizeChanged触发时 ScrollViewer已经建立好了。
  control_SizeChanged中关键语句在于 this.AssociatedObject.GetFirstDescendantOfType();这语句是从当前控件的子控件中查找ScrollViewer控件,查找到后就会调用AttachedScroll对 ScrollViewer的滚动进行监视。
  下面就是关键了,AttachedScroll函数要对 ScrollViewer进行监控,那么如何监控呢?

void AttachedScroll(ScrollViewer Scroll){if (Scroll == null)return;ScrollView = Scroll;Binding binding = new Binding();binding.Source = Scroll;binding.Path = new PropertyPath("VerticalOffset");BindingOperations.SetBinding(this, ScrollViewerTrigger.VerticalOffsetProperty, binding);}

  还记得我之前提到的VerticalOffset属性吗,我通过绑定将ScrollViewer中的 VerticalOffset属性与这个 Trigger中的VerticalOffset绑定在一起,这样ScrollViewer种的 VerticalOffset一旦有变化我这边可以实时指导。ScrollViewer中是没有 VerticalOffset变化通知的事件的,但通过绑定则可以解决这个问题。若做横向监控功能, binding.Path = new PropertyPath("VerticalOffset");换成 binding.Path = new PropertyPath("HorizontalOffset");就可以了。

  关键一步解决了剩下的就好办了, VerticalOffset一旦有变化则会调用到VerticalOffsetPropertyChanged函数,然后再调用OnVerticalOffsetChanged对坐标进行处理。
  OnVerticalOffsetChanged中会对滚动条的当前位置进行筛选,若滚动条滑倒了底部就会触发Trigger自带的Actions,我这里提供一个事件ScrollTrigger,在达到底部时也会触发,方便外部使用。
  使用起来很简单,假如我们的viewmodel中有一个加载更多数据的命令,使用如下

  有些控件中没有ScrollViewer控件,但是有ScrollBar控件,我这里提供一个ScrollBarTrigger,原理与ScrollViewerTrigger稍有不同,哪里不同自己研究。另外ScrollBarTrigger比ScrollViewerTrigger更具通用性,因为ScrollViewer中包含ScrollBar。

下面是 ScrollBarTrigger代码:

public enum DirectionType { Top, Bottom, Left, Right }public class ScrollBarTrigger : TriggerBase{ScrollBar ScrollView;public static readonly DependencyProperty DirectionTypeProperty = DependencyProperty.Register("DirectionType", typeof(DirectionType), typeof(ScrollBarTrigger), new PropertyMetadata(DirectionType.Bottom));public DirectionType DirectionType{get{return (DirectionType)base.GetValue(ScrollBarTrigger.DirectionTypeProperty);}set{base.SetValue(ScrollBarTrigger.DirectionTypeProperty, value);}}public event EventHandler ScrollTrigger;protected override void OnAttached(){base.OnAttached();if (this.AssociatedObject != null && this.AssociatedObject is FrameworkElement){(this.AssociatedObject as FrameworkElement).SizeChanged += control_SizeChanged;}}protected override void OnDetaching(){base.OnDetaching();if (ScrollView != null)ScrollView.ValueChanged -= ScrollView_ValueChanged;if (this.AssociatedObject != null && this.AssociatedObject is FrameworkElement){(this.AssociatedObject as FrameworkElement).SizeChanged -= control_SizeChanged;}}void control_SizeChanged(object sender, SizeChangedEventArgs e){if (this.AssociatedObject == null || !(this.AssociatedObject is FrameworkElement))return;ScrollBar Scroll = this.AssociatedObject.GetFirstDescendantOfType();if (Scroll != null){AttachedScroll(Scroll);(this.AssociatedObject as FrameworkElement).SizeChanged -= control_SizeChanged;}}void AttachedScroll(ScrollBar Scroll){if (Scroll != null){ScrollView = Scroll;ScrollView.ValueChanged += ScrollView_ValueChanged;}}void ScrollView_ValueChanged(object sender, RoutedPropertyChangedEventArgs e){OnOffsetChanged();}void OnOffsetChanged(){if (ScrollView == null)return;ScrollBar Scroll = ScrollView;switch (DirectionType){case DirectionType.Top:case DirectionType.Left:{if (Scroll.Maximum 0)return;}break;case DirectionType.Bottom:case DirectionType.Right:{if (Scroll.Maximum

  如果对Action,Trigger,Behavior有不了解的朋友可以看我之前的博客,会有详细解释。

示例里的工程名誉我将的有写不一样,因为我复用了之前的示例,但不影响我博客里的内容
示例: Test.zip

转:https://www.cnblogs.com/jiangu66/archive/2013/05/10/3071810.html



推荐阅读
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 工作经验谈之-让百度地图API调用数据库内容 及详解
    这段时间,所在项目中要用到的一个模块,就是让数据库中的内容在百度地图上展现出来,如经纬度。主要实现以下几点功能:1.读取数据库中的经纬度值在百度上标注出来。2.点击标注弹出对应信息。3 ... [详细]
  • 涉及的知识点-ViewGroup的测量与布局-View的测量与布局-滑动冲突的处理-VelocityTracker滑动速率跟踪-Scroller实现弹性滑动-屏幕宽高的获取等实现步 ... [详细]
  • 百度地图   绘制东莞东城地图示例
    先上图:index.html ... [详细]
  • Android仿微信右滑返回功能的实例代码
    Android仿微信右滑返回功能的实例代码-先上效果图,如下:先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有 ... [详细]
  • WPF菜鸟谈之依赖属性,附加属性(附加《深入浅出WPF》pdf下载)
    我们都知道.NET中有属性(Property)这个概念。在MSDN中是这样定义属性的:属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 感谢大家对IT十八掌大数据的支持,今天的作业如下:1.实践PreparedStament的CRUD操作。2.对比Statement和PreparedStatement的大批量操作耗时?(1 ... [详细]
  • Matlab 中的一些小技巧(2)
    1.Ctrl+D打开子程序  在MATLAB的Editor中,将输入光标放到一个子程序名称中间,然后按Ctrl+D可以打开该子函数的m文件。当然这个子程序要在路径列表中(或在当前工作路径中)。实际上 ... [详细]
  • 作者一直强调的一个概念叫做oneloopperthread,撇开多线程不谈,本篇博文将学习,怎么将传统的IO复用pollepoll封装到C++类中。1.IO复用复习使用p ... [详细]
  • 初始化_SQL Server 2017 AlwaysOn AG 自动初始化
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了SQLServer2017AlwaysOnAG自动初始化相关的知识,希望对你有一定的参考价值。 ... [详细]
  • XAF新手入门类型子系统(Types Info Subsystem)
    XAF新手入门-类型子系统(TypesInfoSubsystem)-类型子系统概述类型子系统是XAF的核心概念,但我们平时却很少关注它,它集中存储了模块中的类型,它是生成应用程 ... [详细]
  • 一条数据的漫游 XEngine SIGMOD Paper Introduction
    大多数人追寻永恒的家园(归宿),少数人追寻永恒的航向。----瓦尔特.本雅明背景X-Engine是阿里数据库产品事业部自研的OLTP数据库存储引擎, ... [详细]
  • button进阶1.等级3的时候学习了用Grid(网格)布局排列。这次是button背景的引用学习圆角button。我发现学代码难的原因就是英语不好不懂什么意思。设置focysable属性为 ... [详细]
author-avatar
mobiledu2502908767
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有