前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF DataGrid 如何将被选中行带到视野中

WPF DataGrid 如何将被选中行带到视野中

作者头像
独立观察员
发布2022-12-06 19:09:12
1.8K0
发布2022-12-06 19:09:12
举报

WPF DataGrid 如何将被选中行带到视野中

目录

前言

准备工作

方法一

方法二

总结

独立观察员 2021 年 12 月 11 日

前言

在 WPF 开发中,显示表格一般使用 DataGrid 控件,而且我们一般会依据用户的选中行的操作来执行一些逻辑,这种情况,选中了哪一行,用户是心知肚明的。而还有一种情况,我们可能在业务逻辑中,由程序自己选中了某一行,如果这一行当前不在用户界面的可视区(换句话说也就是滚动条没有滚到那个位置),那么我们如何将其带到用户的视野中呢?

准备工作

今天准备介绍两个方法。正所谓,工欲善其事必先利其器,所以在开始之前,我们先来构建一个可以模拟后台选中行的功能。

使用的还是之前用过的 DataGrid 的 Demo 程序(在《WPF DataGrid 通过自定义表头模拟首行固定》和《WPF 触屏事件后触发鼠标事件的问题及 DataGrid 误触问题》中用过),加了一个可以填写要选中的行号的文本框,以及一个执行选中操作的按钮:

下面来演示一下没有自动将选中行带到视野中的情况。我们先将数据添加到 10 条,然后缩小程序的窗口,这样有些数据就在滚动区外面了,也就是不在视野中。然后我们通过程序来选中行,可以看到选中功能是正常的,但是对于视野外的数据,用户看不到其是否选中,需要手动滚动来寻找,如下图(动图):

好,那接下来就介绍怎么解决吧。

方法一

这个方法是参考《【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF》文章中的将 TreeViewItem(树状列表项)带到视野中的方法,我稍微改造了一下,使其同时支持 DataGridRow 和 TreeViewItem,并且之后如果有其它受支持的类型也可以方便地扩展。BringIntoViewBehavior 类提供了一个 IsBroughtIntoViewWhenSelected 附加属性,给每个列表项的 Selected 事件指定了处理方法,处理方法中调用 BringIntoView () 方法,完整代码如下:

代码语言:javascript
复制
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,并在选中项改变时调用(参数为选中行的索引):

代码语言:javascript
复制
/// <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 方法,代码如下:

代码语言:javascript
复制
_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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 独立观察员博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • WPF DataGrid 如何将被选中行带到视野中
    • 前言
      • 准备工作
        • 方法一
          • 方法二
            • 总结
            相关产品与服务
            云开发 CloudBase
            云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档