热门标签 | 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



推荐阅读
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 原文地址:https:blog.csdn.netqq_35361471articledetails84715491原文地址:https:blog.cs ... [详细]
  • 本文详细介绍了如何在Spring框架中设置事件发布器、定义事件监听器及响应事件的具体步骤。通过实现ApplicationEventPublisherAware接口来创建事件发布器,利用ApplicationEvent类定义自定义事件,并通过ApplicationListener接口来处理这些事件。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • Web动态服务器Python基本实现
    Web动态服务器Python基本实现 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • flea,frame,db,使用,之 ... [详细]
  • 本文介绍了一个使用Spring框架和Quartz调度器实现每周定时调用Web服务获取数据的小项目。通过详细配置Spring XML文件,展示了如何设置定时任务以及解决可能遇到的自动注入问题。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • 在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ... [详细]
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • 使用QT构建基础串口辅助工具
    本文详细介绍了如何利用QT框架创建一个简易的串口助手应用程序,包括项目的建立、界面设计与编程实现、运行测试以及最终的应用程序打包。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
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社区 版权所有