Bindungssichtbarkeit für DataGridColumn in WPF

75

Wie kann ich eine Spalte in einem WPF DataGriddurch eine Bindung ausblenden ?

Das habe ich getan:

<DataGridTextColumn Header="Column header"
                    Binding="{Binding ColumnValue}"
                    Width="100"
                    ElementStyle="{StaticResource DataGridRightAlign}"
                    Visibility="{Binding MyColumnVisibility}" />

Und das habe ich bekommen (außer der noch sichtbaren Spalte):

System.Windows.Data-Fehler: 2: FrameworkElement oder FrameworkContentElement für das Zielelement können nicht gefunden werden. BindingExpression: Path = MyColumnVisibility; DataItem = null; Zielelement ist 'DataGridTextColumn' (HashCode = 1460142); Die Zieleigenschaft ist 'Sichtbarkeit' (Typ 'Sichtbarkeit').

Wie behebe ich die Bindung?

ygoe
quelle

Antworten:

175

Erstens DataGridTextColumnoder andere unterstützte dataGrid-Spalten liegen nicht im visuellen Baum vonDataGrid . Daher erbt esDataContextDataGrid standardmäßig nicht von . Es funktioniert jedoch nur für BindingDP und nicht für andere DPs in DataGridColumn.

Da sie nicht in demselben VisualTree liegen, funktioniert jeder Versuch, DataContext zu verwenden RelativeSource, nicht so gut, da DataGrid nicht in der Lage ist, zu DataGrid zu wechseln.

Es gibt jedoch zwei Möglichkeiten, dies zu erreichen:


Erste Verwendung von FreezableKlassenobjekten Freezablekann den DataContext erben, auch wenn sie sich nicht im visuellen oder logischen Baum befinden. So können wir das für unsere Nutzung nutzen.

Erstellen Sie zuerst eine Klasse, die von Freezableund DataDP erbt, die wir zum Binden in XAML verwenden können:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

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

    #endregion

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

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
                                     typeof(BindingProxy));
}

Fügen Sie nun eine Instanz davon in DataGrid-Ressourcen hinzu, damit es den DataContext von DataGrid erben und dann mit seinem Data DP binden kann:

    <DataGrid>
        <DataGrid.Resources>
            <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
                                                Source={StaticResource proxy}}"/>
        </DataGrid.Columns>
    </DataGrid>

Zweitens können Sie mit ElementNameoder auf jedes UI-Element in XAML verweisen x:Reference. Funktioniert jedoch ElementNamenur im selben visuellen Baum, während x: Reference keine solche Einschränkung aufweist.

Das können wir also auch zu unserem Vorteil nutzen. Erstellen Sie einen Dummy FrameworkElementin XAML, wobei die Sichtbarkeit auf "Reduziert" gesetzt ist. FrameworkElement erbt DataContext von seinem übergeordneten Container, der Window oder UserControl sein kann.

Und kann das in DataGrid verwenden:

    <FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Test"
                                Binding="{Binding Name}"
                                Visibility="{Binding DataContext.IsEnable,
                                          Source={x:Reference dummyElement}}"/>
        </DataGrid.Columns>
    </DataGrid>
Rohit Vats
quelle
3
Ich mag diesen zweiten Ansatz. Es ist einfach zu schreiben und ich habe bereits ein anderes Steuerelement für die gleiche Sichtbarkeit, sodass ich nur ein x:Nameund einen Verweis auf seine VisibilityEigenschaft geben kann. Nicht wirklich geradlinig, mehr seitwärts drehend, aber immer noch einfach. Ich denke, wenn Sie an die DataContext-Eigenschaft des referenzierten Elements binden, "entführen" Sie das andere Element, um seinen DataContext für die ansonsten nicht erreichbare DataGridColumn freizugeben, oder? Das DummyElement ist nur die Brücke.
Ygoe
2
@LonelyPixel - Ja, du hast es richtig gemacht. Ich versuche, DataContext von seinem untergeordneten DataGrid-Geschwister zu entführen, da beide denselben DataContext verwenden, sofern dies nicht explizit festgelegt wurde. Ich hätte x: Reference mit DataGrid selbst verwenden können, aber das hätte zu einer zyklischen Abhängigkeit geführt.
Rohit Vats
1
+1 für deine Antwort. Es tut mir leid, ich habe die Frage falsch verstanden. Durch die Verwendung von x:Reference- in WPF 4.0 kann zumindest für Visual Studio 2010 immer noch eine Ausnahme auftreten : Service provider is missing the INameResolver service, kann ignoriert werden. Und so wie ich es verstehe, wurde es in WPF 4.5 behoben.
Anatoliy Nikolaev
2
Persönlich, wenn Sie mich fragen, mag ich erste Annäherung. Overhead ist nur, um eine Klasse zu erstellen, aber sobald Sie es in Ihrem Kätzchen haben, wird das Leben in XAML viel einfacher zu codieren. Ich benutze es öfter.
Rohit Vats
2
@JMIII Weiß nicht, ich benutze das jetzt nirgendwo. Außerdem ist es mir egal, was der XAML-Editor versteht (es ist nicht viel), solange er am Ende ausgeführt wird.
Ygoe
18
<Window.Resources>
    <ResourceDictionary>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
    </ResourceDictionary>
</Window.Resources>

<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
                  AutoGenerateColumns="False"
                  FlowDirection="LeftToRight"
                  ItemsSource="{Binding PayStructures}"
                  SelectedItem="{Binding SelectedItem}">
    <DataGrid.Columns>
         <DataGridTemplateColumn Width="70"
                                 Header="name"
                                 IsReadOnly="True"
                                 Visibility="{Binding DataContext.IsShowName,
                                 Source={StaticResource ProxyElement}}">
             <DataGridTemplateColumn.CellTemplate>
                 <DataTemplate>
                     <TextBlock Text="{Binding FieldName}" />
                 </DataTemplate>
             </DataGridTemplateColumn.CellTemplate>
         </DataGridTemplateColumn>                   
     </DataGrid.Columns>
</mch:MCHDataGrid>

Beispiel für eine gebundene Eigenschaft im Ansichtsmodell:

private Visibility _isShowName;

public Visibility IsShowName
{
    get { return _isShowName; }
    set
    {
        _isShowName = value;
        OnPropertyChanged();
    }
}
Meysam Chegini
quelle
Ich denke, das wurde bereits vor einem Jahr vorgeschlagen. Zu spät.
Ygoe
Wenn Sie die Klasse des aktuellen DataContext drucken möchten, verwenden Sie Folgendes:<TextBlock Text="{Binding DataContext, Source={StaticResource ProxyElement}}"></TextBlock>
Contango
Funktioniert nicht, wenn der Datenkontext tatsächlich nicht statisch ist, sondern variieren kann. In diesem Fall wird Folgendes angezeigt: "System.Windows.Data-Fehler: 3: Element, das DataContext bereitstellt, kann nicht gefunden werden. BindingExpression: (kein Pfad); DataItem = null; Zielelement ist 'FrameworkElement' (Name = 'ProxyFrameworkElement'); Ziel Die Eigenschaft ist 'DataContext' (Typ 'Object') ", wenn das Fenster erstellt wird.
JS