WPF: DataGridTextColumn basierend auf ViewModel-Eigenschaft ausblenden – wie geht das?

1 Antwort

Dein Binding ist nicht richtig, denn innerhalb des DataGridTextColumn-Kontexts beziehst du dich auf die Quelle des DataGrid. Das heißt, bei dir wird nicht im ViewModel nach dem Property IstNichtPM gesucht, sondern in dem Objekt, welches auch das Laenge-Property beinhaltet.

Du brauchst den Datenkontext deines DataGrid-Containers (z.B. Window/Grid/...), welches auf dein ViewModel referenzieren kann. Das ist in dem Fall jedoch nicht ganz so einfach, denn DataGridTextColumn-Komponenten liegen selbst nicht einmal in der visuellen Baumstruktur, sodass man einfach eine relative Quelle (RelativeSource) oder einen Elementnamen einer Komponente (z.B. dem DataGrid), welches Zugriff auf den richtigen Kontext hat, für das Binding angeben kann.

Mit einem freezable Proxy-Objekt, welches als statische Ressource definiert wird und den richtigen Datenkontext aufklaubt, klappt es aber.

Ich zeige es einmal an einem kompletten Beispiel.

Das Model:

namespace SomeExample
{
  public class Item
  {
    public int Length { get; set; }
  }
}

Das ViewModel:

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;

namespace SomeExample
{
  public class ViewModel : INotifyPropertyChanged
  {
    private bool _isNotPm;

    public ViewModel()
    {
      Items = CollectionViewSource.GetDefaultView(new List<Item>
      {
        new Item { Length = 3 },
        new Item { Length = 12 },
        new Item { Length = 5 }
      });
    }

    public ICollectionView Items { get; private set; }

    public bool IsNotPm
    {
      get => _isNotPm;
      set
      {
        if (_isNotPm != value)
        {
          _isNotPm = value;
          OnPropertyChanged(nameof(IsNotPm));
        }
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

Beide Klassen sind soweit noch ziemlich unspektakulär, das ViewModel beinhaltet unter anderem einigen Boilerplate-Code, um Änderungen des boolschen Properties registrieren zu können.

Der oben erwähnte Proxy ist ein freezable DependencyObject. Das ist an der Stelle wichtig, denn ein solches Objekt kann (sofern es nicht eingefroren ist) Eigenschaften (wie den DataContext) von seinem nächstliegenden Elternelement ziehen (unabhängig davon, ob dieses sich in der logischen oder visuellen Baumstruktur befindet). So genügt es später, es als statische Ressource zu definieren, statt ebenso eine Komponente in der visuellen Baumstruktur erstellen (und verstecken) zu müssen.

Die Implementation sieht so aus:

using System.Windows;

namespace SomeExample
{
  public class BindingProxy : Freezable
  {
    public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));

    public object Data
    {
      get => GetValue(DataProperty);
      set => SetValue(DataProperty, value);
    }

    protected override Freezable CreateInstanceCore()
    {
      return new BindingProxy();
    }
  }
}

Der XAML-Code:

<Window 
  x:Class="SomeExample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:SomeExample"
  mc:Ignorable="d"
  Title="Window1"
  Height="450"
  Width="800">
  <Window.DataContext>
    <local:ViewModel />
  </Window.DataContext>
  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
    <local:BindingProxy Data="{Binding}" x:Key="Proxy" />
  </Window.Resources>
  <StackPanel>
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}">
      <DataGrid.Columns>
        <DataGridTextColumn
          Binding="{Binding Length}"
          Header="Length"
          Visibility="{Binding Data.IsNotPm, Source={StaticResource Proxy}, Converter={StaticResource booleanToVisibilityConverter}}"
          Width="70" />
      </DataGrid.Columns>
    </DataGrid>
    <CheckBox Content="Show or Hide Length Column" IsChecked="{Binding IsNotPm, Mode=TwoWay}" />
  </StackPanel>
</Window>

Im oberen Teil wird einmal ein ViewModel-Objekt erstellt und als Datenkontext an die View gebunden. Außerdem werden der Konverter und das Proxy als statische Ressourcen angeführt. Das Proxy kann nun auf den Datenkontext von Window zugreifen, also konkret auf das gebundene ViewModel-Objekt. Explizit wird der Wert des DataContext-Property dem Data-Property zugewiesen.

Das DataGridTextColumn-Objekt kann sich anschließend über das Data-Property des Proxy-Objekts auf IsNotPm beziehen.

{Binding Data.IsNotPm, Source={StaticResource Proxy}, ...}

Zum einfacheren Testen habe ich unter dem DataGrid noch eine Checkbox eingefügt, die an IsNotPm hängt und es daher dem Nutzer erlaubt, die Spalte dynamisch ein- oder auszublenden.


EchoTech 
Beitragsersteller
 03.03.2025, 23:50

Danke. Habe es aber jetzt letztlich durch ein eigenes custom DGV gelöst. So bin ich auf der sicheren Seite