- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
前言:我在迁移旧项目代码的时候发现别人写很多界面都涉及到一个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的大佬都有).
。
最后此篇关于WPF怎么利用behavior优雅的给一个Datagrid添加一个全选的功能的文章就讲到这里了,如果你想了解更多关于WPF怎么利用behavior优雅的给一个Datagrid添加一个全选的功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
是否有办法获得一个 Behavior t [a],其中时间 t 时 [a] 的值是 Behavior t [Behavior t a] 中包含的值> 在时间 t?即,具有以下类型的函数: Behavi
(问题最初是由对 Are there race conditions in this producer-consumer implementation? 的回答下的评论提示的,但这里严格从 C 语言的
摘自本文:http://www-public.int-evry.fr/~gibson/Teaching/CSC7322/ReadingMaterial/Wegner87.pdf 它定义类型: type
阅读Akka 2.6.10 API Docs ,akka.actor.typed.scaladsl.Behaviors.setup 和 akka.actor.typed.scaladsl.Behavi
我从 easymock 和 JUnit 测试用例中得到了一些无法解释的行为。我收到 IllegalStateException:缺少前面方法调用的行为定义:myCollaborator.getCurr
在 akka typed 中,我们有行为的概念。 如果我们想保留 Actor 的相同行为,我们会在处理完一条消息后返回 Behaviors.same。但是我们也可以返回这个。两者有何不同? 最佳答案
我正在 Kubernetes 中创建一个 HorizontalPodAutoscaler,我需要将缩减稳定窗口配置为小于默认值。使用的代码和错误如下: apiVersion: autoscalin
在 Python 中,为什么 [:] 切片操作的行为不一致? 它对于列表和字符串的行为有所不同。 对于列表,它给出一个副本列表对象,对于字符串,它给出相同的字符串对象。 我觉得这令人困惑,违反直觉。有
我需要将一些对象存储到数据库中。 首先 我将它们存储在内存中(存储在集合中) 当其中一个正确存储在数据库中时,我会将其删除 所以, public class AuditService { pr
下面的程序 (prog1) 抛出 OutOfMemoryError 错误。确实如此。但如果我在第 5 行(prog2)下方添加 sysout,它不会抛出错误。这种奇怪的行为有什么原因吗? 程序1: p
我有以下二叉搜索树(在 C++ 中),我对特定代码行有疑问: delete k; 如果我删除该行,我的代码可以正常工作,但我不明白为什么。据我了解:来自 k 的数据被插入到树中,然后变量 k 被删除。
我想知道如果我尝试对已删除或可能尚未分配的指针执行 delete 会发生什么?我读过两件事:第一,delete 运算符会做一些检查,我们不需要检查指针是否为空;然后,我读到它会导致未知的行为.. 我在
我无法解释 Scala 集合的这种行为。 让我们从一些定义开始。 import scala.collection.mutable.Set case class Item(name: String, c
我一直在尝试在 wpf 窗口上实现一种行为,因此我在当前的解决方案中添加了对 System.Winodws.Interactivity 的引用,然后编写了所需的行为。但为了应用这种行为,我必须在 Wi
我试图理解 rdpmc 指令。因此,我有以下 asm 代码: segment .text global _start _start: xor eax, eax mov ebx, 10
我正在关注这里的测试:https://github.com/plone/plone.app.referenceablebehavior/blob/master/plone/app/referencea
行为(方法体)可以是状态机或事件 - 事件很容易理解,因为它们等同于过程代码。 我不明白状态机如何用作操作的行为? 您能为此提供一个简单的示例吗? ---注意--- Operation 是一个仅规范元
我正在尝试在 Cocoa 应用程序中实现自定义终止行为。通常,当我的应用程序正常退出时,它会执行最终运行时数据库清理,然后退出。每当调用 [NSApp Terminate:aSender] 时,都会在
这里没什么太严肃的,只是好奇。 我想举个例子,想出了这段代码: const { Observable, Subject } = Rx const timeout$ = new Subject() co
我希望类中的方法在 IO 线程上运行一些代码,但只有当它们订阅的主题具有特定值时。然后调用者应该在 Android UI 线程上收到响应。 类似这样的事情: public class MyClass
我是一名优秀的程序员,十分优秀!