WPF: DataGridTextColumn basierend auf ViewModel-Eigenschaft ausblenden – wie geht das?
Hallo zusammen,
ich arbeite an einer WPF-Anwendung und habe ein DataGrid, in dem ich eine bestimmte Spalte ausblenden möchte, wenn eine boolesche Eigenschaft (IstPM) im ViewModel true ist.
<DataGridTextColumn Header="Länge"
Binding="{Binding Laenge, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=N1}"
Visibility="{Binding IstNichtPM, Converter={StaticResource booleanToVisibilityConverter}}"
Width="70"/>
Dabei ist IstNichtPM eine bool-Eigenschaft im ViewModel, die true sein sollte, wenn die Spalte sichtbar sein soll.
Ich verwende einen BooleanToVisibilityConverter, der true zu Visible und false zu Collapsed konvertiert.
Problem:
Die Spalte bleibt immer sichtbar oder wird nicht korrekt aktualisiert.
Ich habe auch versucht, es in Code-Behind zu ändern, aber das geht auch nicht, da man kein x-Name geben kann
„Der Wert „colLaenge“ des Name-Attributs kann für das Element „DataGridTextColumn“ nicht festgelegt werden.“
die IstPM und istNichtPM funktioniert aufjedenfall, da ich damit auch Tabs ausgeblendet habe in der Page. Das hat ohne Probleme funktioniert
=>
ausblenden bei IstNichtPM
<TabItem Header="Allgemein" Visibility="{Binding IstNichtPM, Converter={StaticResource booleanToVisibilityConverter}}">
einblenden bei IstPM
<TabItem Header="Allgemein" Visibility="{Binding IstPM, Converter={StaticResource booleanToVisibilityConverter}}">
beim DGV bin ich so langsam echt am verzweifeln.
Hat jemand eine funktionierende Lösung, wie ich eine DataGridTextColumn in WPF dynamisch ausblenden kann?
Bin für jeden Tipp dankbar!
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.
Danke. Habe es aber jetzt letztlich durch ein eigenes custom DGV gelöst. So bin ich auf der sicheren Seite