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;}

在输入的时候可能输入太快,而布局不是立刻进行布局,从代码可以看到,移动的业务就是在布局修改值,但是布局修改不是优先级很高的,但是输入的优先级是很高的,可能在布局的过程就不停输入。所以就需要把输入的命令放入,使用一个函数一个个拿出来,对不同的命令处理,最后再布局。

参见:

在WPF中实现平滑滚动 - 天方 - 博客园

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

其他源代码分析

.net Framework 源代码 · ScrollViewer

.net源码分析 – List - 布鲁克石 - 博客园

一站式WPF--依赖属性(DependencyProperty)一 - 周永恒 - 博客园

2019-10-7-dotnet-Framework-源代码-·-ScrollViewer相关推荐

  1. .net Framework 源代码 · ScrollViewer

    本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单. 看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 IS ...

  2. DotNet Framework源代码调试问题

    几年前,记得vs2008刚发布的时候,带了一个新的特性,就是可以调试.Net Framework的源代码,当时非常兴奋,马上实验了一下,按照(ConfiguringVisual Studio to D ...

  3. 迁移 WinForm 应用从 dotnet framework 到 dotnetcore3.0

    迁移 WinForm 应用从 dotnet framework 到 dotnetcore3.0 Intro 微软从 dotnetcore3.0 开始已经开始支持 wpf 以及 winform 的开发, ...

  4. ROS2网络课程资料分享2019.10.26

    目前,网络上主要的ROS2课程主要有: Constructsim:https://www.theconstructsim.com/robotigniteacademy_learnros/ros-cou ...

  5. dotnet core 和 dotnet Framework 启动可执行文件的差别

    在 Windows 下,使用 .NET Framework 构建出来的应用,可以只有一个可执行文件,在可执行文件里面包含了 IL 代码.使用 .NET Core 构建出来的应用,将会包含一个 Exe ...

  6. 背水一战 Windows 10 (47) - 控件(ScrollViewer 特性): Chaining, Rail, Inertia, Snap, Zoom

    原文:背水一战 Windows 10 (47) - 控件(ScrollViewer 特性): Chaining, Rail, Inertia, Snap, Zoom [源码下载] 背水一战 Windo ...

  7. Windows Server 2012 R2/2016/2019无法安装.NET Framework 3.5.1或语言包的解决方法

    Windows Server 2012 R2/2016/2019无法安装.NET Framework 3.5.1或语言包的解决方法 参考文章: (1)Windows Server 2012 R2/20 ...

  8. 野鸡NOI.AC模拟赛【2019.10.26】

    前言 截止至2019.10.2614:222019.10.26\ \ \ \ 14:222019.10.26    14:22 成绩 正题 T1:NOI.AC−T1:NOI.AC-T1:NOI.AC− ...

  9. android源代码下载AE 文字样式,阅读Android framework源代码方式

    阅读Android framework源代码方式 点击标题下「蓝色微信名」可快速关注 阅读源代码的方式有很多,这里只讲其中的两种方式. 一.AndroidXRef(强烈推荐) 这种方式速度快,操作简单 ...

  10. 背水一战 Windows 10 (46) - 控件(ScrollViewer 基础): ScrollViewer, ScrollBar, ScrollContentPresenter...

    原文:背水一战 Windows 10 (46) - 控件(ScrollViewer 基础): ScrollViewer, ScrollBar, ScrollContentPresenter [源码下载 ...

最新文章

  1. GPU的发展历程、未来趋势及研制实践
  2. WP7 开发(十) 处理Html标记
  3. Floyd cycle算法
  4. CentOS 7安装Redis服务
  5. Spark Streaming 执行流程
  6. 【加速实践】番外篇:numbajit
  7. VTK:Rendering之HiddenLineRemoval
  8. matlab特征点数量,特征点检测效果评估(matlab代码)
  9. Pin code码已被重置
  10. 程序员找不到对象是因为还没遇到一个有远见的丈母娘
  11. 数据结构:分块-区间加法、区间乘法和单点查询
  12. DenseNet:密集卷积网络(2018)
  13. 用xshell7和xftp7连接虚拟机CentOS7.6的步骤
  14. C/C++ 飞机订票系统
  15. 服务器虚拟化cas,CAS虚拟化建设方案
  16. Qt之打印pdf文档
  17. linux wifi驱动分析,Android Wifi驱动--底层
  18. NanoPi-NEO——初体验
  19. java.time.duration_java.time.Duration
  20. 数据库的四种隔离级别

热门文章

  1. 自定义地图数据瓦片化请求的一种实现方案
  2. [leetcode刷题]汇总(二)
  3. arm的多级流水线技术和和存储管理单元mmu
  4. grub 加载不了配置文件不出菜单的情况处理
  5. ProxyPool 爬虫代理IP池(分享)
  6. 如何使用OneDrive共享Microsoft Word文档
  7. Android手机上的SSL连接错误怎么解决?
  8. 二重积分(Double Integral)
  9. H5的页面存储、移动页面适配、常用的相对单位
  10. MCMC、蒙特卡洛近似和Metropolis算法简介