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

来源:https://www.cnblogs.com/pandefu/archive/2023/11/21/17847807.html
-Advertisement-
Play Games

引言 如題,如何以Binding的方式動態隱藏DataGrid列? 預想方案 像這樣: 先在ViewModel創建數據源 People 和控制列隱藏的 IsVisibility,這裡直接以 MainWindow 為 DataContext public partial class MainWindo ...


引言

如題,如何以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

作者: Niuery Daily

出處: https://www.cnblogs.com/pandefu/>

郵箱: [email protected]

關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Assistants介紹 隨著OpenAI將Assistants助手API對外發佈,我們搭建個人知識庫變的如此簡單。開發者將自己的應用通過Assistants API與OpenAI對接,就可以讓每一位客戶擁有不一般體驗的個人知識庫。由於Assistants相關API有30+,本文只列舉完成一個最小功 ...
  • 事務是保證業務操作完整性的一種資料庫機制,具有原子性、一致性、隔離性和持久性(ACID)的特點。 在Java中,可以通過JDBC和MyBatis來控制事務,底層都是通過Connection對象完成的。 Spring使用AOP的方式進行事務開發,通過將事務的額外功能封裝在DataSourceTrans... ...
  • 事件緣起我在Linux伺服器(CentOS 7.8)安裝Python3.10,並替換python軟鏈接為python3之後,yum命令不可用。特此記錄一下。 完整安裝步驟如下: Python3.10安裝 1.使用yum程式提前安裝Python依賴。 yum install wget zlib-dev ...
  • 作者:FishBones 鏈接:https://juejin.cn/post/7185479136599769125 背景 公司的一個ToB系統,因為客戶使用的也不多,沒啥併發要求,就一直沒有經過壓測。這兩天來了一個“大客戶”,對併發量提出了要求:核心介面與幾個重點使用場景單節點吞吐量要滿足最低50 ...
  • 在本篇文章中,我們總結了Python中的異常捕獲的重要性以及如何進行優化。異常捕獲是一種處理程式在執行過程中出現錯誤的機制,對於程式的穩定性和可靠性至關重要。我們詳細學習了Python中的基本異常捕獲語法,包括try、except、else和finally塊,並舉例了常見的異常類型,總之,閱讀本文只... ...
  • scipy.signal模塊主要用於處理和分析信號。它提供了大量的函數和方法,用於濾波、捲積、傅里葉變換、雜訊生成、周期檢測、譜分析等信號處理任務。 此模塊的主要作用是提供一套完整的信號處理工具,從而幫助用戶對各種連續或者離散的時間序列數據、音頻信號、電信號或其他物理信號進行操作和分析。它支持許多標 ...
  • 回調賦予我們很好的函數處理能力,那麼非同步回調則是在非同步編程中的靈魂,這是不可缺少的存在,本章主要是關於Rust中的trait的說明,希望對你有所幫助 ...
  • Welcome to YARP - 1.認識YARP並搭建反向代理服務 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 - 配置提供者(Configuration Providers) 2.3 - 配置過濾器(Configur ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...