WPF 怎么利用behavior优雅的给一个Datagrid添加一个全选的功能

news/2025/1/10 18:25:51/文章来源:https://www.cnblogs.com/lvpp13/p/18664485

前言:我在迁移旧项目代码的时候发现别人写很多界面都涉及到一个DataGrid的全选,但是每个都写的很混乱,现在刚好空闲下来,写一个博客,

给部分可能不太会写这个的同学讲一下,怎么实现全选功能,并且可以在任何项目里面复用这个功能。

先准备一个Datagrid,我们给这个DataGrid取名为 dg1

        <DataGrid AutoGenerateColumns="False" CanUserAddRows="False"ItemsSource="{Binding Path=.}"x:Name="dg1" Height="200"></DataGrid>

再准备一个实体类,并且给这个类添加属性变更通知,也就是实现 INotifyPropertyChanged:

        public class People : NotifyPropertyChangedBase{private bool _isChecked;public bool IsChecked{get { return _isChecked; }set { _isChecked = value; RaisePropertyChanged(); }}public string Name { get; set; }}public class NotifyPropertyChangedBase : INotifyPropertyChanged{public void RaisePropertyChanged([CallerMemberName] string PropertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));}public event PropertyChangedEventHandler PropertyChanged;}

上面的 NotifyPropertyChangedBase 是一个基类,也就是说我们的其他类,只要继承了这个类,我们的属性按照上面的添加 RaisePropertyChanged 方法,就可以实现和UI界面的交互。

把如图所示的Peoples的List赋值给dg1(这里分两种情况,因为DataGrid的数据源一般是List或者DataTable,我们先讲List)

 当然现在DataGrid我们还没有添加列,所以他还什么都显示不出来,所以我们还要给我们的DataGrid添加列

        <DataGrid AutoGenerateColumns="False" CanUserAddRows="False"ItemsSource="{Binding Path=.}"x:Name="dg1" Height="200">
            <DataGrid.Columns><DataGridCheckBoxColumn Binding="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/><DataGridTextColumn Binding="{Binding Name}" Header="Name"/></DataGrid.Columns></DataGrid>

如上面代码所示,添加了两列,一个是选择列,一个是姓名列。这个时候运行项目,会发现这个选择列特别奇怪,要点两次,里面的checkbox才会被选中,所以我们要改造一下这个选择列,我们自己写一个选择列出来。

<DataGrid AutoGenerateColumns="False" CanUserAddRows="False"ItemsSource="{Binding Path=.}"x:Name="dg1" Height="200"><DataGrid.Columns><DataGridTemplateColumn><DataGridTemplateColumn.CellTemplate><DataTemplate><CheckBox IsChecked="{Binding IsChecked,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center"/></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn><DataGridTextColumn Binding="{Binding Name}" Header="Name"/></DataGrid.Columns>
</DataGrid>

如上图所示,我们把DataGridCheckBoxColumn替换成上面红色的代码部分,也就是重新写一个模板,这个时候运行项目,和原本采用DataGridCheckBoxColumn的效果一样,但是我们现在只需要点击一下按钮就可以选中行了,

为了演示,我们可以自己添加一个TextBlock来清晰的显示我们是选中了哪一行数据

我们在dg1的上面添加一个TextBlock,代码如下

<TextBlock Text="{Binding ElementName=dg1, Path=SelectedItem.Name}" VerticalAlignment="Top"/>

如图所示,我们通过红色部分的代码进行绑定(如果你清晰的知道自己绑定的对象是个什么类型,我们都可以通过像红色部分代码一样来快捷的绑定,剩下的事情就交给WPF去帮我们做就行了)

运行一下我们的代码,然后切换一下选中行,textblock就会跟着选中行一起变化文字

 然后现在选择功能有了,还需要添加一个全选的功能,我们把选择列的列头改造一下,代码如下:

 <DataGrid AutoGenerateColumns="False" CanUserAddRows="False"ItemsSource="{Binding Path=.}"x:Name="dg1" Height="200"><DataGrid.Columns><DataGridTemplateColumn>
             <DataGridTemplateColumn.HeaderStyle><Style TargetType="{x:Type DataGridColumnHeader}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type DataGridColumnHeader}"><CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,1,0"/></ControlTemplate></Setter.Value></Setter></Style></DataGridTemplateColumn.HeaderStyle><DataGridTemplateColumn.CellTemplate><DataTemplate><CheckBox IsChecked="{Binding IsChecked,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center"/></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn><DataGridTextColumn Binding="{Binding Name}" Header="Name"/></DataGrid.Columns></DataGrid>

上面红色部分的代码是我们新增的代码,添加完以后,运行一下项目,就会发现列头变成了一个选择框,我们就是通过点击这个选择框来实现全选和全不选的功能

关键部分来了,如果我们只是想实现功能的话,就很简单,给这个checkbox添加checked事件和unchecked事件就行了,代码如下:

 <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,1,0"Checked="CheckBox_Checked"Unchecked="CheckBox_Unchecked"/>
        private void CheckBox_Checked(object sender, RoutedEventArgs e){foreach(var people in Peoples){people.IsChecked = true;}}private void CheckBox_Unchecked(object sender, RoutedEventArgs e){foreach (var people in Peoples){people.IsChecked = false;}}

添加完上面代码,我们运行项目,点击全选框,就可以实现全选和全不选,但是这样子一点也不优雅,不能复用,所以我们要改一下。

改这个就需要用到behavior这个东西,这个如果没用过的话,会觉得很不好理解,但是它不是很难,多用就知道怎么用了。

先去nuget上面安装一下依赖包

 找到上面这个  Behaviors.WPF,安装一下,然后添加如下代码:

    public class DataGridSelectedAllBehavior : Behavior<CheckBox>{protected override void OnAttached(){AssociatedObject.Checked += AssociatedObject_Checked;AssociatedObject.Unchecked += AssociatedObject_Unchecked;}protected override void OnDetaching(){AssociatedObject.Checked -= AssociatedObject_Checked;AssociatedObject.Unchecked -= AssociatedObject_Unchecked;}private void AssociatedObject_Unchecked(object sender, RoutedEventArgs e){MessageBox.Show("hello unchecked");}private void AssociatedObject_Checked(object sender, RoutedEventArgs e){MessageBox.Show("hello checked");}}
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,1,0"><i:Interaction.Behaviors><local:DataGridSelectedAllBehavior/></i:Interaction.Behaviors>
</CheckBox>

把之前的checked事件和unchecked事件给删掉,改成红色代码部分,添加命名空间:xmlns:i="http://schemas.microsoft.com/xaml/behaviors",
local是你项目的命名空间,根据项目添加,我的项目是叫wpfapp1,所以我的是:xmlns:local="clr-namespace:WpfApp1"

我们再运行一下项目,点击一下选择框,就会出现下面的提示

 如果成功的弹出了上面的提示,就说明到现在,代码都没有问题了,然后就是接着调整代码了。

 我们的 DataGridSelectedAllBehavior是继承的Behavior<CheckBox>,这里的Behavior括号里面是一个泛型,因为我们是把这个Behavior附加到CheckBox上的,所以我们就选择CheckBox,如果你想附加别的,比如Button,你就填写Button。

 然后它下面就会有AssociatedObject这个对象,我们附加的是什么东西,这个AssociatedObject就是个什么东西

 然后我们把代码改成如下所示:

        private void AssociatedObject_Unchecked(object sender, RoutedEventArgs e){var peoples = AssociatedObject.DataContext as List<People>;if (peoples != null){foreach(var people in peoples){people.IsChecked = false;}}}private void AssociatedObject_Checked(object sender, RoutedEventArgs e){var peoples = AssociatedObject.DataContext as List<People>;if (peoples != null){foreach (var people in peoples){people.IsChecked = true;}}}

到这里还没完,你运行会发现,我们的AssociatedObject的DataContext是一个null值,所以我们还要修改一下xaml里面的代码

<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,1,0"DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext}"><i:Interaction.Behaviors><local:DataGridSelectedAllBehavior/></i:Interaction.Behaviors>
</CheckBox>

通过上面红色部分的代码,我们就可以把我的这个Peoples的list传递给我们的AssociatedObject,我们再运行项目,就实现了全选和全不选的功能。

但是到这里还没完,因为People这个对象肯定不能用到项目里面去啊,这个只是一个测试类,项目里面又不是每个类都有  选择  这个属性的,那怎么办

我们通过接口来实现

    public class People : NotifyPropertyChangedBase, IModelIsChecked{private bool _isChecked;public bool IsChecked{get { return _isChecked; }set { _isChecked = value; RaisePropertyChanged(); }}public string Name { get; set; }}public interface IModelIsChecked{bool IsChecked { get; set; }}

添加一个  IModelIsChecked  的interface,然后让我们的People继承它

再修改一下我们behavior的代码

        private void AssociatedObject_Unchecked(object sender, RoutedEventArgs e){var peoples = AssociatedObject.DataContext as IList;if (peoples != null){foreach(var people in peoples){var item = people as IModelIsChecked;if (item != null){item.IsChecked = false;}}}}private void AssociatedObject_Checked(object sender, RoutedEventArgs e){var peoples = AssociatedObject.DataContext as IList;if (peoples != null){foreach (var people in peoples){var item = people as IModelIsChecked;if (item != null){item.IsChecked = true;}}}}

这样我们的类就和我们的Behavior解耦了,我们只要后面的类实现了这个IModelIsChecked 的接口,就都能实现全选功能了。

现在还有一种情况,就是如果我们的数据源不是一个List,而是一个DataTable的情况,一个是可以采用把DataTable转化成List的形式然后走上面的逻辑,还有一个就是同样可以修改我们的behavior来实现

private void AssociatedObject_Unchecked(object sender, RoutedEventArgs e)
{var peoples = AssociatedObject.DataContext as IList;if (peoples != null){foreach(var people in peoples){var item = people as IModelIsChecked;if (item != null){item.IsChecked = false;}}}var dataTable = AssociatedObject.DataContext as DataTable;if(dataTable != null){foreach(DataRow row in dataTable.Rows){row["IsChecked"] = false;}}
}private void AssociatedObject_Checked(object sender, RoutedEventArgs e)
{var peoples = AssociatedObject.DataContext as IList;if (peoples != null){foreach (var people in peoples){var item = people as IModelIsChecked;if (item != null){item.IsChecked = true;}}}var dataTable = AssociatedObject.DataContext as DataTable;if (dataTable != null){foreach (DataRow row in dataTable.Rows){row["IsChecked"] = true;}}
}
            DataTable dt = new DataTable();dt.Columns.Add("IsChecked",typeof(bool));dt.Columns.Add("Name", typeof(string));dt.Rows.Add(false, "Tom");dt.Rows.Add(false, "Jerry");dg1.DataContext = dt;

只要我们的DataTable有名为IsChecked的列就好了

如果没有IsChecked怎么办

你知道我要说什么的

这里推荐大家加一下QQ群:332035933   (这里面平时基本上没什么人说话,但是如果有人问问题,都会很积极的回答,java,.net,vue的大佬都有)。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867262.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用chai3d-GEL模块进行软体模型力反馈仿真的一点碎片化记录

在要模拟的网格模型中手动添加节点或者对于形状比较复杂的模型使用TetGen之类的网格划分程序自动添加节点和连接;然后设置合理的仿真参数(质量、刚度、重力、时间步长...) 骨架驱动:SkeletonModel 使用骨架结构来表示变形体。骨架由一系列节点(cGELSkeletonNode)和连接这…

销售新手必看:七大关键要素助你快速蜕变行业精英

在销售领域的探索之路上,我时常遇到新入行的同仁们询问如何迅速提升自我。回望自己初涉销售的时光,那段没有专业背景支撑,也缺乏资深前辈指引的日子,我选择了最质朴的方式——埋首于书店中,搜寻销售相关的书籍。然而,那些偏重理论研究的书籍,并未给予我太多实战中的助力…

电机控制的数字化升级:基于DSP和FPGA的仿真与实现

​数字信号处理器(DSP,Digital Signal Processor)在工业自动化领域的应用日益广泛。DSP是一种专门用于将模拟信号转换成数字信号并进行处理的技术,能够实现信号的数字滤波、重构、调制和解调等多项功能,确保信号处理的精确性和稳定性。特别是在电机控制系统中,DSP的应用尤…

中考英语优秀范文-热点话题-传统文化-001 Chinese Treasures: Opera Paper Art 国宝探秘:京剧与剪纸

1 写作要求 坚定文化自信,弘扬中华优秀传统文化,做中华文明的传播者是新时代中国青少年的责任与使命。假如你是李华,近期,一批外国朋友要到你所在的学校参观访问,请用英语写一篇发言稿,向他们介绍中华优秀传统文化。 内容包括: 1 欢迎朋友们的到来; 2 总体介绍中国传统…

货梯载人监控报警自动停梯系统

货梯载人监控报警自动停梯系统利用安装在货梯轿厢内的监控摄像头,实时捕捉轿厢内的画面信息,货梯载人监控报警自动停梯系统一旦确认有人员进入货梯轿厢监测范围内,系统便会迅速输出报警信号。同时,与电梯控制系统实现联动,强制电梯不关门、不运行,从而将人员拒之于危险之…

【YashanDB知识库】使用DBeaver 插入数据 nvarchar字段插入为空

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7901516.html?templateId=1718516 【问题分类】DBeaver使用 【关键字】DBeaver、nvarchar 【问题描述】使用DBeaver ,插入数据nvarchar字段插入为空。其他字段都有数据,且插入没有报错。【问题原…

[.NET] 使用客户端缓存提高API性能

在现代应用程序中,性能始终是一个关键的考虑因素。无论是提高响应速度,降低延迟,还是减轻服务器负载,开发者都在寻找各种方法来优化他们的API。在Web开发中,利用客户端缓存是一种有效的方法,可以显著提高API的性能。本文将结合Replicant与Delta库,深入探讨如何在.NET中使…

ABB机器人IO板DSQC651维修检查方法

ABB机器人作为工业自动化的重要设备,其稳定性和可靠性对于生产线的持续运行至关重要。然而,在实际使用中,由于各种原因,可能会出现ABB机器人IO板DSQC651故障,影响机器人的正常运行。 一、ABB工业机械臂IO板故障的常见原因 ABB机器人IO板故障的原因可能有很多,常见的包括电…

Flutter进阶(6):详解 Key

一、Key是什么? 用官方的说法就是:Key 是 Widget、Element 和 SemanticNodes 的唯一标识符。 组件在更新的时候,其状态的保存主要是通过判断组件的类型或者 key 值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用 key。…

数据的涟漪效应:实时数据赋能保险资产管理,打造数字化、信息化、智能化的保险新业态

本期内容以保险公司的具体案例为切入点,探讨数字化技术如何助力保险资产管理行业应对数据管理的挑战并实现创新突破。使用 TapData,化繁为简,摆脱手动搭建、维护数据管道的诸多烦扰,轻量替代 OGG, Kettle 等同步工具,以及基于 Kafka 的 ETL 解决方案,「CDC + 流处理 + 数…

KingbaseES RAC在线扩容案例

KingbaseES、KingbaseES RAC案例说明: KingbaseES RAC在两节点的基础上,执行在线扩容为3节点。 集群版本: test=# select version();version ---------------------KingbaseES V008R006 (1 row)集群架构:操作系统: [root@node210 KingbaseHA]# cat /etc/os-release NAME=&…

Xinference:企业级大模型推理和部署平台

导读 近年来,大语言模型(LLM,后简称大模型)的发展突飞猛进。本文将分享未来速度公司在开源领域以及大模型落地过程中的核心产品——Xinference,其定位是一个真正企业级的、严肃的大模型推理和部署平台。公司致力于在 LLMOps(大模型运维)过程中,帮助大家降低部署的成本和…