前言:我在迁移旧项目代码的时候发现别人写很多界面都涉及到一个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添加一个全选的功能的更多相关文章

  1. wpf DataGrid CheckBox列全选

    最近在wpf项目中遇到当DataGrid的header中的checkbox选中,让该列的checkbox全选问题,为了不让程序员写自己的一堆事件,现写了一个自己的自定义控件 在DataGrid的 &l ...

  2. MVVM框架下,WPF实现Datagrid里的全选和选择

    最近的一个项目是用MVVM实现,在实现功能的时候,就会有一些东西,和以前有很大的区别,项目中就用到了常用的序号,就是在Datagrid里的一个字段,用checkbox来实现. 既然是MVVM,就要用到 ...

  3. Echarts使用一个图例legend实现全选和全部取消的功能

    1.修改legend的data值,在前面加上全选和全不选,data = ['全选','全不选',1,2,3] 2.监听 legendselectchanged事件 / 使用刚指定的配置项和数据显示图表 ...

  4. 利用GCD 中的 dispatch_source_timer 给tableViewCell添加动态刷新的计时/倒计时功能

    1.思路一(失败) 在设置好cell 里的内容之后在每个cell 返回时调用定时器事件,更新cell 内容,然后刷新整个表格. - (void)didadida:(UITableViewCell *) ...

  5. 【读书笔记】iOS-storyBoard-为一个按钮添加一个点击事件

    按照故事板的用语,应用中的一个界面屏幕被称作一个”场景(Scene)",以后添加额外的场景时,停靠区中将有另一个部分. 一,新建立一个工程,如图所示. 二,选中Main.storyboard ...

  6. 用Vue实现一个全选指令

    最近用vue做了两个项目,都需要实现全选反选的功能,两个项目用了两种实现方法,第一个项目用vue的computed,第二个项目用指令来实现,用起来,发觉指令更加方便. 第一次做全选的时候是刚开始接触v ...

  7. 如何在datagridview 的head上绘制一个全选按钮

    winform的项目中,经常要用到datagridview控件,但是为控件添加DataGridViewCheckBoxColumn来实现数据行选择这个功能的时候,经常需要提供全选反选功能,如果不重绘控 ...

  8. WPF自定义行为Behavior,实现双击控件复制文本

    WPF引用xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity& ...

  9. 【转】利用Behavior Designer制作敌人AI

    http://www.unity.5helpyou.com/3112.html 本篇unity3d教程,我们来学习下利用Behavior Designer行为树插件来制作敌人AI,下面开始! Beha ...

  10. WPF [调用线程无法访问此对象,因为另一个线程拥有该对象。] 解决方案以及如何实现字体颜色的渐变

    本文说明WPF [调用线程无法访问此对象,因为另一个线程拥有该对象.] 解决方案以及如何实现字体颜色的渐变 先来看看C#中Timer的简单说明,你想必猜到实现需要用到Timer的相关知识了吧. C# ...

随机推荐

  1. HAL+CubeIDE,STM32F407ZGT6正点原子探索者,舵机驱动,从零开始

    CubeIDE_HAL库_从零开始玩舵机 1.材料准备 开发板:正点原子STM32F407ZGT6探索者 舵机:SG90 舵机线材分辨:褐色 / 红色 / 橘黄色 -- GND / VCC / PWM ...

  2. 基于surging的木舟平台如何构建起微服务

    一.概述 木舟平台分为微服务平台和物联网平台, 上面几篇都是介绍如何通过网络组件接入设备,那么此篇文章就细致介绍下在木舟平台下如何构建微服务. 木舟 (Kayak) 是什么? 木舟(Kayak)是基于 ...

  3. AI千恋万花(java调用api实现)附完整项目及注释)重置版)

    感觉博客的第一版质量有点低下了,删了重置一下,希望能给其他人的代码带来一些灵感 前情提要:https://www.cnblogs.com/h4o3/p/18523151 由于是匆忙制作的老婆系统,主界 ...

  4. OpenMM的安装与使用

    技术背景 OpenMM是一款基于Python开发的开源分子动力学模拟软件,这几年因为AlphaFold的缘故,使得这个软件的热度有了不少提升.并且可以使用GPU硬件加速,所以性能上也不赖.这里介绍一下 ...

  5. [昌哥IT课堂]使用MySQL Shell 部署沙盒数据库实例详解

    概述:这部分解释了如何使用AdminAPI设置沙盒部署.部署和使用本地MySQL的沙盒实例是开始探索AdminAPI的好方法.在将功能部署到生产服务器之前,您可以在本地测试功能.AdminAPI具有内 ...

  6. elementUI 选择开始结束日期加限制

    需求是开始结束日期不得大于当前时间,当开始日期发生变化时,结束日期不得小于开始日期且不得大于当前日期 <el-form-item label="开始日期:"> < ...

  7. Goland破解之无限重置(最新)

    分享一下 JetBrains 全家桶 IDEA 2021.x 的激活破解教程,相当于永久激活 破解了,亲测有效,下面是详细文档哦~ JetBrains 全家桶 IDEA 2021.x 破解激活教程,相 ...

  8. mysql5.7之密码重置

    一.windows下更改mysql数据库密码在windows下找到my.ini文件,例如:C:\ProgramData\MySQL\MySQL Server 5.7,打开该文件夹下的my.ini文件, ...

  9. redis教程(Mac)

    1.首先,检查是否已经安装Homebrew,如果没有安装Homebrew,请先安装 2.使用Homebrew安装命令,在终端输入以下命令 brew install redis 当前默认安装5.0.8版 ...

  10. AI让照片跳舞,人人都能是舞王!Swan下载介绍

    最近,兵马俑.马斯克以及各地网友跳科目三和网红舞的视频陆续在社交媒体和朋友圈刷屏,这些大约10秒左右的视频都不是真人出镜,均由大模型生成,这种低门槛的跳舞方式引发了网友的广泛体验,掀起了一波斗舞狂潮「 ...