WPF 如何流畅地滚动ScrollViewer 简单实现下
看了看原生UWP的ScrollViewer,滑动很流畅(例如 开始菜单),但是WPF自带的ScrollViewer滚动十分生硬..
突发奇想,今天来实现一个流畅滚动的ScrollViewer.
一、目标
查阅网上的实现方法,要么直接重写控件,要么一堆Storyboard..很是无奈,还有些许bug.
主要目标如下:
1.能够流畅地滚动ScrollViewer 要求:有惯性的物理滚动 而不是 生硬的 内容滚动.
2.在使用动画的过程中,避免多个begin导致卡顿
3.好用(? )
二、准备工作
实现以上目标需要到的几件东西:
1.对于Listbox之类的控件需要先讲滚动方式变为 像素滚动 (若没有的话就可以不设置)
<ListBox
VirtualizingPanel.ScrollUnit = "Pixel"
VirtualizingPanel.VirtualizationMode="Recycling"/>
2.选一种动画使用的 缓动函数 这里推荐使用 CubicEase的EaseOut函数 (优点:接近于惯性滚动 对于CPU比较友好)
3.要对ScrollViewer启用滚动动画 需要自己写依赖属性:
public static class ScrollViewerBehavior
{
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(ScrollViewerBehavior), new UIPropertyMetadata(0.0, OnVerticalOffsetChanged));
public static void SetVerticalOffset(FrameworkElement target, double value) => target.SetValue(VerticalOffsetProperty, value);
public static double GetVerticalOffset(FrameworkElement target) => (double)target.GetValue(VerticalOffsetProperty);
private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) => (target as ScrollViewer)?.ScrollToVerticalOffset((double)e.NewValue);
}
三、编码
//继承ScrollViewer 通过截获MouseWheel事件控制滚动
public class MyScrollViewer : ScrollViewer
{
//记录上一次的滚动位置
private double LastLocation = ;
//重写鼠标滚动事件
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
double WheelChange = e.Delta;
//可以更改一次滚动的距离倍数 (WheelChange可能为正负数!)
double newOffset = LastLocation - (WheelChange * );
//Animation并不会改变真正的VerticalOffset(只是它的依赖属性) 所以将VOffset设置到上一次的滚动位置 (相当于衔接上一个动画)
ScrollToVerticalOffset(LastLocation);
//碰到底部和顶部时的处理
if (newOffset < )
newOffset = ;
if (newOffset > ScrollableHeight)
newOffset = ScrollableHeight; AnimateScroll(newOffset);
LastLocation = newOffset;
//告诉ScrollViewer我们已经完成了滚动
e.Handled = true;
}
private void AnimateScroll(double ToValue)
{
//为了避免重复,先结束掉上一个动画
BeginAnimation(ScrollViewerBehavior.VerticalOffsetProperty, null);
DoubleAnimation Animation = new DoubleAnimation();
Animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
Animation.From = VerticalOffset;
Animation.To = ToValue;
//动画速度
Animation.Duration = TimeSpan.FromMilliseconds();
//考虑到性能,可以降低动画帧数
//Timeline.SetDesiredFrameRate(Animation, 40);
BeginAnimation(ScrollViewerBehavior.VerticalOffsetProperty, Animation);
}
}
使用方法:直接创建 <MyScrollViewer/>
如果是在ListBox中,可以通过修改模板的方式,把<ScrollViewer/>换成MyScrollViewer即可.
四、关于性能
按照上述方法实现的功能,CPU占用率基本稳定在10% ,但是要疯狂地上下滑动滚轮的话,25%+ 但是动画还是很流畅的
以下有几点可以改进的措施:
滚动的CPU占用不只是对偏移量的一个计算,与你滚动的UI画面内容具有很大的关系
例如大量的图片Image 3D对象 复杂的自定义控件 等等 都需要在滚动时计算并绘制。
你可以尝试以下方法:
1).启用ListBox的虚拟化技术
2).对于图像的绘制,可以通过降低其渲染度(可能会损坏图片质量 特别是低清小图 ),对你的图片对象使用:
RenderOptions.SetBitmapScalingMode(控件对象,BitmapScalingMode.LowQuality);
3).就像上文中所说,可以通过降低渲染帧数来控制CPU占用率(可能会又是流畅度)
4).如果能找到更好的缓动函数
5).参阅: 优化控件性能 -WPF |Microsoft Docs
WPF 如何流畅地滚动ScrollViewer 简单实现下的更多相关文章
- VS编程,WPF中两个滚动条 ScrollViewer 同步滚动的一种方法
原文:VS编程,WPF中两个滚动条 ScrollViewer 同步滚动的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/ar ...
- WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Scr ...
- 【转】WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: ScrollViewer的样式拆解及基本样式定义: ListBox集合 ...
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- WPF 超长文本的来回滚动
原文:WPF 超长文本的来回滚动 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/article/details/8362 ...
- 不用splitter控件 简单实现对mfc对话框的分割的方法
不用splitter控件 简单实现对mfc对话框的分割的方法 直接贴上源代码主要部分吧 这个是基于对话框的工程 进行对话框的分割实现 只是相应了三个消息函数,看一下就会明白的 我空间资源里边有现成的 ...
- springmvc springJDBC 简单实训银行账户管理系统
springmvc springJDBC 简单实训银行账户管理系统 1.简单介绍一下,在校时每周结束都会有一次学习总结,简称“实训”,这次实训内容是spring,因为是最近热门框架,我就先从基础方面开 ...
- WPF获取外部EXE图标最简单的方法
原文:WPF获取外部EXE图标最简单的方法 首先在工程添加对System.Drawing的引用 创建以下方法: public static ImageSource GetIcon(string fil ...
- WPF MVVM+EF增删改查 简单示例(二) 1对1 映射
WPF MVVM+EF增删改查 简单示例(一)实现了对学生信息的管理. 现在需求发生变更,在录入学生资料的时候同时需要录入学生的图片信息,并且一名学生只能有一张图片资料.并可对学生的图片资料进行更新. ...
随机推荐
- ESlint中console.log报错问题
ESlint中console.log报错问题 由于ESlint规范化,导致console.log的使用也会报错,下面是设置允许console.log控制台输出 描述:打开 package.json 文 ...
- linux -- 一般使用经验(四)
一.使用grep进行条件筛选(主要为日志) 1.grep -E '条件1|条件2|条件3' 文件名 (cat -n 文件.log |grep -E '2020-01-16 15:24:48|条件 ...
- PAT 乙级-1025 链表反转
给定一个常数K以及一个单链表L,请编写程序将L中每K个结点反转.例如:给定L为1→2→3→4→5→6,K为3,则输出应该为3→2→1→6→5→4:如果K为4,则输出应该为4→3→2→1→5→6,即最后 ...
- Kubernetes学习笔记(八):Deployment--声明式的升级应用
概述 本文核心问题是:如何升级应用. 对于Pod的更新有两种策略: 一是删除全部旧Pod之后再创建新Pod.好处是,同一时间只会有一个版本的应用存在:缺点是,应用有一段时间不可用. 二是先创建新Pod ...
- HTTP——无状态协议理解
无状态服务器是指一种把每个请求作为与之前任何请求都无关的独立的事务的服务器. HTTP是一个属于应用层的面向对象的协议 ------未完待续
- MySQL8离线安装
现在离线安装包: 登录官网准备下载 https://dev.mysql.com/downloads/mysql/ 2,开始下载 解压安装包: 开始解压: 解压完成: 新建init文件: 在解压目录下创 ...
- Java-接口(另类抽象)
1.1 特点 用interface定义 接口中所有成员变量都默认是由public static final修饰的 接口中所有方法都默认是由public abstract修饰的 接口没有构造器 接口采用 ...
- 本地安装JDK1.7和1.8,可相互快速切换
1.JDK官网下载jdk1.7和jdk1.8 https://www.oracle.com/java/technologies/javase-jdk8-downloads.html 2.将jdk1.7 ...
- Rocket - debug - Periphery
https://mp.weixin.qq.com/s/uGxn-Xec0LkwdaSsCtQBvw 简单介绍Periphery的实现. 1. ExportDebugDMI/ExportDebugJTA ...
- Rocket - tilelink - Nodes
https://mp.weixin.qq.com/s/KJ8pVH76rdxPOZ1vE3QlKA 简单介绍tilelink对Diplomacy Nodes的实现. 1. TLImp ...