WPF 控件库——带有惯性的ScrollViewer*(转)
转:https://blog.csdn.net/ahilll/article/details/82418892
一、先看看效果

二、原理
虽然效果很简单,但是网上的一些资料涉及的代码量非常可观,而且效果也不是很理想,滚动的时候没有一个顺滑感。我这里提供的源码一共120多行,就能实现上图的效果。
本质上我们只要接管ScrollViewer的滚动逻辑,并且把这个逻辑替换成带有惯性的即可,那么如何去接管呢?这里的关键是先屏蔽ScrollViewer的鼠标滚轮事件:
1 protected override void OnMouseWheel(MouseWheelEventArgs e)
2 {
3 e.Handled = true;
4 }
这样一来,ScrollViewer就不会响应滚轮事件了,我们就在这里做文章。首先我们给这个ScrollViewer添加一个属性 IsEnableInertia ,用来控制是否使用惯性,因为萝卜青菜各有所爱,不要想着强制所有人使用惯性,所以滚轮响应方法变为:
1 protected override void OnMouseWheel(MouseWheelEventArgs e)
2 {
3 if (!IsEnableInertia)
4 {
5 base.OnMouseWheel(e);
6 return;
7 }
8 e.Handled = true;
9 }
控制ScrollViewer的垂直滚动可以使用 ScrollViewer.ScrollToVerticalOffset ,横向也一样。为什么不能用 VerticalOffset ?因为 VerticalOffset 在注册的时候就说明了是只读的:
1 private static readonly DependencyPropertyKey VerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly(nameof (VerticalOffset), typeof (double), typeof (ScrollViewer), (PropertyMetadata) new FrameworkPropertyMetadata((object) 0.0));
2
3 public static readonly DependencyProperty VerticalOffsetProperty = ScrollViewer.VerticalOffsetPropertyKey.DependencyProperty;
好了,接下来就是怎么在滚轮响应方法中实现惯性运动了,也就是一种减速运动。想到这儿,熟悉动画的博友很快就知道要用WPF的动画来实现了,默认的动画都是一次线性的,要有惯性效果就得用缓动函数,WPF的缓动函数有很多,而 CubicEase 非常适合用来做惯性,它的描述图如下:

图中,横轴表示时间,纵轴表示运动距离。很明显,中间的 EaseOut 模式就是我们想要的。到了这里思路就清晰了,我们可以定义一个属性 CurrentVerticalOffset ,我们会在它上面实现动画,在它的值回调函数中调用 ScrollViewer.ScrollToVerticalOffset 来更新ScrollViewer的滚动位置。当然我们还需要一个私有字段 _totalVerticalOffset ,这个是用来存放ScrollViewer滚动目标位置的,滚轮向下滚动一个单位我们就给它减去一次 e.Delta ,这里的e是滚轮响应方法传进来的参数,每次给它赋值之后,就可以在 CurrentVerticalOffset 上执行动画了: BeginAnimation(CurrentVerticalOffsetProperty, animation) ,需要特别注意的是,当一个依赖属性用了动画改变后,再对其赋值则不会生效,原因是在一个动画到达活动期的终点后,时间线默认会保持其进度,直到其父级的活动期和保持期结束为止。如果想在动画结束后还可以手动更改依赖属性的值,则需要把 FillBehavior 设置为Stop。不过这样又会出现一个问题,一旦动画结束,这个依赖属性又会恢复初始值,所以还要给这个动画订阅一个 Completed 事件,在事件响应方法中为 CurrentVerticalOffset 给定目标值,也就是 _totalVerticalOffset 。
最后还有一个冲突问题,当手动拖动滑块或者当用上下文菜单改变滚动条位置时是不能用动画的,因为这时候没有触发 OnMouseWheel ,没关系,这正是我们想要的,但是如果再次触发 OnMouseWheel 就有问题了,因为手动触发滚动的时候我们没有给 CurrentVerticalOffset 和 _totalVerticalOffset 赋值( CurrentVerticalOffset 和 _totalVerticalOffset 只在 OnMouseWheel 中赋值),所以在用动画执行滚动操作前要先判断一下是否需要先更新一下它们俩,如何判断?我们可以用一个私有字段 _isRunning 来维护状态,每当动画开始就给它赋值true,结束则赋值false。这样一来,当 _isRunning = false 时,说明在调用 OnMouseWheel 前,动画已经结束,用户可能已经手动改变了滚动条位置(也可能没有,但这并不影响),所以就要给之前俩兄弟更新一下值了。
因为常见的惯性滚动以垂直方向居多,所以我没有写水平方向的逻辑,但也很容易扩展,有兴趣的博友可以下载源代码自己研究。
三、源码
本文所讨论的控件源码已经在github开源:https://github.com/NaBian/HandyControl
WPF 控件库——带有惯性的ScrollViewer*(转)的更多相关文章
- WPF 控件库——带有惯性的ScrollViewer
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- WPF 控件库——仿制Windows10的进度条
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- WPF 控件库——仿制Chrome的ColorPicker
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- WPF 控件库——轮播控件
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- WPF 控件库——可拖动选项卡的TabControl
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- 《Dotnet9》系列-开源C# WPF控件库3《HandyControl》强力推荐
大家好,我是Dotnet9小编,一个从事dotnet开发8年+的程序员.我最近开始写dotnet分享文章,希望能让更多人看到dotnet的发展,了解更多dotnet技术,帮助dotnet程序员应用do ...
- 我的WPF控件库——KAN.WPF.XCtrl(141105)
自己开发的WPF控件库,只是初版,有扩展的Button,TextBox,Window.详细参见前几篇博文. WPF自定义控件(一)——Button:http://www.cnblogs.com/Qin ...
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) 背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...
- 国内开源C# WPF控件库Panuon.UI.Silver推荐
国内优秀的WPF开源控件库,Panuon.UI的优化版本.一个漂亮的.使用样式与附加属性的WPF UI控件库,值得向大家推荐使用与学习. 今天站长(Dotnet9,站长网址:https://dotne ...
随机推荐
- linux减少服务器带宽的方法
linux减少服务器带宽的方法用百度静态资源公共库http://cdn.code.baidu.com/ 不仅可以不使用服务器流量 而且还有cdn加速比方说http://apps.bdimg.com/l ...
- 最新超简单解读torchvision
torchvision https://pytorch.org/docs/stable/torchvision/index.html#module-torchvision The torchvisio ...
- c#学习笔记1-简单算法
using System; namespace Demo { class Studycs { public static void Main(String[] args) { // String re ...
- 递归实现全排列python
python递归实现"abcd"字符串全排列 1.保持a不动,动bcd 2.保持b不动,动cd 3.保持c不动,动d def pailie(head="",st ...
- Vue框架(三)——Vue项目搭建和项目目录介绍、组件、路由
Vue项目环境搭建 1) 安装node,在官网下载好,然后在本地安装 官网下载安装包,傻瓜式安装:https://nodejs.org/zh-cn/ 2) 换源安装cnpm >: npm ins ...
- 不会前后端,用vps搭建个人博客(二)
<接上一篇> 四.添加网页内容 1.下载安装WordPress 输入以下命令: wget https://wordpress.org/latest.tar.gz 当然你也可以用浏览器进 ...
- DS 图解快排
快速排序是交换排序,是冒泡排序的改进版. 快排过程: 1.选定一个分界值 2.分成三个部分(小于分界部分,分界值,大于分界值部分) 3.对于分开的两 ...
- 【Go】开发中遇到的坑——持续更新
关于CGo多语言编译 问题出现在将openCV封装到go语言的时候.在编译时需要设置 CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o xxx mai ...
- Java核心技术梳理-类加载机制与反射
一.引言 反射机制是一个非常好用的机制,C#和Java中都有反射,反射机制简单来说就是在程序运行状态时,对于任意一个类,能够知道这个类的所有属性和方法,对于任意一个对象,能够调用它的任意属性和方法,其 ...
- string.Compare()方法
判断字符串中是否包含一个值 返回一个值,该值指示指定的 String 对象是否出现在此字符串中. String a = "abcd"; if(source.a("a&qu ...