WPF --- 如何以Binding方式隐藏DataGrid列
引言
如题,如何以Binding的方式动态隐藏DataGrid列?
预想方案
像这样:
先在ViewModel创建数据源 People 和控制列隐藏的 IsVisibility,这里直接以 MainWindow 为 DataContext
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
Persons = new ObservableCollection<Person>() { new Person() { Age = 11, Name = "Peter" }, new Person() { Age = 19, Name = "Jack" } };
DataContext = this;
}
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isVisibility;
public bool IsVisibility
{
get => isVisibility;
set
{
isVisibility = value;
OnPropertyChanged(nameof(IsVisibility));
}
}
private ObservableCollection<Person> persons;
public ObservableCollection<Person> Persons
{
get { return persons; }
set { persons = value; OnPropertyChanged(); }
}
}
然后创建 VisibilityConverter,将布尔值转化为 Visibility。
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool isVisible && isVisible)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然后再界面绑定 IsVisibility,且使用转化器转化为Visibility,最后增加一个 CheckBox 控制是否隐藏列。
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding Persons}"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn
Header="年龄"
Width="*"
Binding="{Binding Age}"
Visibility="{Binding DataContext.IsVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type Window}}, Converter={StaticResource VisibilityConverter}}" />
<DataGridTextColumn Header="姓名" Width="*" Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
<CheckBox
Grid.Column="1"
Content="是否显示年龄列"
IsChecked="{Binding IsVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
这样应该没问题,Visibility 是依赖属性,能直接通过 Binding 的方式赋值。
但实际测试时就会发现,勾选 CheckBox 能够改变 DataContext.IsVisibility 的值,但是无法触发转换器 VisibilityConverter,即使不用 RelativeSource 方式,更改为指定 ElementName获取元素的方式,也一样不生效。
这是为什么呢?
我疑惑了很久,直到看到了Visual Studio中的实时可视化树:

从图中可以看出,虽然我在 Xaml 中声明了两列 DataGridTextColumn,但他根本不在可视化树中。
获取 RelativeSource 和指定 ElementName 的方式,本质上还是在可视化树中寻找元素,所以上述方案无法生效。
那为什么 DataGridTextColumn 不在可视化树中呢?
可视化树(Visula Tree)
在上面那个问题之前,先看看什么是可视化树?
我们先从微软文档来看一下WPF中其他控件的继承树。
比如 Button

比如 DataGrid :

又比如 ListBox :

大家可以去看看其他的控件,几乎 WPF 中所有的控件都继承自 Visual(例如,Panel、Window、Button 等都是由 Visual 对象构建而成)。
Visual 是 WPF 中可视化对象模型的基础,而 Visual 对象通过形成可视化树(Visual Tree)来组织所有可视化模型。所以Visual Tree 是一个层次结构,包含了所有界面元素的视觉表示。所有继承自 Visual 或 UIElement(UI 元素的更高级别抽象)的对象都存在于可视化树中。
但是,DataGridColumn 是一个特例,它不继承 Visual,它直接继承 DependencyObject,如下:

所以,DataGridColumn的继承树就解答了他为什么不在可视化树中。
解决方案
所以,通过直接找 DataContext 的方式,是不可行的,那就曲线救国。
既然无法找到承载 DataContext.IsVisibility 的对象,那就创建一个能够承载的对象。首先该对象必须是 DependencyObject 类型或其子类,这样才能使用依赖属性在 Xaml 进行绑定,其次必须有属性变化通知功能,这样才能触发 VisibilityConverter,实现预期功能。
这时候就需要借助一个抽象类 System.Windows.Freezable。摘取部分官方解释如下:


从文档中可以看出 Freezable 非常符合我们想要的,第一它本身继承 DependencyObject 且 它在子属性值更改时能够提供变化通知。
所以我们可以创建一个自定义 Freezable 类,实现我们的功能,如下:
public class CustomFreezable : Freezable
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(CustomFreezable));
public object Value
{
get => (object)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
protected override void OnChanged()
{
base.OnChanged();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
}
protected override Freezable CreateInstanceCore()
{
return new CustomFreezable();
}
}
然后在 Xaml 添加 customFreezable 资源,给 DataGridTextColumn 的 Visibility 绑定资源
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter" />
<local:CustomFreezable x:Key="customFreezable" Value="{Binding IsVisibility, Converter={StaticResource VisibilityConverter}}" />
</Window.Resources>
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding Persons}"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn
x:Name="personName"
Width="*"
Binding="{Binding Age}"
Header="年龄"
Visibility="{Binding Value, Source={StaticResource customFreezable}}" />
<DataGridTextColumn
Width="*"
Binding="{Binding Name}"
Header="姓名" />
</DataGrid.Columns>
</DataGrid>
<CheckBox
Grid.Column="1"
Content="是否显示年龄列"
IsChecked="{Binding IsVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
测试:
勾选后,显示年龄列:

取消勾选后,隐藏年龄列

小结
本篇文章中,首先探索了 DataGridTextColumn 为什么不在可视化树结构内,是因为所有继承自 Visual 或 UIElement(UI 元素的更高级别抽象)的对象才存在于可视化树中。,DataGridTextColumn是直接继承DependencyObject ,所以才不在可视化树结构内。
其次探索如何通过曲线救国,实现以 Binding 的方式实现隐藏DataGridTextColumn,我们借助了一个核心抽象类 System.Windows.Freezable。该抽象类是 DependencyObject 的子类,能使用依赖属性在 Xaml 进行绑定,且有属性变化通知功能,触发 VisibilityConverter转换器,实现了预期功能。
如果大家有更优雅的方案,欢迎留言讨论。
参考
stackoverflow - how to hide wpf datagrid columns depending on a propert?: https://stackoverflow.com/questions/6857780/how-to-hide-wpf-datagrid-columns-depending-on-a-property
Freezable Objects Overview: https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/freezable-objects-overview?view=netframeworkdesktop-4.8&wt.mc_id=MVP
WPF --- 如何以Binding方式隐藏DataGrid列的更多相关文章
- MVVM框架下 WPF隐藏DataGrid一列
最近的一个项目,需要在部分用户登录的时候,隐藏DataGrid中的一列,但是常规的绑定不好使,在下面举个例子. XAML部分代码 <Window x:Class="DataGridCo ...
- 编写 WPF DataGrid 列模板,实现更好的用户体验
Julie Lerman 下载代码示例 最近我在为一个客户做一些 Windows Presentation Foundation (WPF) 方面的工作. 虽然我提倡使用第三方工具,但有时也会避免使用 ...
- easyui datagrid 列隐藏和显示
easyui datagrid 列隐藏和显示 用js怎么控制列的显示和隐藏? 最佳答案 $('#grid').datagrid('hideColumn','列field');把hideColu ...
- WPF DataGrid 列宽填充表格方法
WPF中使DataGrid 列宽填充表格方法,设置ColumnWidth属性为ColumnWidth="*"即可. 源码: <DataGrid AutoGenerateCol ...
- js控制easyui datagrid列的显示和隐藏
easyui datagrid 列隐藏和显示 $('#grid').datagrid('hideColumn','列field'); //把hideColumn换成showColumn 即为显示列
- jQuery动态显示和隐藏datagrid中的某一列的方法
在EasyUI中: 1)展示某列的方法: $('#jgrid').datagrid('showColumn', 'XXX'); -----其中 XXX 是隐藏列的 field 属性值 2) ...
- EasyUI datagrid列隐藏与显示
隐藏DataGrid某一列 $("#datagrid_view").datagrid('hideColumn', filed); 2. 显示DataGrid隐藏的某一列 $(&qu ...
- WPF入门教程系列二十三——DataGrid示例(三)
DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...
- WPF入门教程系列二十一——DataGrid示例(一)
前面我们学习了ListView控件的使用示例,今天我们来学习DataGrid的有关知识.提到DataGrid 不管是Asp.Net中的网页开发还是WinForm应用程序开发都会频繁使用.通过它我们可以 ...
- wpf将表中数据显示到datagrid示例(转)
原文:http://www.jb51.net/article/47120.htm 这篇文章主要介绍了wpf将表中数据显示到datagrid示例,需要的朋友可以参考下 a.在.xaml文件中拖入一个da ...
随机推荐
- Pandas:获取Dataframe索引
解决方案 效果图 参考链接 https://blog.csdn.net/YENTERTAINR/article/details/109254583
- RocketMq消费原理及源码解析
消费原理概览 先简单说下常见的rocketMq的部署方式,上图中broker为真正计算和存储消息的地方,而nameServer负责维护broker地 图中右侧consume message部分即是本文 ...
- 洛谷 P1336 最佳课题选择 题解
P1336 最佳课题选择 题解 状态:考虑\(f_{i,j}\)表示前\(i\)种论文里面,一共写了\(j\)篇,的最少花费时间. 转移策略:我们一次考虑每一种论文写多少篇.假设写\(k\)篇,\(k ...
- Hugging News #0821: Hugging Face 完成 2.35 亿美元 D 轮融资
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...
- 纯前端导出word手写复杂表格,并还原成word。百分百还原表格。一文搞定前端表格导出为word
本次的需求是手写一个养老院老人生活能力评定表,并且要能够录入信息,最终导出 表格因为有七页所以代码很多,可以不用看表格模板的详细代码. 先贴上最终效果图 填写完导出之后 基本上实现了样式的百分百还原导 ...
- 别再用 offset 和 limit 分页了,性能太差!
不需要担心数据库性能优化问题的日子已经一去不复返了. 随着时代的进步,随着野心勃勃的企业想要变成下一个 Facebook,随着为机器学习预测收集尽可能多数据的想法的出现. 作为开发人员,我们要不断地打 ...
- 分享一个 SpringBoot + Redis 实现「查找附近的人」的小技巧
前言 SpringDataRedis提供了十分简单的地理位置定位的功能,今天我就用一小段代码告诉大家如何实现. 正文 1.引入依赖 <dependency> <groupId> ...
- 云上的甜蜜早安:腾讯云云函数助力PHP打造女友专属每日推送
用腾讯云的云函数做一个微信公众号早安,每天定时发送早安给你的女朋友! 1.首先我们登录腾讯云,在搜索栏搜索云函数,或直接用这个链接进入curl.qcloud.com/Td0IkpmD 2.进入云函数, ...
- 【krpano】KRPano打开黑屏: FATAL ERROR
在KRPano开发过程中,初学者打开项目经常遇到如下的问题: FATAL ERROR:tour.xml – loading failed! (0) 或者是: ERROR:Local usage wit ...
- 485modbus转profinet网关连接威纶通与三菱变频器modbus通讯
485modbus转profinet网关连三菱变频器modbus通讯触摸屏监控 本案例介绍了如何通过485modbus转profinet网关连接威纶通与三菱变频器进行modbus通讯.485modbu ...