引言

如题,如何以Binding的方式动态隐藏DataGrid列?

预想方案

像这样:

先在ViewModel创建数据源 People 和控制列隐藏的 IsVisibility,这里直接以 MainWindowDataContext

 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(例如,PanelWindowButton 等都是由 Visual 对象构建而成)。

Visual 是 WPF 中可视化对象模型的基础,而 Visual 对象通过形成可视化树(Visual Tree)来组织所有可视化模型。所以Visual Tree 是一个层次结构,包含了所有界面元素的视觉表示。所有继承自 VisualUIElement(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 资源,给 DataGridTextColumnVisibility 绑定资源

<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 为什么不在可视化树结构内,是因为所有继承自 VisualUIElement(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列的更多相关文章

  1. MVVM框架下 WPF隐藏DataGrid一列

    最近的一个项目,需要在部分用户登录的时候,隐藏DataGrid中的一列,但是常规的绑定不好使,在下面举个例子. XAML部分代码 <Window x:Class="DataGridCo ...

  2. 编写 WPF DataGrid 列模板,实现更好的用户体验

    Julie Lerman 下载代码示例 最近我在为一个客户做一些 Windows Presentation Foundation (WPF) 方面的工作. 虽然我提倡使用第三方工具,但有时也会避免使用 ...

  3. easyui datagrid 列隐藏和显示

    easyui datagrid 列隐藏和显示 用js怎么控制列的显示和隐藏?   最佳答案   $('#grid').datagrid('hideColumn','列field');把hideColu ...

  4. WPF DataGrid 列宽填充表格方法

    WPF中使DataGrid 列宽填充表格方法,设置ColumnWidth属性为ColumnWidth="*"即可. 源码: <DataGrid AutoGenerateCol ...

  5. js控制easyui datagrid列的显示和隐藏

    easyui datagrid 列隐藏和显示 $('#grid').datagrid('hideColumn','列field'); //把hideColumn换成showColumn 即为显示列

  6. jQuery动态显示和隐藏datagrid中的某一列的方法

    在EasyUI中: 1)展示某列的方法:     $('#jgrid').datagrid('showColumn', 'XXX');  -----其中 XXX 是隐藏列的  field 属性值 2) ...

  7. EasyUI datagrid列隐藏与显示

    隐藏DataGrid某一列 $("#datagrid_view").datagrid('hideColumn', filed); 2. 显示DataGrid隐藏的某一列 $(&qu ...

  8. WPF入门教程系列二十三——DataGrid示例(三)

    DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...

  9. WPF入门教程系列二十一——DataGrid示例(一)

    前面我们学习了ListView控件的使用示例,今天我们来学习DataGrid的有关知识.提到DataGrid 不管是Asp.Net中的网页开发还是WinForm应用程序开发都会频繁使用.通过它我们可以 ...

  10. wpf将表中数据显示到datagrid示例(转)

    原文:http://www.jb51.net/article/47120.htm 这篇文章主要介绍了wpf将表中数据显示到datagrid示例,需要的朋友可以参考下 a.在.xaml文件中拖入一个da ...

随机推荐

  1. NFS快速入门(一):简介、原理

    NFS网络文件共享存储 什么是NFS NFS 是 Network File System 的缩写,中文意思是网络文件系统.它的主要功能是通过网络(一般是局域网)让不同主机系统之间可以共享文件或目录.N ...

  2. Wow: 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架

    领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源 架构图 事件源 可观测性 OpenAPI (Spring WebFlux 集成) 自动注册 命 ...

  3. .Net Web API 006 Controller上传大文件

    1.上传大文件的方式 上传大文件就需要一段一段的上传,主要是先在客户端获取文件的大小,例如想一次传256kb,那就按照256kb分割.分割后又两种上传方式. (1)逐个数据段读取,然后调用API上传, ...

  4. 部署安装zookeeper集群

    版本:3.7.0 节点IP: 172.50.13.103 172.50.13.104 172.50.13.105 获取安装包: wget http://mirrors.ustc.edu.cn/apac ...

  5. nacos适配SqlServer、Oracle

    继上文<nacos适配达梦.瀚高.人大金仓数据库及部分源码探究>后补充nacos适配SqlServer.Oracle的贴码,主要区别是SqlServer.Oracle的分页SQL有点不一样 ...

  6. Web通用漏洞--sql注入

    SQL注入 mysql注入目的:获取当前web权限 mysql注入--常规查询&union联合查询 MYSQL--Web组成架构 服务器搭建web服务可能存在多个站点搭建在一台服务器中,数据集 ...

  7. 商品详情接口设计:使用API调用获取淘宝商品数据的完整方案

    ​ 在如今的电商时代,获取商品的详细信息是实现商业化应用的基础.本文将详细介绍如何通过API调用来获取淘宝商品数据,并提供一个完整的商品详情接口设计方案,包括代码示例.开发人员可以根据此方案快速实现商 ...

  8. 达梦数据库-DW-国产化--九五小庞

    武汉达梦数据库股份有限公司成立于2000年,是国内领先的数据库产品开发服务商,国内数据库基础软件产业发展的关键推动者.公司为客户提供各类数据库软件及集群软件.云计算与大数据等一系列数据库产品及相关技术 ...

  9. 使用shuffle sharding增加容错性

    使用shuffle sharding增加容错性 最近在看kubernetes的API Priority and Fairness,它使用shuffle sharding来为请求选择处理队列,以此防止高 ...

  10. Blazor Server 发起HttpPost请求,但是多参数

    一.介绍 今天突然想起之前工作上遇到的一个问题,在做Blazor 开发时后端给的一个接口请求方式是Post ,但是他需要携带多个参数,新建一个公共类又觉得麻烦,我就尝试着怎么在Post请求中携带多个参 ...