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

原文: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);
        }
    }
}

ScrollToControlAction.cs

其使用方法如下:

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

源码下载

原文地址:https://www.cnblogs.com/lonelyxmas/p/8443628.html

时间: 2024-08-04 21:24:22

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

【WPF学习】第二十章 内容控件

原文:[WPF学习]第二十章 内容控件 内容控件(content control)是更特殊的控件类型,它们可包含并显示一块内容.从技术角度看,内容控件时可以包含单个嵌套元素的控件.与布局容器不同的是,内容控件只能包含一个子元素,而布局容器主要愿意可以包含任意多个牵头元素. 正如前面所介绍,所有WPF布局容器都继承自抽象类Panel,该类提供了对包含多个元素的支持.类似地,所有内容控件都继承自抽象类ContentControl.下图显示了ContentControl类的层次结构. 图 Conten

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

一直以来,我们设计windows应用程序,都是将控件的尺寸定好,无论窗体大小怎么变,都不会改变,这样的设计对于一般的应用程序来说是没有问题的,但是对于一些比较特殊的应用,比如有背景图片的,需要铺面整个屏幕,由于存在多种不同的分辨率,所以会出现布局混乱的情况.今天我们来看看WPF中如何让我们的控件也随着分辨率放大缩小.下面来写一个例子看看效果吧~  一.普通布局中的问题 这里我们写一个简单的页面,新建WPF项目,在MainWindow里面添加按钮,如下图: 这个页面很简单,只有三个按钮,我们想的是

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

一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: ProcessBar自定义标准样式: ProcessBar自定义环形进度样式: 二.ProcessBar标准样式 效果图: ProcessBar的样式非常简单: <!--ProgressBar Style--> <Style TargetType="ProgressBar" x

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

原文:WPF笔记(1.9 样式和控件模板)--Hello,WPF! 资源的另一个用途是样式设置: <Window >  <Window.Resources>    <Style x:Key="myStyle" TargetType="{x:Type TextBlock}">      <Setter Property="VerticalAlignment" Value="Center"

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

首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如  WPF中不规则窗体与WebBrowser控件的兼容问题解决办法.该网友的解决办法也是别出心裁的,为什么这样说呢,你下载了他的程序认真读一下就便知道,他的webBrowser控件的是单独放在一个Form中,让这个Form与WPF中的一个Bord控件进行关联,进行同步移动,但是在移动的时候会出现闪烁,并且还会出现运动的白点,用户体验肯定不好. OK,

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

原文:WPf 带滚动条WrapPanel 自动换行 和控件右键菜单 技能点包括 WPf 样式的引用 数据的验证和绑定 比较适合初学者 前台: <Window.Resources> <local:PathToSource x:Key="n2"/> <Style x:Key="{x:Type ContextMenu}" TargetType="{x:Type ContextMenu}"> <Setter Pr

wpf PNG图形窗体 使用webbrowser控件

wpf ,PNG图形半透明窗体 ,使用webbrowser控件 附件:http://files.cnblogs.com/xe2011/WpfApplication1_webbrowser_transparent.rar MainWindow1.XAML <Window x:Name="MainWindow1" x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.co

1、CRM2011编程实战——清空指定页签下面的所有选项,并对页签下面的指定控件进行操作

需求:当页面加载时,"呼叫编号"保持不变,"任务号"自动更新,"接报时间"和"发生日期"自动设置为当天日期和时间,其它选项全部恢复为初始状态. 字段说明: 控件名称 值 控件名称 值 控件名称 值 反馈单登录 tab_4 任务号 hxcs_ftaskcode 接报时间 hxcs_fdatetimeofcasehappended 发生日期 hxcs_fdatetimeofcasehappended 呼叫编号 hxcs_fcall

4、CRM2011编程实战——将窗体中指定控件的值做处理后更新到另一个字段中

需求:将接报时间加上到期提醒时间后得到的值,更新到字段"到期截止时间" Js调用: //设置到期截止时间 function setDeadLine(){ var recordId = Xrm.Page.data.entity.getId(); var entityName = Xrm.Page.data.entity.getEntityName(); var reportedTime = Xrm.Page.getControl("hxcs_fdatetimeofrequest