欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-10-30 15:09:34
在前端 ui 开发中,有时,我们会遇到这样的需求:在一个 scrollviewer 中有很多内容,而我们需要实现在执行某个操作后能够定位到其中指定的控件处;这很像在 htm...

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

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

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

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

// 获取要定位之前 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 作进一步了解。

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

其使用方法如下:

<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,我们以非常灵活的方式实现了本文所提出的需求。

 源码下载

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。