在前端 UI 开发中,有时,我们会遇到这样的需求:在一个 ScrollViewer 中有很多内容,而我们需要实现在执行某个操作后能够定位到其中指定的控件处;这很像在 HTML 页面中点击一个链接后定位到当前网页上的某个 anchor。

要实现它,首先我们需要看 ScrollViewer 为我们提供的 API,其中并没有类似于 ScrollToControl 这样的方法;在它的几个以 ScrollTo 开头的方法中,最合适的就是 ScrollToVerticalOffset 这个方法了,这个方法接受一个参数,即纵向的偏移位置。那么,很重要的问题:我们怎么能得到要定位的那个控件在 ScrollViewer 中的位置呢?

在我之前写的这篇文章中:XAML: 获取元素的位置,有如何获到元素相对位置的介绍,建议大家先了解一下,其中使用了 Visual.TransformToVisual 方法等。当你理解了这篇文章后,再回过头来看本文后面的内容,就很容易了。

接下来,我们使用以下代码,即可实现上述需求:

1
2
3
4
5
6
7
// 获取要定位之前 ScrollViewer 目前的滚动位置
 var currentScrollPosition = ScrollViewer.VerticalOffset;
 var point = new Point(0, currentScrollPosition);
 
 // 计算出目标位置并滚动
 var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
 ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);

另外,由于通常情况下,我们会采用 MVVM 模式,因此我们可以将上述代码封装成一个 Action,而避免在 Code-Behind 代码文件中添加上述代码。

新创建的名为 ScrollToControlAction 的 Action,在其中定义两个依赖属性 ScrollViewer 和 TargetControl,分别表示指定的要操作的 ScrollViewer 和要定位到的控件,然后将上述代码放到其 Invoke 方法中即可。由于 Action 并非本文主题,所以这里并不会展开太多的讲解,可以参考以下代码或本文后提供的 Demo 作进一步了解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
namespace ScrollTest
{
 /// <summary>
 /// 在 ScrollViewer 中定位到指定的控件
 /// 说明:目前支持的是垂直滚动
 /// </summary>
 public class ScrollToControlAction : TriggerAction<FrameworkElement>
 {
 public static readonly DependencyProperty ScrollViewerProperty =
 DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollToControlAction), new PropertyMetadata(null));
 
 public static readonly DependencyProperty TargetControlProperty =
 DependencyProperty.Register("TargetControl", typeof(FrameworkElement), typeof(ScrollToControlAction), new PropertyMetadata(null));
 
 /// <summary>
 /// 目标 ScrollViewer
 /// </summary>
 public ScrollViewer ScrollViewer
 {
 get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
 set { SetValue(ScrollViewerProperty, value); }
 }
 
 /// <summary>
 /// 要定位的到的控件
 /// </summary>
 public FrameworkElement TargetControl
 {
 get { return (FrameworkElement)GetValue(TargetControlProperty); }
 set { SetValue(TargetControlProperty, value); }
 }
 
 protected override void Invoke(object parameter)
 {
 if (TargetControl == null || ScrollViewer == null)
 {
 throw new ArgumentNullException($"{ScrollViewer} or {TargetControl} cannot be null");
 }
 
 // 检查指定的控件是否在指定的 ScrollViewer 中
 // TODO: 这里只是指定离它最近的 ScrollViewer,并没有继续向上找
 var container = TargetControl.FindParent<ScrollViewer>();
 if (container == null || container != ScrollViewer)
 {
 throw new Exception("The TargetControl is not in the target ScrollViewer");
 }
 
 // 获取要定位之前 ScrollViewer 目前的滚动位置
 var currentScrollPosition = ScrollViewer.VerticalOffset;
 var point = new Point(0, currentScrollPosition);
 
 // 计算出目标位置并滚动
 var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
 ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);
 }
 }
}

其使用方法如下:

1
2
3
4
5
6
7
<Button>
 <i:Interaction.Triggers>
  <i:EventTrigger EventName="Click">
  <local:ScrollToControlAction ScrollViewer="{Binding ElementName=s}" TargetControl="{Binding ElementName=txtSectionC}" />
  </i:EventTrigger>
 </i:Interaction.Triggers>
</Button>

至此,结合 Action,我们以非常灵活的方式实现了本文所提出的需求。

源码下载

WPF实现ScrollViewer滚动到指定控件处的更多相关文章

  1. WPF: 实现 ScrollViewer 滚动到指定控件处

    在前端 UI 开发中,有时,我们会遇到这样的需求:在一个 ScrollViewer 中有很多内容,而我们需要实现在执行某个操作后能够定位到其中指定的控件处:这很像在 HTML 页面中点击一个链接后定位 ...

  2. WPF布局之让你的控件随着窗口等比放大缩小,适应多分辨率满屏填充应用

    一直以来,我们设计windows应用程序,都是将控件的尺寸定好,无论窗体大小怎么变,都不会改变,这样的设计对于一般的应用程序来说是没有问题的,但是对于一些比较特殊的应用,比如有背景图片的,需要铺面整个 ...

  3. WPF自定义控件与样式(9)-树控件TreeView与菜单Menu-ContextMenu

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

  4. WPF自定义控件与样式(10)-进度控件ProcessBar自定义样

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

  5. WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案

    首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如  WPF中不规则窗体与WebBrowser控件的兼 ...

  6. WPF笔记(1.9 样式和控件模板)——Hello,WPF!

    原文:WPF笔记(1.9 样式和控件模板)--Hello,WPF! 资源的另一个用途是样式设置: <Window >  <Window.Resources>    <St ...

  7. WPF自定义控件(三)の扩展控件

    扩展控件,顾名思义就是对已有的控件进行扩展,一般继承于已有的原生控件,不排除继承于自定义的控件,不过这样做意义不大,因为既然都自定义了,为什么不一步到位呢,有些不同的需求也可以通过此来完成,不过类似于 ...

  8. Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)

    jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...

  9. WPf 带滚动条WrapPanel 自动换行 和控件右键菜单

    原文:WPf 带滚动条WrapPanel 自动换行 和控件右键菜单 技能点包括 WPf 样式的引用 数据的验证和绑定 比较适合初学者 前台: <Window.Resources> < ...

随机推荐

  1. python 生成式和生成器

    #!/usr/bin/env python # -*- coding:utf-8 -*- # @Time : 2017/10/17 21:46 # @Author : lijunjiang # @Fi ...

  2. python 复习-2

    把一个数字的list从小到大排序,然后写入文件,然后从文件中读取出来文件件内容,然后反序,再追加到文件的下一行中 """把一个数字的list从小到大排序,然后写入文件,然 ...

  3. CAS统一认证系统学习

    Yale CAS(耶鲁集中认证服务)简介 http://www.5iops.com/html/2012/sso_0713/181.html 单点登陆框架CAS的研究 http://www.cnblog ...

  4. AC日记——[Sdoi2016]数字配对 bzoj 4514

    4514 思路: 很受伤现在,,测了那么多次不过的原因就是因为INF不够大: 解法有两种: 解法1: 把n个点按照质因数个数为奇或偶分为两个点集(很容易就可以想到): 然后,按照题目连边跑最大费用流: ...

  5. iOS--子视图如何穿透上层视图响应事件

    一.使用方法:- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; 二.背景知识iOS系统检 ...

  6. java 读写操作

    java代码: 写入: public void getNotice(HttpServletRequest request, String notice){ String message = JSON. ...

  7. libev和libuv的区别

    libuv 和 libev ,两个名字相当相近的 I/O Library,最近有幸用两个 Library 都写了一些东西,下面就来说一说我本人对两者共同与不同点的主观表述. 高性能网络编程这个话题已经 ...

  8. 228. 汇总区间(leetcode)

    #整体思路:使用堆栈,在Python中可以使用列表代替:如果a[i]-a[i-1]==1,就要将a[i]合并到之前的区间里,#所以我们队首位元素开辟一个区间为[a[0],a[0]]#做最后汇总时候,如 ...

  9. GIS可视化——麻点图

    一.引言 目前在客户端绘制POI(Point of Interest,兴趣点)的方式主要是div(Marker的形式).svg.canvas.VML(后边三种就是Vector Layer)几种方式,这 ...

  10. linux中find的用法

    找所在目录的文件用 find -name “file*” -print  #注意不要用加文件路径,查找文件也好用双双引号括住: 也可以 find ./ -name “file*” -print