看了看原生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 简单实现下的更多相关文章

  1. VS编程,WPF中两个滚动条 ScrollViewer 同步滚动的一种方法

    原文:VS编程,WPF中两个滚动条 ScrollViewer 同步滚动的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/ar ...

  2. WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Scr ...

  3. 【转】WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: ScrollViewer的样式拆解及基本样式定义: ListBox集合 ...

  4. Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用

    一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...

  5. WPF 超长文本的来回滚动

    原文:WPF 超长文本的来回滚动 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/article/details/8362 ...

  6. 不用splitter控件 简单实现对mfc对话框的分割的方法

    不用splitter控件  简单实现对mfc对话框的分割的方法 直接贴上源代码主要部分吧 这个是基于对话框的工程 进行对话框的分割实现 只是相应了三个消息函数,看一下就会明白的 我空间资源里边有现成的 ...

  7. springmvc springJDBC 简单实训银行账户管理系统

    springmvc springJDBC 简单实训银行账户管理系统 1.简单介绍一下,在校时每周结束都会有一次学习总结,简称“实训”,这次实训内容是spring,因为是最近热门框架,我就先从基础方面开 ...

  8. WPF获取外部EXE图标最简单的方法

    原文:WPF获取外部EXE图标最简单的方法 首先在工程添加对System.Drawing的引用 创建以下方法: public static ImageSource GetIcon(string fil ...

  9. WPF MVVM+EF增删改查 简单示例(二) 1对1 映射

    WPF MVVM+EF增删改查 简单示例(一)实现了对学生信息的管理. 现在需求发生变更,在录入学生资料的时候同时需要录入学生的图片信息,并且一名学生只能有一张图片资料.并可对学生的图片资料进行更新. ...

随机推荐

  1. jsp获取当前日期

    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>  <jsp: ...

  2. JVM_双亲委派机制

    双亲委派机制及作用 什么是双亲委派机制 当`.class`文件需要被加载时,它首先把这个任务委托给他的上级类加载器,层层往上委托,如果上级的类加载器没有加载过,自己才会去加载这个类. 源码分析 pro ...

  3. Shift - And字符串快速处理 hdu5972+cf

    基础知识介绍 KMP就是不断往前找1的位置,而ShiftAnd经过三步处理已经完成这个迭代的过程了 如果匹配两个字符集有限的字符串的话,那么Shift-And比kmp要快,找区间内某字符串出现的数目也 ...

  4. Lowest Common Multiple Plus(hdu2028)

    思考: 乘法爆咋数据.把int换成unsigned就过了,同时%d换成%u.求最大公约数和最小公倍数. #include<stdio.h> int gcd(unsigned x, unsi ...

  5. Centos 安装 Anaconda

    # 首先从 Anaconda 官网下载 anaconda Linux 64Bit 版本命令行安装包 $ wget https://repo.continuum.io/archive/Anaconda3 ...

  6. 符合PSR-0规范的自动加载

    PSR-0规范 命名空间必须与绝对路径一致 类名首字母必须大写 除去入口文件外,其他“.php”必须只有一个类 类文件必须自动载入,不采用include等 单一入口 1)目录结构 2)代码 入口文件i ...

  7. uiautomator2通过wifi操作手机

    参考来源:https://www.cnblogs.com/c-x-a/p/11176066.html,有部分不适合当前版本的做了修改 1.手机通过USB连接电脑,先开启远程adb模式,操作如下(可以指 ...

  8. indetityserver4-implicit-grant-types-请求流程叙述-下篇

    上一篇将请求流程描述一遍,这篇将描述一下相关的源码. 1 访问客户端受保护的资源 GET /Home/Secure HTTP/1.1HTTP/1.1 302 Found Date: Tue, 23 O ...

  9. Redis学习笔记(4)

    一.Redis主从复制 1. 概念 为了避免服务的单点故障,会把数据复制到多个副本放在不同的服务器上,且这些拥有数据副本的服务器可以用于处理客户端的读请求,扩展整体的性能.我们把这种机制称之为主从复制 ...

  10. [安卓安全] 01.安卓本地数据存储:Shared Preferences安全风险浅析

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...