2019-10-7-dotnet-Framework-源代码-·-ScrollViewer
| title | author | date | CreateTime | categories |
|---|---|---|---|---|
|
dotnet Framework 源代码 · ScrollViewer
|
lindexi
|
2019-10-07 13:15:25 +0800
|
2018-3-13 15:9:13 +0800
|
C# .net Framework 源代码分析 WPF ScrollViewer dotnet
|
本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单。
看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 IScrollInfo 或者给他滚动添加动画
使用
下面告诉大家如何简单使用 ScrollViewer ,一般在需要滚动的控件外面放一个 ScrollViewer 就可以实现滚动。
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,20">Scrolling is enabled when it is necessary.
Resize the window, making it larger and smaller.</TextBlock>
<Rectangle Fill="Red" Width="500" Height="500"></Rectangle>
</StackPanel>
</ScrollViewer>
但不是所有的控件外面放一个 ScrollViewer 都能实现滚动,因为滚动实际上需要控件自己做。
原理
下面来告诉大家滚动是如何做的。
一个最简单的方法是设置元素的 transForm.Y 通过这个方式进行滚动是最简单的方法,但是缺点是其他控件不能做其他的移动。
在 ScrollViewer 存在两个滚动方式,物理滚动 和 逻辑滚动,如果使用 物理滚动 那么滚动就是ScrollViewer做的,如何使用逻辑滚动,那么滚动就是控件自己做的。
那么我从 ScrollViewer 接收输入开始讲起
输入
如果大家使用 ScrollViewer 进行滚动,那么也许会遇到一个神奇的需求,如何在触摸下滚动。是的,如果使用一个简单的 ScrollViewer 是无法使用触摸滚动
请看代码,写一个简单的 ScrollViewer 里面有一些矩形,可以看到这时可以进行鼠标滚动,但是触摸是无法滚动。
<Grid>
<ScrollViewer>
<StackPanel x:Name="HcrkKmqnnfzo"></StackPanel>
</ScrollViewer>
</Grid>
在后台遍历颜色然后添加
public MainWindow()
{
InitializeComponent(); foreach (var temp in typeof(Brushes)
.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
.Select(temp => temp.GetValue(null, null)))
{
var rectangle = new Rectangle
{
Height = 20,
Fill = (Brush)temp
}; HcrkKmqnnfzo.Children.Add(rectangle);
}
}
代码:WPF ScrollView 代码解释 1.1-CSDN下载
如果没有csdn积分,尝试使用 我的网盘,但是我的网盘如果过期请告诉我
如果需要在触摸使用滚动,那么需要设置PanningMode,可以设置支持垂直拖动。
如果这时设置了PanningMode,就会发现拖动时让窗口抖动,这时需要在窗口重写 OnManipulationBoundaryFeedback ,请看下面代码。函数里面什么都不要写,详细请看 https://stackoverflow.com/a/6918131/6116637
protected override void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e)
{
}
修改后的代码:WPF ScrollView 代码解释 1.2-CSDN下载
那么在鼠标滚动是如何收到滚动?
从微软源代码可以看到 ScrollViewer 继承 ContentControl,所以可以重写 OnMouseWheel ,请看他的代码
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
if (e.Handled) { return; } if (!HandlesMouseWheelScrolling)
{
return;
} if (ScrollInfo != null)
{
if (e.Delta < 0) { ScrollInfo.MouseWheelDown(); }
else { ScrollInfo.MouseWheelUp(); }
} e.Handled = true;
}
实际上 ScrollViewer 是不做滚动的,实际的滚动是 ScrollInfo 进行滚动。
ScrollInfo
那么 ScrollInfo 是什么,实际上他是一个接口,在 ScrollViewer 里面放的控件实际上不是直接放在 ScrollViewer 里,控件是放在 ScrollContentPresenter,而 ScrollContentPresenter 是写在 ScrollViewer 的 Style 里,在 ScrollViewer 可以看到这个代码
[TemplatePart(Name = "PART_ScrollContentPresenter", Type = typeof(ScrollContentPresenter))]
但是从垃圾微软的代码可以看到,没有属性直接使用这个,而是在使用的地方这样写GetTemplateChild(ScrollContentPresenterTemplateName) as ScrollContentPresenter;
这样写的性能是比较差的。
那么他是如何给 ScrollInfo 赋值?实际上在这个类的 HookupScrollingComponents 就是给 ScrollInfo 赋值,在 HookupScrollingComponents 调用的地方就是 OnApplyTemplate 所以大家可以看到,在初始化的时候就已经知道了控件。
从垃圾微软的源代码可以看到 HookupScrollingComponents 的逻辑,首先是判断属性CanContentScroll 判断元素里的控件是否可以滚动,如果元素里的控件可以滚动,那么再判断元素里的控件是不是继承IScrollInfo如果是的话,嗯,没了,就把 ScrollInfo 赋值。如果里面的控件不是继承IScrollInfo,那么判断一下他是不是处于列表,如果是的话就拿列表ItemsPresenter作为ScrollInfo。如果还是拿不到,只好用自己作为ScrollInfo
从这里可以看到 CanContentScroll 如果没有设置,就直接使用这个类,也就是物理滚动就是这个类做的。如果一个元素不在列表内,不继承 IScrollInfo 那么即使设置使用逻辑滚动,实际上也是物理滚动。物理滚动就是元素不知道滚动,所有的移动都是元素无法控制。和物理滚动不同,逻辑的就是元素控制所有滚动。
物理滚动
下面来告诉大家,物理滚动是如何做,实际上的滚动就是在布局中使用下面的代码,让元素布局在滚动的地方,所以看起来就是元素滚动
Rect childRect = new Rect(child.DesiredSize);
if (IsScrollClient)
{
childRect.X = -HorizontalOffset;
childRect.Y = -VerticalOffset;
}
//this is needed to stretch the child to arrange space,
childRect.Width = Math.Max(childRect.Width, arrangeSize.Width);
childRect.Height = Math.Max(childRect.Height, arrangeSize.Height);
child.Arrange(childRect);
可以看到布局设置反过来的 HorizontalOffset 作为元素的 x 移动,通过这样就可以让元素移动
但是元素如果移动在 ScrollViewer 外面,如何裁剪?实际上就是使用重写了 GetLayoutClip 进行裁剪
return new RectangleGeometry(new Rect(RenderSize));
从代码可以知道,实际上的 ScrollViewer 是不会滚动元素的,滚动元素的是 ScrollViewer 里面的元素,滚动的方式一般都使用在布局的时候设置元素的 X、Y 来让元素滚动。我看了 StackPanel 和其他几个类,都是使用这个方式,因为对比 Translate 的方式,这个方法不会用到 Translate 也就不会在用户修改 Translate 的时候无法移动。另外这个方法是在布局做的,直接计算,如果修改 Translate 还需要在布局重新计算,所以这个方法的性能会比较高。
触摸输入
那么 ScrollViewer 是如何在触摸的时候获得输入?实际上在触摸的时候用的是 Manipulation ,在判断 PanningMode 给值
if (panningMode == PanningMode.HorizontalOnly)
{
e.Mode = ManipulationModes.TranslateX;
}
else if (panningMode == PanningMode.VerticalOnly)
{
e.Mode = ManipulationModes.TranslateY;
}
else
{
e.Mode = ManipulationModes.Translate;
}
所以在 ManipulationDelta 可以拿到移动的值,因为直接拿到的值就是用户希望的路径所以直接设置不需要计算
但是需要倍数 PanningRatio ,如果需要惯性,那么只需要设置惯性就可以。
大概整个源代码只有这些,很多的代码都是在判断边界,还有处理一些用户输入。
在触摸的时候,核心的代码是 ManipulateScroll ,传入了当前的移动和累计的移动、是否水平移动。通过判断当前的移动是否有移动然后乘以倍数,然后通过设置 HorizontalOffset 这几个属性的值,重新布局就可以。
所以所有的代码实际上就是获得输入,然后传入给对应的 ScrollInfo ,通过 ScrollInfo 实现的方法做具体的业务。
不过 ScrollViewer 不是直接传入 ScrollInfo 需要移动的,而且发送命令
public void ScrollToHorizontalOffset(double offset)
{
double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset");
// Queue up the scroll command, which tells the content to scroll.
// Will lead to an update of all offsets (both live and deferred).
EnqueueCommand(Commands.SetHorizontalOffset, validatedOffset, null);
}
然后在具体的函数 ExecuteNextCommand 拿出一个个的命令,进行移动
private bool ExecuteNextCommand()
{
IScrollInfo isi = ScrollInfo; Command cmd = _queue.Fetch();
switch(cmd.Code)
{
case Commands.LineUp: isi.LineUp(); break;
case Commands.LineDown: isi.LineDown(); break;
case Commands.LineLeft: isi.LineLeft(); break;
case Commands.LineRight: isi.LineRight(); break;
//去掉差不多的代码
case Commands.Invalid: return false;
}
return true;
}
在输入的时候可能输入太快,而布局不是立刻进行布局,从代码可以看到,移动的业务就是在布局修改值,但是布局修改不是优先级很高的,但是输入的优先级是很高的,可能在布局的过程就不停输入。所以就需要把输入的命令放入,使用一个函数一个个拿出来,对不同的命令处理,最后再布局。
参见:
IScrollInfo in Avalon part I – BenCon's WebLog
IScrollInfo in Avalon part II – BenCon's WebLog
IScrollInfo in Avalon part III – BenCon's WebLog
IScrollInfo tutorial part IV – BenCon's WebLog
其他源代码分析
2019-10-7-dotnet-Framework-源代码-·-ScrollViewer的更多相关文章
- .net Framework 源代码 · ScrollViewer
本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单 看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 ISc ...
- .net Framework 源代码 · ScrollViewer
本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单. 看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 IS ...
- dotnet Framework 源代码 类库的意思
本文告诉大家 dotnet framework 的源代码类库的意思 下面列出来 dotnet framework 源代码的各个类库的作用. System System 命名空间包含基本类和基类,这些类 ...
- dotnet Framework 源代码 · Ink
本文是分析 .NET Framework 源代码的系列,主要告诉大家微软做笔迹用的思路,怎么做的笔迹才是高性能的,用户体验比较好的.我会告诉大家源代码的思想,当然这个文章会比较无聊.如果你是想做笔迹的 ...
- 调试 .NET Framework 源代码、.DotNetCore源码
调试 .NET Framework 源代码..DotNetCore源码 如何调试 .NET Framework 源代码 在 Visual Studio 调试器中指定符号 (.pdb) 和源文件 .NE ...
- 背水一战 Windows 10 (46) - 控件(ScrollViewer 基础): ScrollViewer, ScrollBar, ScrollContentPresenter
[源码下载] 背水一战 Windows 10 (46) - 控件(ScrollViewer 基础): ScrollViewer, ScrollBar, ScrollContentPresenter 作 ...
- 背水一战 Windows 10 (47) - 控件(ScrollViewer 特性): Chaining, Rail, Inertia, Snap, Zoom
[源码下载] 背水一战 Windows 10 (47) - 控件(ScrollViewer 特性): Chaining, Rail, Inertia, Snap, Zoom 作者:webabcd 介绍 ...
- 如何:调试 .NET Framework 源代码
文章标题:如何:调试 .NET Framework 源代码 文章地址:https://technet.microsoft.com/zh-cn/cc667410.aspx
- 2019.10 搜索引擎最新排名,Elasticsearch遥遥领先
大数据的搜索平台已经成为了众多企业的标配,Elasticsearch.Splunk(商业上市公司).Solr(Apache开源项目)是其中最为优秀和流行的选择.在2019.10 最新搜索引擎排名中,E ...
- 【2019.10.17】十天Web前端程序员体验(软件工程实践第五次作业)
结对信息.具体分工 Github地址:https://github.com/MokouTyan/131700101-031702425 学号 昵称 主要负责内容 博客地址 131700101 莫多 代 ...
随机推荐
- 【JZOJ5064】【GDOI2017第二轮模拟day2】友好城市 Kosarajo算法+bitset+ST表+分块
题面 在Byteland 一共有n 座城市,编号依次为1 到n,这些城市之间通过m 条单向公路连接. 对于两座不同的城市a 和b,如果a 能通过这些单向道路直接或间接到达b,且b 也能如此到达a,那么 ...
- 【django后端分离】Django Rest Framework之一般配置(简单剖析)
1:常设状态码 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent). 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成 ...
- VS2013下设置git同步,“出现了错误。详细消息: An error was raised by libgit2. Category = Net (Error).”。
前言: 这个错误耽误了我数个小时,终于解决,不知道为何VS官方在与github同步上面做得如此麻烦.希望能帮到大家. 出现了错误.详细消息: An error was raised by libgit ...
- Directx11教程(65) 渲染到纹理
原文:Directx11教程(65) 渲染到纹理 通常情况下,我们的render target都是后缓冲,但也可以把render target设置为一个2d 纹理,然后再通过贴图的方式,把这个 ...
- Directx教程(26) 简单的光照模型(5)
原文:Directx教程(26) 简单的光照模型(5) 在前面的工程中,我们都是在vs中实现顶点光照计算,然后再把顶点颜色传到ps中.本章中我们尝试fragment光照(或者说叫ps光照),在 ...
- Jmeter VS LR参数取值方式和迭代方式
Jmeter的参数化 Jmeter中的参数就是变量. 变量的来源:测试计划.UDV.CSV.函数.正则表达式.数据库. 以Jmeter的CSV文件参数化为例:
- day39-Spring 05-Spring的AOP:不带有切点的切面
Spring底层的代理的实现: 不带切点的切面是对类里面的所有的方法都进行拦截. 做Spring AOP的开发需要两个包:一个是AOP的包,一个是AOP联盟的包(因为规范是由AOP联盟提出来的). 用 ...
- js获取url制定的某个参数
<script>function getURLParam(strParamName, url) { var strReturn = ""; var strH ...
- 百度网盘直链下载助手(MacOS&Chrome)
简介 众所周知,通过百度网盘(未开通会员)直接下载文件的速度极慢,通过安装浏览器插件可以极大的提高下载速度. 安装文件 Tampermonkey NeatDownloadManager Extensi ...
- 《DL/T 1476-2015 电力安全工器具预防性试验规程》中的样品名称及试验项目