首页 > 其他分享 >WPF --- 如何以Binding方式隐藏DataGrid列

WPF --- 如何以Binding方式隐藏DataGrid列

时间:2023-11-21 22:44:08浏览次数:42  
标签:IsVisibility --- Freezable object Binding DataGrid Visual 可视化 public

引言

如题,如何以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中的实时可视化树:

image.png

从图中可以看出,虽然我在 Xaml 中声明了两列 DataGridTextColumn,但他根本不在可视化树中。

获取 RelativeSource 和指定 ElementName 的方式,本质上还是在可视化树中寻找元素,所以上述方案无法生效。

那为什么 DataGridTextColumn 不在可视化树中呢?

可视化树(Visula Tree)

在上面那个问题之前,先看看什么是可视化树?

我们先从微软文档来看一下WPF中其他控件的继承树。

比如 Button

image.png

比如 DataGrid

image.png

又比如 ListBox

image.png

大家可以去看看其他的控件,几乎 WPF 中所有的控件都继承自 Visual(例如,PanelWindowButton 等都是由 Visual 对象构建而成)。

Visual 是 WPF 中可视化对象模型的基础,而 Visual 对象通过形成可视化树(Visual Tree)来组织所有可视化模型。所以Visual Tree 是一个层次结构,包含了所有界面元素的视觉表示。所有继承自 VisualUIElement(UI 元素的更高级别抽象)的对象都存在于可视化树中。

但是,DataGridColumn 是一个特例,它不继承 Visual,它直接继承 DependencyObject,如下:

image.png

所以,DataGridColumn的继承树就解答了他为什么不在可视化树中。

解决方案

所以,通过直接找 DataContext 的方式,是不可行的,那就曲线救国。

既然无法找到承载 DataContext.IsVisibility 的对象,那就创建一个能够承载的对象。首先该对象必须是 DependencyObject 类型或其子类,这样才能使用依赖属性在 Xaml 进行绑定,其次必须有属性变化通知功能,这样才能触发 VisibilityConverter,实现预期功能。

这时候就需要借助一个抽象类 System.Windows.Freezable。摘取部分官方解释如下:

image.png
image.png

从文档中可以看出 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>

测试:

勾选后,显示年龄列:
image.png

取消勾选后,隐藏年龄列
image.png

小结

本篇文章中,首先探索了 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

标签:IsVisibility,---,Freezable,object,Binding,DataGrid,Visual,可视化,public
From: https://www.cnblogs.com/pandefu/p/17847807.html

相关文章

  • 13--2. 两数相加
    【首先,题目是给了两个链表,我们可以直接使用哦】我们要自己创建一个链表来存储结果//将第一个节点的值初始化为0,用来指向头指针(意思就是pre后面的那个值才是头指针),返回结果LinkNodepre=newLinkNode(0);//cur是一个可移动的指针,作为储存两个数之和的位置LinkNodecur=......
  • 前端学习-JavaScript学习-js基础05
    学习教程:黑马程序员视频链接对象了解<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Docum......
  • B站C语言 ----结构体
    //描述一个学生////struct结构体关键字 Stu-结构体标签 stuctStu-结构体类型//structStu//{// //成员变量// charname[20];// shortage;// chartele[12];// charsex[5];////}s1,s2,s3;//s1,s2,s3是三个全局的结构体变量//typedefstructStu//{// //......
  • kvm-虚拟机登陆方式VNC、virsh console
    阅读目录(Content)1、虚拟机多,VNC登陆问题2、多虚拟机,VNC登陆的实战3、使用virsh console登陆实战3.1、需求3.2、虚拟机开启支持console 3.3、登陆测试3.4、退回virshconsole方法回到顶部(gototop)1、虚拟机多,VNC登陆问题当我们虚拟机过多的时候,如果想用vn......
  • 2023-2024-2 20232404 《网络空间安全导论》第2周学习总结
    教材学习内容总结密码学概述总结:1代换和置换,构造现代成对密码算法最重要核心;2香农是当代密码及信息学的集大成者,大多有关学说无法跳出其框架。密码学基本概念总结:1、RSA是第一个既能用于数据加密也能用于数字签名的算法;2与数学联系紧密,且中国也作出了一定贡献。密码学新......
  • 组队学习-学习笔记P1
    聪明办法学Python2ndEditionChapter1启航GettingStarted注释Comment分类:单行注释,使用#开头多行注释,使用'''或"""包裹起来作用:注释主要是用于对代码进行解释和说明,可以提升代码的可读性注释并不会被当做代码处理#magiccomment除外程序员最讨厌的1......
  • A____Z____RECOVER____DATA勒索恢复---惜分飞
    有客户MySQL数据库被黑,业务库中表被删除,并创建A____Z____RECOVER____DATA库,里面有一张readme表,内容为:mysql>select*fromreadme\G;***************************1.row***************************zh_content:请尽快与我们取得联系,否则我们将会公布你的数据......
  • 无涯教程-Sed - 特殊字符
    SED提供了两个特殊字符,它们被视为命令。本章说明了这两个特殊字符的用法。=命令"="命令显示行号。以下是"="命令的语法:[/pattern/]=[address1[,address2]]==命令将行号及其内容写入标准输出流。以下示例说明了这一点。[jerry]$sed'='books.txt执行上述代码后,您将......
  • day - 1
    Python学习一Chap1启航注释分类:单行注释,使用#开头;(只能写在一行中)多行注释,使用‘’‘’‘’‘或“”“”“”包裹起来(单引号(')与双引号("")在python中没有太大区别);作用:注释主要是用于对代码进行解释和说明,可以提升代码的可读性注释并不会被当作代码处......
  • 2023-11-21 hexo next主题 如何在博客网站底部添加备案号
    主题:NexT.Pisces v5.1.4找到路径:博客目录名称\themes\hexo-theme-next\layout\_partials找到文件:footer.swig,并在里面添加备案号,如图:未改变前:<divclass="copyright">{##}{%setcurrent=date(Date.now(),"YYYY")%}{##}&copy;{%iftheme.footer.since......