1. 什么是滚动轮劫持

这篇文章介绍一个很简单的继承自ScrollViewer的控件:

  1. public class ExtendedScrollViewer : ScrollViewer
  2. {
  3. protected override void OnMouseWheel(MouseWheelEventArgs e)
  4. {
  5. if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0)
  6. return;
  7. if (VerticalOffset == 0 && e.Delta >= 0)
  8. return;
  9. base.OnMouseWheel(e);
  10. }
  11. }

所有代码就这么多,这个ExtendedScrollViewer 只是用来解决滚动轮劫持(scroll-wheel-hijack)的问题。所谓的滚动轮劫持,简单来说即是在一个可以滚动的页面使用鼠标滚轮滚动页面的过程中鼠标进入某个可以滚动的子元素导致只在这个子元素中滚动而整个页面想滚滚不动了。

具体看看这个例子:

这个情况相信很多人都遇到过,滚轮被“劫持”后索性去拖动滚动条。有次我遇到个内嵌了很多ScrollViewer的长页面,使用起来真的很恼人,所以我使用ExtendedScrollViewer 解决了这个问题。当然还有另外很多种情况的滚动轮劫持,也有很多解决方案,这篇文章只介绍我遇到的情况和我的解决方案。

2. 实现

在WPF中要禁止ScrollViewer捕获鼠标滚动时间,可以重写OnMouseWheel成一个空的方法:

  1. protected override void OnMouseWheel(MouseWheelEventArgs e)
  2. {
  3. }

OnMouseWheel方法用于响应鼠标滚轮的事件,将它重载成空方法即不再处理鼠标滚利事件。注意在这种情况下不可以使用e.Handled = true,因为我们的目标是让外层的ScrollViewer可以接收到鼠标滚轮事件,所以不能更改MouseWheelEventArgs 的Handled。

当然我们不满足于无脑禁用鼠标滚轮,我们应该更智能些,先让ScrollViewer滚到底,再交由外层的ScrollViewer滚下去。这里面用到几个属性:

MouseWheelEventArgs中的Delta表示鼠标滚轮的变更量,当这个值为正数时表示滚轮向上。

ExtentHeight,获取ScrollViewer内容的实际高度。

ViewportHeight,获取当前可视区域的高度。

VerticalOffset,包含滚动内容对应于页首的垂直偏移量的值,有效值介于 0 与 ExtentHeight 减去 ViewportHeight 所得的数值之间。

熟悉了上面几个属性的作用后我们可以更好地控制鼠标滚轮的行为,当鼠标向上滚动时,判断现在是否已经滚到顶了,如果是就不处理鼠标滚轮事件:

  1. if (VerticalOffset == 0 && e.Delta >= 0)
  2. return;

而当鼠标向下滚动时,需要根据ViewportHeightVerticalOffsetExtentHeight判断当前是否已经滚动到底,如果是就不处理鼠标滚轮事件:

  1. if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0)
  2. return;

3. 其他ScrollViewer方案

ScrollViewer还有很多中玩法,但我工作中不常用到所以就没做。如果觉得不满足还可以参考HandyControl的ScrollViewer,它直接提供了一个CanMouseWheel属性用于控制是否响应鼠标滚轮,另外还支持了滚动等功能。

4. 参考

ScrollViewer.OnMouseWheel(MouseWheelEventArgs) Method (System.Windows.Controls) Microsoft Docs

MouseWheelEventArgs.Delta Property (System.Windows.Input) Microsoft Docs

ScrollViewer.ExtentHeight Property (System.Windows.Controls) Microsoft Docs

ScrollViewer.ViewportHeight Property (System.Windows.Controls) Microsoft Docs

ScrollViewer.VerticalOffset Property (System.Windows.Controls) Microsoft Docs

5. 源码

ExtendedScrollViewer.cs at master

[WPF自定义控件库] 关于ScrollViewr和滚动轮劫持(scroll-wheel-hijack)的更多相关文章

  1. [WPF自定义控件库] 关于ScrollViewer和滚动轮劫持(scroll-wheel-hijack)

    原文:[WPF自定义控件库] 关于ScrollViewer和滚动轮劫持(scroll-wheel-hijack) 1. 什么是滚动轮劫持# 这篇文章介绍一个很简单的继承自ScrollViewer的控件 ...

  2. WPF 如何创建自己的WPF自定义控件库

    在我们平时的项目中,我们经常需要一套自己的自定义控件库,这个特别是在Prism这种框架下面进行开发的时候,每个人都使用一套统一的控件,这样才不会每个人由于界面不统一而造成的整个软件系统千差万别,所以我 ...

  3. [WPF自定义控件库]使用WindowChrome自定义RibbonWindow

    原文:[WPF自定义控件库]使用WindowChrome自定义RibbonWindow 1. 为什么要自定义RibbonWindow 自定义Window有可能是设计或功能上的要求,可以是非必要的,而自 ...

  4. [WPF自定义控件库] 让Form在加载后自动获得焦点

    原文:[WPF自定义控件库] 让Form在加载后自动获得焦点 1. 需求 加载后让第一个输入框或者焦点是个很基本的功能,典型的如"登录"对话框.一般来说"登录" ...

  5. [WPF自定义控件库]以Button为例谈谈如何模仿Aero2主题

    1. 为什么选择Aero2 除了以外观为卖点的控件库,WPF的控件库都默认使用"素颜"的外观,然后再提供一些主题包.这样做的最大好处是可以和原生控件或其它控件库兼容,而且对于大部分 ...

  6. [WPF自定义控件库]简单的表单布局控件

    1. WPF布局一个表单 <Grid Width="400" HorizontalAlignment="Center" VerticalAlignment ...

  7. [WPF自定义控件库]使用WindowChrome的问题

    1. 前言 上一篇文章介绍了使用WindowChrome自定义Window,实际使用下来总有各种各样的问题,这些问题大部分都不影响使用,可能正是因为不影响使用所以一直没得到修复(也有可能别人根本不觉得 ...

  8. [WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

    1. 前言 WPF有一个灵活的UI框架,用户可以轻松地使用代码控制控件的外观.例设我需要一个控件在鼠标进入的时候背景变成蓝色,我可以用下面这段代码实现: protected override void ...

  9. [WPF自定义控件库]为Form和自定义Window添加FunctionBar

    1. 前言 我常常看到同一个应用程序中的表单的按钮----也就是"确定"."取消"那两个按钮----实现得千奇百怪,其实只要使用统一的Style起码就可以统一按 ...

随机推荐

  1. python课堂整理1

      1.变量 变量只能由字母.数字.下划线组成 特例:1.变量不能用数字开头    2.不能是python的关键字 3.最好不要和python内置的东西重复 让变量名有意义 些 python3的关键字 ...

  2. vim批量注释和反注释快捷键

    vim批量注释和反注释快捷键 我是个vim新手,非常喜欢这个工具,因为纯手工操作吧.可是有些快捷键还是不知道,写Python的时候经常要调试,会批量注释掉一些代码,vim不像pycharm那样 Ctr ...

  3. zabbix3.4汉化

    1.管理员用户登入zabbix页面,更改语言为Chinese(zh_CN),点击Update 2.解决zabbix页面中文乱码 2.1在windows的C:\Windows\Fonts找到字体文件si ...

  4. halcon视频教程如何学习?怎么样才能踏入机器视觉这个行业?

    本人是工作八年的视觉工程师,主要从事Halcon和Visionpro视觉开发,谈谈个人对视觉学习看法: 1.HALCON是德国MVtec公司开发的一套完善的标准的机器视觉算法包,它节约了产品成本,缩短 ...

  5. 【RabbitMQ】一文带你搞定RabbitMQ延迟队列

    本文口味:鱼香肉丝   预计阅读:10分钟 一.说明 在上一篇中,介绍了RabbitMQ中的死信队列是什么,何时使用以及如何使用RabbitMQ的死信队列.相信通过上一篇的学习,对于死信队列已经有了更 ...

  6. NLP(十二)依存句法分析的可视化及图分析

      依存句法分析的效果虽然没有像分词.NER的效果来的好,但也有其使用价值,在日常的工作中,我们免不了要和其打交道.笔者这几天一直在想如何分析依存句法分析的结果,一个重要的方面便是其可视化和它的图分析 ...

  7. SSM框架实现原理图(转)

  8. Angular JS 中的服务注册方法

    在Angular JS中创建服务的几种方法 factory() service() constant() value() provider() factory(name,fn(){}) 该服务为单例的 ...

  9. 脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本

    1 查看壳程序信息 使用ExeInfoPe 分析: 发现这个壳的类型没有被识别出来,Vc 6.0倒是识别出来了,Vc 6.0的特征是 入口函数先调用GetVersion() 2 用OD找OEP 拖进O ...

  10. IOS应用无法下载、此时无法安装应用程序

    无法安装应用 app开发者,进行程序测试,重试还不行,就重新打包, 个人,更改wifi的dns 在“设置” –> “WiFi” –> 进入当前的WiFi 进入之后点击旁边的叹号,然后进入之 ...