WPF 控件库系列博文地址:

WPF 控件库——仿制Chrome的ColorPicker

WPF 控件库——仿制Windows10的进度条

WPF 控件库——轮播控件

WPF 控件库——带有惯性的ScrollViewer

WPF 控件库——可拖动选项卡的TabControl

一、先看看效果

二、原理

  虽然效果很简单,但是网上的一些资料涉及的代码量非常可观,而且效果也不是很理想,滚动的时候没有一个顺滑感。我这里提供的源码一共120多行,就能实现上图的效果。

  本质上我们只要接管ScrollViewer的滚动逻辑,并且把这个逻辑替换成带有惯性的即可,那么如何去接管呢?这里的关键是先屏蔽ScrollViewer的鼠标滚轮事件:

 protected override void OnMouseWheel(MouseWheelEventArgs e)
{
e.Handled = true;
}

  这样一来,ScrollViewer就不会响应滚轮事件了,我们就在这里做文章。首先我们给这个ScrollViewer添加一个属性 IsEnableInertia ,用来控制是否使用惯性,因为萝卜青菜各有所爱,不要想着强制所有人使用惯性,所以滚轮响应方法变为:

 protected override void OnMouseWheel(MouseWheelEventArgs e)
{
if (!IsEnableInertia)
{
base.OnMouseWheel(e);
return;
}
e.Handled = true;
}

  控制ScrollViewer的垂直滚动可以使用 ScrollViewer.ScrollToVerticalOffset ,横向也一样。为什么不能用 VerticalOffset ?因为 VerticalOffset 在注册的时候就说明了是只读的:

 private static readonly DependencyPropertyKey VerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly(nameof (VerticalOffset), typeof (double), typeof (ScrollViewer), (PropertyMetadata) new FrameworkPropertyMetadata((object) 0.0));

 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的更多相关文章

  1. WPF 控件库——带有惯性的ScrollViewer*(转)

    转:https://blog.csdn.net/ahilll/article/details/82418892 一.先看看效果 二.原理 虽然效果很简单,但是网上的一些资料涉及的代码量非常可观,而且效 ...

  2. WPF 控件库——仿制Windows10的进度条

    WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...

  3. WPF 控件库——仿制Chrome的ColorPicker

    WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...

  4. WPF 控件库——轮播控件

    WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...

  5. WPF 控件库——可拖动选项卡的TabControl

    WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...

  6. 《Dotnet9》系列-开源C# WPF控件库3《HandyControl》强力推荐

    大家好,我是Dotnet9小编,一个从事dotnet开发8年+的程序员.我最近开始写dotnet分享文章,希望能让更多人看到dotnet的发展,了解更多dotnet技术,帮助dotnet程序员应用do ...

  7. 我的WPF控件库——KAN.WPF.XCtrl(141105)

    自己开发的WPF控件库,只是初版,有扩展的Button,TextBox,Window.详细参见前几篇博文. WPF自定义控件(一)——Button:http://www.cnblogs.com/Qin ...

  8. 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程

    反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)   背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...

  9. 国内开源C# WPF控件库Panuon.UI.Silver推荐

    国内优秀的WPF开源控件库,Panuon.UI的优化版本.一个漂亮的.使用样式与附加属性的WPF UI控件库,值得向大家推荐使用与学习. 今天站长(Dotnet9,站长网址:https://dotne ...

随机推荐

  1. Redis数据清除问题

    Redis中数据清除可以分为两种方式 手动清除:指定要清除的key,通过delete命令即可清除 自动清除:使用Redis提供的数据过期策略 Redis数据过期策略      redis提供了非常灵活 ...

  2. Linux:课程安排、Linux简介、虚拟机安装、课前准备(常用设置和操作)

    一.课程安排 1)Linux 的作用 商业服务器基本上都是 Linux: 开源软件都先支持 Linux: 大数据分析.机器学习首先选 Linux: 整个互联网地基靠Linux撑起来: Linux 系统 ...

  3. HTMLTestRunner生成报告 中文展示乱码的问题

  4. php中的move_uploaded_file

    1.定义和用法 move_uploaded_file() 函数将上传的文件移动到新位置. 若成功,则返回 true,否则返回 false. 2.语法 move_uploaded_file(file,n ...

  5. Oracle 索引扫描的几种情况

    index range scan(索引范围扫描): 1.对于unique index来说,如果where 条件后面出现了<,> ,between ...and...的时候,那么就可能执行i ...

  6. Rest之路 - 介绍篇

    What is REST ? REST 是 REpresentational State Transfer 的缩写.是一种基于HTTP协议来进行进行数据交换的web标准框架.她的思想是:视组件为资源. ...

  7. Python 列表学习笔录

    列表是Python中最基本的数据结构,列表是最常用的Python数据类型,列表的数据项不需要具有相同的类型.列表中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类 ...

  8. C#应用调试C++ dll的方法

    最近碰到个C#应用闪退的问题,由于通讯部分调用了C++工程写的dll,下面介绍一种调试的方法. 右键 启动项目,分别配置常规和和调试即可,如下图. 常规中,输出目录设置为安装目录中dll对应的目录: ...

  9. 15 更多jQuery知识

    jqueryUI 官网: https://jqueryui.com/ jqueryUI 中文网: http://www.jqueryui.org.cn/ jquery插件内容包含 官网demo: ht ...

  10. CSV Format

    [CSV Format] The comma separated values format (CSV) has been used for exchanging and converting dat ...