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. unittest之跳过用例(skip) (含如何调用类里面函数相互调取变量的方法)

    当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例. 或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例也就没 ...

  2. mac常用命令(随时更新)

    mac 强制退出快捷键 1.使用键盘快捷键强制退出处于活跃状态的Mac程序 快捷键:Command+Option+Shift+Esc 这样按住一两秒钟,就可以强制退出当前程序了,算是最方便的一种方法. ...

  3. 关于datagridview自动增加行高度和显示全部内容的设置

    this.dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders;  //自动调动dat ...

  4. Delphi 原生ADO(二)

    我发现很多朋友在开发数据库时都使用 Delphi 自带的 ADO 组 件 或 Diamond ADO,其实在 Delphi 中使用原生 ADO 接口也是十分方便和有效的.我使用原生 ADO 开发项目已 ...

  5. In function 'int av_clipl_int32_c(int64_t)': error: 'UINT64_C' was not declared in this scope

    cygwin下使用ndk编译jni时遇到的错误: /ffmpeg/include/libavutil/common.h: In function 'int av_clipl_int32_c(int64 ...

  6. SecureCRT中某些命令提示符下按Backspace显示^H的解决方法

    SecureCRT中某些命令提示符下按Backspace显示^H的解决方法 安装了Apache Derby数据库服务器之后,使用ij客户端去连接derby服务端,可是在ij中输入命令的时候,每当输入错 ...

  7. Apache Derby数据库系统使用方法

    Apache Derby数据库系统使用方法 最近由于项目要求,试用了一下Apache Derby数据库,这里对了解到的内容做一个记录. Apache Derby是一个开源的关系型数据库管理系统,用Ja ...

  8. Hadoop之MapReduce(一)简介及简单案例

    简介 Hadoop MapReduce是一个分布式运算编程框架,基于该框架能够容易地编写应用程序,进而处理海量数据的计算. MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算. ...

  9. ms project设置行高

    1.取消某列的自动换行右击“任务名称”——自动换行 2.全选所有任务点击左上角单元格 3.设置所有行的行高点击任意行最左边单元格的下边框,向上拖放 4.ok

  10. js确定来源页然后跳转

    <script type="text/javascript"> function Navigate() { if(document.referrer == 'http: ...