WPF实现ScrollViewer滚动到指定控件处
在前端 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,我们以非常灵活的方式实现了本文所提出的需求。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 首个机器人“伴娘”亮相天津婚礼引围观