WPF DataGrid 如何将被选中行带到视野中
前言
在 WPF 开发中,显示表格一般使用 DataGrid 控件,而且我们一般会依据用户的选中行的操作来执行一些逻辑,这种情况,选中了哪一行,用户是心知肚明的。而还有一种情况,我们可能在业务逻辑中,由程序自己选中了某一行,如果这一行当前不在用户界面的可视区(换句话说也就是滚动条没有滚到那个位置),那么我们如何将其带到用户的视野中呢?
准备工作
今天准备介绍两个方法。正所谓,工欲善其事必先利其器,所以在开始之前,我们先来构建一个可以模拟后台选中行的功能。
使用的还是之前用过的 DataGrid 的 Demo 程序(在《WPF DataGrid 通过自定义表头模拟首行固定》和《WPF 触屏事件后触发鼠标事件的问题及 DataGrid 误触问题》中用过),加了一个可以填写要选中的行号的文本框,以及一个执行选中操作的按钮:
下面来演示一下没有自动将选中行带到视野中的情况。我们先将数据添加到 10 条,然后缩小程序的窗口,这样有些数据就在滚动区外面了,也就是不在视野中。然后我们通过程序来选中行,可以看到选中功能是正常的,但是对于视野外的数据,用户看不到其是否选中,需要手动滚动来寻找,如下图(动图):
好,那接下来就介绍怎么解决吧。
方法一
这个方法是参考《【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF》文章中的将 TreeViewItem(树状列表项)带到视野中的方法,我稍微改造了一下,使其同时支持 DataGridRow 和 TreeViewItem,并且之后如果有其它受支持的类型也可以方便地扩展。BringIntoViewBehavior 类提供了一个 IsBroughtIntoViewWhenSelected 附加属性,给每个列表项的 Selected 事件指定了处理方法,处理方法中调用 BringIntoView () 方法,完整代码如下:
using System.Windows; using System.Windows.Controls; /* * 源码已托管:https://gitee.com/dlgcy/WPFTemplateLib */ namespace WPFTemplateLib.WpfHelpers { /// <summary> /// 功能:列表项被选中时带到视野中 /// 参考:http://dlgcy.com/introduction-to-attached-behaviors-in-wpf/ /// 说明:用于 DataGrid 时需要设置 EnableRowVirtualization="False" /// </summary> /// <example> /// <code> /// Setter Property="wpfHelpers:BringIntoViewBehavior.IsBroughtIntoViewWhenSelected" Value="True"/> /// </code> /// </example> public class BringIntoViewBehavior { #region IsBroughtIntoViewWhenSelected public static bool GetIsBroughtIntoViewWhenSelected(FrameworkElement item) { return (bool)item.GetValue(IsBroughtIntoViewWhenSelectedProperty); } public static void SetIsBroughtIntoViewWhenSelected(FrameworkElement item, bool value) { item.SetValue(IsBroughtIntoViewWhenSelectedProperty, value); } /// <summary> /// 是否在选中时带到视野中 /// </summary> public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty = DependencyProperty.RegisterAttached( "IsBroughtIntoViewWhenSelected", typeof(bool), typeof(BringIntoViewBehavior), new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged)); static void OnIsBroughtIntoViewWhenSelectedChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { FrameworkElement item = depObj as FrameworkElement; if (item == null) return; if (e.NewValue is bool == false) return; switch (depObj) { case DataGridRow row: { if ((bool)e.NewValue) row.Selected += OnItemSelected; else row.Selected -= OnItemSelected; break; } case TreeViewItem treeViewItem: { if ((bool)e.NewValue) treeViewItem.Selected += OnItemSelected; else treeViewItem.Selected -= OnItemSelected; break; } default: break; } } static void OnItemSelected(object sender, RoutedEventArgs e) { //忽略所有只是报告子孙的 Selected 被触发的祖先。 if (!ReferenceEquals(sender, e.OriginalSource)) return; if (e.OriginalSource is FrameworkElement item) item.BringIntoView(); } #endregion } }
此方法用于 DataGrid 时需要设置 EnableRowVirtualization=”False”(默认为 true):
使用时只要在行样式中应用这个附加属性即可:
注意引入命名空间:
效果如下(动图):
方法二
如果开了行虚拟化(EnableRowVirtualization=”True”),离可视区较远的行的 Selected 事件就不会被触发,以上方法就不行了。
类似于这个帖子的情况《WPF 开启行虚拟化的时候,行选择功能不正常,求解决方案》(https://bbs.csdn.net/topics/392666509):
所以如果因为数据量比较大必须开启行虚拟化时,可以使用下面的方法。
先给 DataGrid 命个名(如 x:Name=”Dg” )方便后台使用,然后在 ViewModel 中添加一个选中项改变事件 SelectedItemChanged,并在选中项改变时调用(参数为选中行的索引):
/// <summary> /// 选中项改变事件 /// </summary> public event Action<int> SelectedItemChanged; private User _SelectedItem; /// <summary> /// 选中项 /// </summary> public User SelectedItem { get => _SelectedItem; set { SetProperty(ref _SelectedItem, value); SelectedItemChanged?.Invoke(Datas.IndexOf(_SelectedItem)); } }
接着在后台事件中注册事件处理方法,处理方法中调用了 DataGrid 的 ScrollIntoView 方法,代码如下:
_vm.SelectedItemChanged += OnSelectedItemChanged; /// <summary> /// 选中项改变事件执行方法 /// </summary> /// <param name="index">选中行索引</param> private void OnSelectedItemChanged(int index) { Dg.ScrollIntoView(Dg.Items.GetItemAt(index)); }
效果和方法一的一样,就不再赘述了。
总结
关于将 DataGrid 选中行带到视野中的需求,本文介绍了两种方法。方法一提供了一个附加属性,可以方便地实现该需求,不过要求不能开启行虚拟化。方法二则是需要在 ViewModel 和页面后台编写代码,通过事件来触发相关操作,不过可以支持行虚拟化。大家可以依据实际情况选择使用,如果有更好的方法,欢迎交流。
源代码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20211211
原创文章,转载请注明: 转载自 独立观察员(dlgcy.com)
本文链接地址: [WPF DataGrid 如何将被选中行带到视野中](https://dlgcy.com/wpf-datagrid-bring-selected-row-into-view/)
关注微信公众号 独立观察员博客(DLGCY_BLOG) 第一时间获取最新文章
发表评论