代码分享 ScrollViewerListener 获取ScrollViewer的位置改变
WP没有办法直接获取ScrollViewer的位置,但是可以ScrollViewer通过VerticalOffset的绑定监听实现。
下面写了一个ScrollViewerListener 用于给ScrollViewer扩展一个VerticalScrollChanged事件。
思路是这样的:
1 首先给ScrollViewer附加2个属性VerticalOffsetProperty和ListenerProperty.
2 VerticalOffsetProperty是VerticalOffset的复制,但是VerticalOffsetProperty在meta里定义了自己的回调OnVerticalOffsetChanged,
然后利用Binding的特点 将Source 设置成ScrollViewer,使ta的VerticalOffset和ScrollViewerListener引用的VerticalOffsetProperty绑定起来,
这样ScrollViewer的VerticalOffset也会使VerticalOffsetProperty同时改变,并能在OnVerticalOffsetChanged回调里面处理了。
3 ListenerProperty就是ScrollViewerListener本身的引用。
在处理OnVerticalOffsetChanged回调的时候有个问题,就是sender是ScrollViewer怎么取到ScrollViewerListener呢?
通过ListenerProperty就可以把ScrollViewerListener附加给ScrollViewer这样就可以取得ScrollViewerListener的引用了。
也不会因为在静态方法引用了ScrollViewerListener造成GC不能回收(网上有一种在构造函数使用lambda的方法会产生这种问题)。
4 最后取回ScrollViewerListener的引用,并引发VerticalScrollChanged事件。功能就完成了。

/// <summary> /// ScrollViewerListener /// </summary> internal class ScrollViewerListener { /// <summary> /// 附加ScrollViewerListener /// </summary> public static readonly DependencyProperty ListenerProperty; /// <summary> /// 附加VerticalOffset /// </summary> private static readonly DependencyProperty VerticalOffsetProperty; static ScrollViewerListener() { ListenerProperty = DependencyProperty.RegisterAttached("Listener", typeof (ScrollViewerListener), typeof (ScrollViewer), new PropertyMetadata(default(ScrollViewerListener))); VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset", typeof (double), typeof (ScrollViewer), new PropertyMetadata(default(double), OnVerticalOffsetChanged)); } /// <summary> /// ScrollViewer监听器 /// </summary> /// <param name="scrollViewer">ScrollViewer</param> public ScrollViewerListener(ScrollViewer scrollViewer) { if (scrollViewer == null) return; //附加ScrollViewerListener给ScrollViewer scrollViewer.SetValue(ListenerProperty, this); //附加绑定ScrollViewer.VerticalOffset给VerticalOffsetProperty scrollViewer.SetBinding(VerticalOffsetProperty, new Binding("VerticalOffset") { Source = scrollViewer }); } private static void OnVerticalOffsetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var scrollViewer = sender as ScrollViewer; if (scrollViewer != null && e.OldValue != e.NewValue) { //从scrollViewer取回ScrollViewerListener var listener = scrollViewer.GetValue(ListenerProperty) as ScrollViewerListener; if (listener != null) { listener.OnVerticalScrollChanged(); } } } /// <summary> /// 纵向滚动事件 /// </summary> public event EventHandler VerticalScrollChanged; private void OnVerticalScrollChanged() { var tmp = VerticalScrollChanged; if (tmp != null) tmp(this, EventArgs.Empty); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库