引言

如题,如何以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. Yunfly 一款高效、性能优异的 node.js web 框架

    介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用. 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers ...

  2. c# 如何将程序加密隐藏?

    下面将介绍如何通过LiteDB将自己的程序进行加密,首先介绍一下LiteDB. LiteDB LiteDB是一个轻量级的嵌入式数据库,它是用C#编写的,适用于.NET平台.它的设计目标是提供一个简单易 ...

  3. quarkus数据库篇之二:无需数据库也能运行增删改查(dev模式)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇内容并非数据库相关的核心知识,而是对一个 ...

  4. CodeForces-1324D-Pair-of-Topics

    题意 对于两个长度为\(n\)的数组\(a[]\)和\(b[]\),找到有多少对\(i\)和\(j\)\((i<j)\),满足\(a_i+a_j>b_i+b_j\) 分析 首先发现如果\( ...

  5. WPF MVVM之点滴分享

    (第五点:绑定源有修改) 我并不打算长篇累牍的介绍什么是MVVM.我尽量简洁的介绍,并把自己的经验分享给大家. 一.关于MVVM M:Model,数据模型(后台存储数据的类) V:View,视图(大部 ...

  6. 避坑|在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次;|UI自动化测试|数据驱动

    在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次: 原因:excel.xlsx内容清除时用delete快捷键导致, ...

  7. 《Python魔法大冒险》003 两个神奇的魔法工具

    魔法师:小鱼,要开始编写魔法般的Python程序,我们首先需要两个神奇的工具:Python解释器和代码编辑器. 小鱼:这两个工具是做什么的? 魔法师:你可以把Python解释器看作是一个魔法棒,只要你 ...

  8. 安装软件提示 "无法完成操作, 因为文件包含病毒或潜在的垃圾软件" 如何处理

    在Windows端安装一些小众电脑软件的时候,经常会遇到无法安装的问题,比较常见的情况是会提示 "无法完成操作, 因为文件包含病毒或潜在的垃圾软件", 或者提示"不能执行 ...

  9. JDK8升级JDK11最全实践干货来了

    1.前言 截至目前(2023年),Java8发布至今已有9年,2018年9月25日,Oracle发布了Java11,这是Java8之后的首个LTS版本.那么从JDK8到JDK11,到底带来了哪些特性呢 ...

  10. JVM面试题、关键原理、JMM

    boolean:占用1个字节,取值为true或false. byte:占用1个字节,范围为-128到127. short:占用2个字节,范围为-32,768到32,767. int:占用4个字节,范围 ...