Wie verwende ich WPF-Bindungen mit RelativeSource?

Antworten:

783

Wenn Sie an eine andere Eigenschaft des Objekts binden möchten:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}

Wenn Sie eine Eigenschaft für einen Vorfahren erhalten möchten:

{Binding Path=PathToProperty,
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

Wenn Sie eine Eigenschaft für das übergeordnete Element mit Vorlagen erhalten möchten (damit Sie in einer ControlTemplate bidirektionale Bindungen erstellen können)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}

oder kürzer (dies funktioniert nur für OneWay-Bindungen):

{TemplateBinding Path=PathToProperty}
Abe Heidebrecht
quelle
15
Für dieses "{Binding Path = PathToProperty, RelativeSource = {RelativeSource AncestorType = {x: Type typeOfAncestor}}}" muss anscheinend "Mode = FindAncestor" vor "AncestorType"
EdwardM
1
Für welche Technologie? In WPF wird dies abgeleitet, wenn Sie eine angeben AncestorType.
Abe Heidebrecht
2
Ich stimme @EdwardM zu. Wenn ich FindAncestorvorher weglasse , AncestorTypewird folgende Fehlermeldung angezeigt: "RelativeSource befindet sich nicht im FindAncestor-Modus". (In VS2013, Community-Version)
kmote
1
@kmote, das hat bei mir seit .net 3.0 funktioniert, und ich habe noch einmal überprüft, ob es in kaxaml so funktioniert ... Wieder, welche Technologie verwenden Sie? Der XAML-Prozessor unterscheidet sich für WPF / Silverlight / UWP, sodass Sie bei verschiedenen Technologien möglicherweise unterschiedliche Ergebnisse erzielen. Sie haben auch VS Community erwähnt. Vielleicht handelt es sich also um eine IDE-Warnung, die jedoch zur Laufzeit funktioniert.
Abe Heidebrecht
6
Ich wollte hier nur beachten, dass Sie, wenn Sie an eine Eigenschaft im DataContext der RelativeSource binden möchten, diese explizit angeben müssen : {Binding Path=DataContext.SomeProperty, RelativeSource=.... Dies war für mich als Neuling etwas unerwartet, als ich versuchte, innerhalb einer DataTemplate an den DataContext eines Elternteils zu binden.
DrEsperanto
133
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

Das Standardattribut von RelativeSourceist die ModeEigenschaft. Ein vollständiger Satz gültiger Werte wird hier angegeben ( von MSDN ):

  • PreviousData Ermöglicht das Binden des vorherigen Datenelements (nicht des Steuerelements, das das Datenelement enthält) in die Liste der angezeigten Datenelemente.

  • TemplatedParent Bezieht sich auf das Element, auf das die Vorlage (in der das datengebundene Element vorhanden ist) angewendet wird. Dies ähnelt dem Festlegen einer TemplateBindingExtension und gilt nur, wenn sich die Bindung in einer Vorlage befindet.

  • Selbst Bezieht sich auf das Element, für das Sie die Bindung festlegen, und ermöglicht es Ihnen, eine Eigenschaft dieses Elements an eine andere Eigenschaft desselben Elements zu binden.

  • FindAncestor Bezieht sich auf den Vorfahren in der übergeordneten Kette des datengebundenen Elements. Sie können dies verwenden, um an einen Vorfahren eines bestimmten Typs oder dessen Unterklassen zu binden. Dies ist der Modus, den Sie verwenden, wenn Sie AncestorType und / oder AncestorLevel angeben möchten.

Drew Noakes
quelle
128

Hier ist eine visuellere Erklärung im Kontext einer MVVM-Architektur:

Geben Sie hier die Bildbeschreibung ein

Jeffrey Knight
quelle
19
Habe ich etwas verpasst? Wie können Sie das als einfache und klare Grafik betrachten? 1: Die Bedeutung der Felder auf der linken Seite hängt nicht wirklich mit der auf der rechten Seite zusammen (warum befindet sich im ViewModel eine CS-Datei?) 2: Auf was zeigen diese DataContext-Pfeile? 3: Warum befindet sich die Message-Eigenschaft nicht im ViewModel1? und vor allem 5: Warum benötigen Sie eine RelativeSource-Bindung, um zum DataContext des Fensters zu gelangen, wenn der TextBlock bereits denselben DataContext hat? Mir fehlt hier eindeutig etwas, also bin ich entweder ziemlich dumm oder diese Grafik ist nicht so einfach und klar, wie jeder denkt! Bitte klären Sie mich auf
Markus Hütter
2
@ MarkusHütter Das Diagramm zeigt einer Gruppe verschachtelte Ansichten und entsprechende ViewModels. Der DataContext von View1 ist ViewModel1, möchte jedoch an eine Eigenschaft von BaseViewModel gebunden werden. Da BaseViewModel der DataContext von BaseView (einem Fenster) ist, kann es dies tun, indem es den ersten übergeordneten Container findet, der ein Fenster ist, und seinen DataContext verwendet.
Mcargille
6
@MatthewCargille Ich weiß sehr gut , was es soll bedeuten, dass nicht mein Punkt war. Aber versetzen Sie sich in die Position von jemandem, der XAML und MVVM nicht gut kennt, und Sie werden sehen, dass dies nicht einfach und klar ist .
Markus Hütter
1
Ich muss @ MarkusHütter übrigens zustimmen, die Bindung auf der linken Seite könnte so einfach sein: {Binding Message}(etwas einfacher ...)
florien
@florien Ich denke nicht, zumindest für meinen Anwendungsfall. Ich habe eine DataTemplate, die auf den DataContext des MainWindow (meine Ansichtsmodellklasse) verweisen muss, um eine Liste der Optionen für ein Dropdown-Menü (aus einer Datenbank geladen) abzurufen. Die DataTemplate ist an ein Modellobjekt gebunden, das ebenfalls aus der Datenbank geladen wird, jedoch nur Zugriff auf die ausgewählte Option hat. Ich musste explizit festlegen Path=DataContext.Message, dass die Bindung funktioniert. Dies ist sinnvoll, da Sie relative Bindungen zu Breite / Höhe / etc. einer Kontrolle.
DrEsperanto
47

Bechir Bejaoui stellt in seinem Artikel hier die Anwendungsfälle der RelativeSources in WPF vor :

Die RelativeSource ist eine Markup-Erweiterung, die in bestimmten Bindungsfällen verwendet wird, wenn wir versuchen, eine Eigenschaft eines bestimmten Objekts an eine andere Eigenschaft des Objekts selbst zu binden, wenn wir versuchen, eine Eigenschaft eines Objekts an einen anderen seiner relativen Eltern zu binden. beim Binden eines Abhängigkeitseigenschaftswerts an ein Stück XAML im Falle einer benutzerdefinierten Steuerelemententwicklung und schließlich im Fall der Verwendung eines Differentials einer Reihe gebundener Daten. Alle diese Situationen werden als relative Quellmodi ausgedrückt. Ich werde alle diese Fälle einzeln aufdecken.

  1. Modus Selbst:

Stellen Sie sich diesen Fall vor, ein Rechteck, dessen Höhe immer gleich seiner Breite ist, sagen wir ein Quadrat. Wir können dies mit dem Elementnamen tun

<Rectangle Fill="Red" Name="rectangle" 
                Height="100" Stroke="Black" 
                Canvas.Top="100" Canvas.Left="100"
                Width="{Binding ElementName=rectangle,
                Path=Height}"/>

In diesem obigen Fall müssen wir jedoch den Namen des Bindungsobjekts angeben, nämlich das Rechteck. Mit der RelativeSource können wir den gleichen Zweck unterschiedlich erreichen

<Rectangle Fill="Red" Height="100" 
               Stroke="Black" 
               Width="{Binding RelativeSource={RelativeSource Self},
               Path=Height}"/>

In diesem Fall sind wir nicht verpflichtet, den Namen des Bindungsobjekts anzugeben, und die Breite entspricht immer der Höhe, wenn die Höhe geändert wird.

Wenn Sie die Breite auf die Hälfte der Höhe einstellen möchten, können Sie dies tun, indem Sie der Bindungs-Markup-Erweiterung einen Konverter hinzufügen. Stellen wir uns jetzt einen anderen Fall vor:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
               Path=Parent.ActualWidth}"/>

Der obige Fall wird verwendet, um eine bestimmte Eigenschaft eines bestimmten Elements an eine seiner direkten übergeordneten zu binden, da dieses Element eine Eigenschaft enthält, die als übergeordnet bezeichnet wird. Dies führt uns zu einem anderen relativen Quellmodus, dem FindAncestor.

  1. Modus FindAncestor

In diesem Fall wird eine Eigenschaft eines bestimmten Elements an einen seiner Elternteile, Of Corse, gebunden. Der Hauptunterschied zum obigen Fall besteht darin, dass Sie den Ahnen-Typ und den Ahnen-Rang in der Hierarchie bestimmen müssen, um die Eigenschaft zu verknüpfen. Versuchen Sie übrigens, mit diesem Stück XAML zu spielen

<Canvas Name="Parent0">
    <Border Name="Parent1"
             Width="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualWidth}"
             Height="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualHeight}">
        <Canvas Name="Parent2">
            <Border Name="Parent3"
            Width="{Binding RelativeSource={RelativeSource Self},
           Path=Parent.ActualWidth}"
           Height="{Binding RelativeSource={RelativeSource Self},
              Path=Parent.ActualHeight}">
               <Canvas Name="Parent4">
               <TextBlock FontSize="16" 
               Margin="5" Text="Display the name of the ancestor"/>
               <TextBlock FontSize="16" 
                 Margin="50" 
            Text="{Binding RelativeSource={RelativeSource  
                       FindAncestor,
                       AncestorType={x:Type Border}, 
                       AncestorLevel=2},Path=Name}" 
                       Width="200"/>
                </Canvas>
            </Border>
        </Canvas>
     </Border>
   </Canvas>

Die obige Situation besteht aus zwei TextBlock-Elementen, die in eine Reihe von Rahmen eingebettet sind, und Canvas-Elementen, die ihre hierarchischen Eltern darstellen. Der zweite TextBlock zeigt den Namen des angegebenen übergeordneten Elements auf der relativen Quellenebene an.

Versuchen Sie also, AncestorLevel = 2 in AncestorLevel = 1 zu ändern, und sehen Sie, was passiert. Versuchen Sie dann, den Typ des Vorfahren von AncestorType = Border in AncestorType = Canvas zu ändern, und sehen Sie, was passiert.

Der angezeigte Text ändert sich je nach Ahnenart und -stufe. Was passiert dann, wenn die Ahnenebene nicht für den Ahnen-Typ geeignet ist? Das ist eine gute Frage, ich weiß, dass Sie sie gleich stellen werden. Die Antwort ist, dass keine Ausnahmen ausgelöst werden und auf der TextBlock-Ebene nichts angezeigt wird.

  1. TemplatedParent

In diesem Modus kann eine bestimmte ControlTemplate-Eigenschaft mit einer Eigenschaft des Steuerelements verknüpft werden, auf das die ControlTemplate angewendet wird. Um das Problem hier gut zu verstehen, sehen Sie ein Beispiel unten

<Window.Resources>
<ControlTemplate x:Key="template">
        <Canvas>
            <Canvas.RenderTransform>
                <RotateTransform Angle="20"/>
                </Canvas.RenderTransform>
            <Ellipse Height="100" Width="150" 
                 Fill="{Binding 
            RelativeSource={RelativeSource TemplatedParent},
            Path=Background}">

              </Ellipse>
            <ContentPresenter Margin="35" 
                  Content="{Binding RelativeSource={RelativeSource  
                  TemplatedParent},Path=Content}"/>
        </Canvas>
    </ControlTemplate>
</Window.Resources>
    <Canvas Name="Parent0">
    <Button   Margin="50" 
              Template="{StaticResource template}" Height="0" 
              Canvas.Left="0" Canvas.Top="0" Width="0">
        <TextBlock FontSize="22">Click me</TextBlock>
    </Button>
 </Canvas>

Wenn ich die Eigenschaften eines bestimmten Steuerelements auf seine Steuerelementvorlage anwenden möchte, kann ich den TemplatedParent-Modus verwenden. Es gibt auch eine ähnliche zu dieser Markup-Erweiterung, nämlich die TemplateBinding, eine Art Kurzschrift der ersten, aber die TemplateBinding wird zur Kompilierungszeit im Gegensatz zum TemplatedParent ausgewertet, der unmittelbar nach der ersten Laufzeit ausgewertet wird. Wie Sie in der folgenden Abbildung sehen können, werden der Hintergrund und der Inhalt über die Schaltfläche auf die Steuerungsvorlage angewendet.

Cornel Marian
quelle
Sehr schöne Beispiele für mich, verwendet den Find Ancestor, um an einen Befehl im Datenkontext eines Elternteils zu binden ListView. Der Elternteil hat 2 weitere ListViewEbenen darunter. Das half mir verhindern Gabe von Daten in jedem nachfolgenden vm jedes ListView‚sDataTemplate
Caleb W.
34

In der WPF- RelativeSourceBindung werden drei propertieszu setzen:

1. Modus: Dies ist ein Modusenum , der vier Werte haben kann:

ein. PreviousData ( value=0): Weistpropertydem gebundenenden vorherigen Wert vonzu

b. TemplatedParent ( value=1): Dies wird verwendet, wenn Sietemplatesein Steuerelement definieren und an einen Wert / eine Eigenschaft des Steuerelements binden möchtencontrol.

Definieren Sie zum BeispielControlTemplate :

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

c. Selbst ( value=2): Wenn wir uns von einemselfoder einempropertySelbstbinden wollen.

Zum Beispiel: Senden geprüft Zustand checkboxwie CommandParameterbeim Einstellen der CommandaufCheckBox

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

d. FindAncestor ( value=3): Wenn von einem Elternteil binden wollencontrol inVisual Tree.

Zum Beispiel: Binden Sie ein checkboxin , recordswenn ein grid, wenn header checkboxgeprüft wird

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />

2. AncestorType: Wenn der Modus aktiviert ist, FindAncestordefinieren Sie, welcher Ahnen-Typ

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}

3. AncestorLevel: Wenn der ModusFindAncestordann die Ebene des Vorfahren ist (wenn zwei gleiche Elterntypen vorhanden sindvisual tree)

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}

Oben sind alle Anwendungsfälle für RelativeSource binding.

Hier ist ein Referenzlink .

Kylo Ren
quelle
2
Genial ... das hat bei mir funktioniert: <DataGridCheckBoxColumn Header = "Paid" Width = "35" Binding = "{Bindung RelativeSource = {RelativeSource Mode = FindAncestor, AncestorType = {x: Typfenster}}, Pfad = DataContext.SelectedBuyer.IsPaid , Mode = OneWay} "/> wo ich versucht habe, mich an den ausgewählten Käufer des übergeordneten Fensters zu binden. IsPaid-Eigenschaft
Michael K
21

Vergessen Sie nicht TemplatedParent:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

oder

{Binding RelativeSource={RelativeSource TemplatedParent}}
Bob King
quelle
16

Es ist erwähnenswert, dass für diejenigen, die über dieses Denken von Silverlight stolpern:

Silverlight bietet nur eine reduzierte Teilmenge dieser Befehle

Matthew Black
quelle
Ja, ich habe auch nach SL-Unterstützung gesucht. Stimmen
TravisWhidden
16

Ich habe eine Bibliothek erstellt, um die Bindungssyntax von WPF zu vereinfachen und die Verwendung von RelativeSource zu vereinfachen. Hier sind einige Beispiele. Vor:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
{Binding Path=Text, ElementName=MyTextBox}

Nach:

{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}

Hier ist ein Beispiel dafür, wie die Methodenbindung vereinfacht wird. Vor:

// C# code
private ICommand _saveCommand;
public ICommand SaveCommand {
 get {
  if (_saveCommand == null) {
   _saveCommand = new RelayCommand(x => this.SaveObject());
  }
  return _saveCommand;
 }
}

private void SaveObject() {
 // do something
}

// XAML
{Binding Path=SaveCommand}

Nach:

// C# code
private void SaveObject() {
 // do something
}

// XAML
{BindTo SaveObject()}

Sie finden die Bibliothek hier: http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

Beachten Sie im Beispiel 'BEFORE', das ich für die Methodenbindung verwende, dass der Code bereits optimiert wurde, indem RelayCommandder zuletzt überprüfte Code kein nativer Teil von WPF ist. Ohne das wäre das Beispiel "VORHER" noch länger gewesen.

Luis Perez
quelle
2
Diese Art von Handhalteübungen zeigt die Schwäche von XAML; viel zu kompliziert.
DudeNumber4
16

Einige nützliche Kleinigkeiten:

So geht's meistens im Code:

Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);

Ich habe dies größtenteils aus Binding Relative Source im Code Behind kopiert .

Außerdem ist die MSDN-Seite in Bezug auf Beispiele ziemlich gut: RelativeSource-Klasse

Nathan Cooper
quelle
5
Meine vage Erinnerung an WPF ist, dass das Binden von Code wahrscheinlich nicht das Beste ist.
Nathan Cooper
12

Ich habe gerade gepostet andere Lösung für den Zugriff auf den DataContext eines übergeordneten Elements in Silverlight veröffentlicht, die für mich funktioniert. Es verwendetBinding ElementName.

Juve
quelle
10

Ich habe nicht jede Antwort gelesen, aber ich möchte diese Informationen nur im Falle einer relativen Quellbefehlsbindung einer Schaltfläche hinzufügen.

Wenn Sie eine relative Quelle mit verwenden Mode=FindAncestor, muss die Bindung wie folgt aussehen:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

Wenn Sie Ihrem Pfad keinen DataContext hinzufügen, kann die Eigenschaft zur Ausführungszeit nicht abgerufen werden.

Kevin VDF
quelle
9

Dies ist ein Beispiel für die Verwendung dieses Musters, das bei leeren Datagrids für mich funktioniert hat.

<Style.Triggers>
    <DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
        <Setter Property="Background">
            <Setter.Value>
                <VisualBrush Stretch="None">
                    <VisualBrush.Visual>
                        <TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Setter.Value>
        </Setter>
    </DataTrigger>
</Style.Triggers>
Edd
quelle
6

Wenn ein Element nicht Teil des visuellen Baums ist, funktioniert RelativeSource niemals.

In diesem Fall müssen Sie eine andere Technik ausprobieren, die von Thomas Levesque entwickelt wurde.

Er hat die Lösung in seinem Blog unter [WPF] Wie man an Daten bindet, wenn der DataContext nicht vererbt wird . Und es funktioniert absolut hervorragend!

Für den unwahrscheinlichen Fall, dass sein Blog nicht verfügbar ist, enthält Anhang A eine Spiegelkopie seines Artikels .

Bitte hier nicht kommentieren, bitte direkt in seinem Blogbeitrag kommentieren .

Anhang A: Spiegel des Blogposts

Die DataContext-Eigenschaft in WPF ist äußerst praktisch, da sie automatisch von allen untergeordneten Elementen des Elements geerbt wird, dem Sie sie zuweisen. Daher müssen Sie es nicht für jedes Element, das Sie binden möchten, erneut festlegen. In einigen Fällen ist der DataContext jedoch nicht verfügbar: Dies geschieht für Elemente, die nicht Teil des visuellen oder logischen Baums sind. Es kann dann sehr schwierig sein, eine Eigenschaft an diese Elemente zu binden…

Lassen Sie uns anhand eines einfachen Beispiels veranschaulichen: Wir möchten eine Liste der Produkte in einem DataGrid anzeigen. Im Raster möchten wir in der Lage sein, die Spalte Preis basierend auf dem Wert einer ShowPrice-Eigenschaft, die vom ViewModel verfügbar gemacht wird, ein- oder auszublenden. Der naheliegende Ansatz besteht darin, die Sichtbarkeit der Spalte an die ShowPrice-Eigenschaft zu binden:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding ShowPrice,
                Converter={StaticResource visibilityConverter}}"/>

Leider hat das Ändern des Werts von ShowPrice keine Auswirkung, und die Spalte ist immer sichtbar. Warum? Wenn wir uns das Ausgabefenster in Visual Studio ansehen, sehen wir die folgende Zeile:

System.Windows.Data-Fehler: 2: Das maßgebliche FrameworkElement oder FrameworkContentElement für das Zielelement wurde nicht gefunden. BindingExpression: Path = ShowPrice; DataItem = null; Zielelement ist 'DataGridTextColumn' (HashCode = 32685253); Die Zieleigenschaft ist 'Sichtbarkeit' (Typ 'Sichtbarkeit').

Die Nachricht ist ziemlich kryptisch, aber die Bedeutung ist eigentlich recht einfach: WPF weiß nicht, welches FrameworkElement zum Abrufen des DataContext verwendet werden soll, da die Spalte nicht zum visuellen oder logischen Baum des DataGrid gehört.

Wir können versuchen, die Bindung zu optimieren, um das gewünschte Ergebnis zu erzielen, indem wir beispielsweise die RelativeSource auf das DataGrid selbst setzen:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding DataContext.ShowPrice,
                Converter={StaticResource visibilityConverter},
                RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

Oder wir können ein an ShowPrice gebundenes Kontrollkästchen hinzufügen und versuchen, die Spaltensichtbarkeit an die IsChecked-Eigenschaft zu binden, indem wir den Elementnamen angeben:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding IsChecked,
                Converter={StaticResource visibilityConverter},
                ElementName=chkShowPrice}"/>

Aber keine dieser Problemumgehungen scheint zu funktionieren. Wir erzielen immer das gleiche Ergebnis.

An diesem Punkt scheint es der einzig praktikable Ansatz zu sein, die Spaltensichtbarkeit in Code-Behind zu ändern, was wir normalerweise lieber vermeiden, wenn wir das MVVM-Muster verwenden… Aber ich werde nicht so schnell aufgeben, zumindest nicht während es andere Optionen gibt, die berücksichtigt werden müssen 😉

Die Lösung für unser Problem ist eigentlich recht einfach und nutzt die Freezable-Klasse. Der Hauptzweck dieser Klasse besteht darin, Objekte zu definieren, die einen veränderbaren und schreibgeschützten Status haben. Das Interessante in unserem Fall ist jedoch, dass Freezable-Objekte den DataContext erben können, auch wenn sie sich nicht im visuellen oder logischen Baum befinden. Ich kenne den genauen Mechanismus nicht, der dieses Verhalten ermöglicht, aber wir werden ihn nutzen, damit unsere Bindung funktioniert…

Die Idee ist, eine Klasse zu erstellen (ich habe sie aus Gründen, die sehr bald offensichtlich werden sollten, BindingProxy genannt), die Freezable erbt und eine Datenabhängigkeitseigenschaft deklariert:

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); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

Wir können dann eine Instanz dieser Klasse in den Ressourcen des DataGrid deklarieren und die Data-Eigenschaft an den aktuellen DataContext binden:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

Der letzte Schritt besteht darin, dieses BindingProxy-Objekt (mit StaticResource leicht zugänglich) als Quelle für die Bindung anzugeben:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding Data.ShowPrice,
                Converter={StaticResource visibilityConverter},
                Source={StaticResource proxy}}"/>

Beachten Sie, dass dem Bindungspfad "Daten" vorangestellt wurde, da der Pfad jetzt relativ zum BindingProxy-Objekt ist.

Die Bindung funktioniert jetzt ordnungsgemäß und die Spalte wird basierend auf der ShowPrice-Eigenschaft ordnungsgemäß angezeigt oder ausgeblendet.

Contango
quelle