Wie verwende ich WPF-Bindungen mit RelativeSource?
-
01-07-2019 - |
Frage
Wie verwende ich RelativeSource
mit WPF-Bindungen und was sind die verschiedenen Anwendungsfälle?
Lösung
Wenn Sie auf das Objekt auf eine andere Eigenschaft binden:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
Wenn Sie eine Eigenschaft auf einem Vorfahren zu bekommen:
{Binding Path=PathToProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Wenn Sie eine Immobilie auf den Templat-Eltern zu bekommen (so Sie 2-Wege-Bindungen in einem Control tun können)
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
oder kürzer (dies funktioniert nur für OneWay Bindungen):
{TemplateBinding Path=PathToProperty}
Andere Tipps
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
Das Standardattribut von RelativeSource
ist der Mode
Eigentum.Ein vollständiger Satz gültiger Werte ist hier angegeben (von MSDN):
VorherigeDaten Ermöglicht Ihnen, das vorherige Datenelement (nicht das Steuerelement, das das Datenelement enthält) in der Liste der angezeigten Datenelemente zu binden.
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 ist nur anwendbar, wenn sich die Bindung innerhalb einer Vorlage befindet.
Selbst Bezieht sich auf das Element, für das Sie die Bindung festlegen, und ermöglicht 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 eine Bindung an einen Vorfahren eines bestimmten Typs oder seiner Unterklassen herzustellen.Dies ist der Modus, den Sie verwenden, wenn Sie AncestorType und/oder AncestorLevel angeben möchten.
Hier ist eine visuelle Erklärung im Rahmen einer MVVM Architektur:
Stellen Sie sich vor, diesen Fall, ein Rechteck, das wir wollen, dass seine Höhe zu seiner Breite immer gleich ist, lassen Sie ein Quadrat sagen. Wir können dies mit dem Elementnamen mit
<Rectangle Fill="Red" Name="rectangle"
Height="100" Stroke="Black"
Canvas.Top="100" Canvas.Left="100"
Width="{Binding ElementName=rectangle,
Path=Height}"/>
Aber in diesem obigen Fall sind wir verpflichtet, den Namen des Bindungsobjekts anzuzeigen, nämlich das Rechteck. Wir können den gleichen Zweck erreichen anders die Relative mit
<Rectangle Fill="Red" Height="100"
Stroke="Black"
Width="{Binding RelativeSource={RelativeSource Self},
Path=Height}"/>
Für diesen Fall sind wir nicht verpflichtet, den Namen des Bindungsobjekts zu erwähnen und die Breite wird immer gleich die Höhe, wenn die Höhe geändert wird.
Wenn Sie die Breite zu sein, die Hälfte der Höhe auf Parameter, dann können Sie dies tun, indem einem Konverter an die Binding-Markup-Erweiterung hinzufügen. Lassen Sie uns einen anderen Fall vorstellen, jetzt:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self},
Path=Parent.ActualWidth}"/>
Der obige Fall ist eine bestimmte Eigenschaft eines bestimmten Elements zu einer seiner direkten Mutter diejenigen zu binden verwendet, wie dieses Element eine Eigenschaft enthält, die Eltern genannt wird. Dies führt uns zu einem anderen relativen Quelle-Modus, der der FindAncestor ist.
Bechir Bejaoui macht die Anwendungsfälle der RelativeSources in WPF in seinem Artikel hier :
Die Relative ist eine Markup-Erweiterung, die insbesondere verwendet wird, Bindung Fällen, wenn wir versuchen, eine Eigenschaft eines bestimmten Objekts zu binden eine andere Eigenschaft des Objekts selbst, wenn wir versuchen, eine Eigenschaft zu binden ein Objekt auf einen anderen seinen relativ Eltern, wenn Bindung a Abhängigkeitseigenschaft Wert auf ein Stück XAML bei Custom Controls Entwicklung und schließlich bei einem Differential einer Serie unter Verwendung von ein gebundenen Daten. All diese Situationen sind als relative Quelle ausgedrückt Modi. Ich werde all diese Fälle einer nach dem anderen aus.
- Modus Selbst:
Stellen Sie sich vor, diesen Fall, ein Rechteck, das wir, dass seine Höhe wollen, ist immer gleich seiner Breite, lassen Sie uns ein Quadrat sagen. Wir können dies tun, die mit Elementname
<Rectangle Fill="Red" Name="rectangle" Height="100" Stroke="Black" Canvas.Top="100" Canvas.Left="100" Width="{Binding ElementName=rectangle, Path=Height}"/>
Aber in diesem obigen Fall sind wir verpflichtet, den Namen der anzuzeigen Bindungsobjekt, nämlich das Rechteck. Wir können den gleichen Zweck erreichen anders mit der Relative
<Rectangle Fill="Red" Height="100" Stroke="Black" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
Für diesen Fall sind wir nicht verpflichtet, den Namen der Bindung zu erwähnen Objekt und die Breite wird auf die Höhe immer gleich sein, wenn die Höhe wird geändert.
Wenn Sie die Breite auf Parameter die Hälfte der Höhe zu sein, dann Sie können dies durch einen Konverter an die Binding-Markup-Erweiterung hinzufügen. Lassen Sie uns einen anderen Fall vorstellen, jetzt:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
Der obige Fall ist eine bestimmte Eigenschaft eines bestimmten Elements zu binden verwendet einer seiner direkten Mutter one als dieses Element hält eine Eigenschaft, die ist genannt Parent. Dies führt uns zu einem anderen relativen Quelle-Modus, ist der FindAncestor ein.
- Modus FindAncestor
In diesem Fall ist eine Eigenschaft eines bestimmten Elements wird zu einem seiner gebunden werden Eltern, Corse. Der wesentliche Unterschied zu dem obigen Fall ist die Tatsache, das, es ist an Ihnen, den Vorfahren Typ und den Vorfahren zu bestimmen Rang in der Hierarchie die Eigenschaft zu binden. Durch die Art und Weise versuchen, mit zu spielen dieses Stück XAML
<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 oben beschriebene Situation ist von zwei TextBlock- Elemente diejenigen eingebettet sind innerhalb einer Reihe von Grenzen und Segeltuchelementen repräsentieren diejenigen ihre hierarchisch Eltern. Der zweite Textblock wird angezeigt, den Namen der gegebene Elternteil in der relativen Quellebene.
versuchen So AncestorLevel = 2 bis AncestorLevel = 1 und sehen Sie zu ändern, was das passiert. Dann versuchen, die Art des Vorfahren zu ändern aus AncestorType = Border zu AncestorType = Leinwand und sehen, was passiert.
Der angezeigte Text wird entsprechend den Vorfahren Typen verändern und Niveau. Was ist dann geschehen, wenn die Vorfahre Ebene nicht geeignet das ist Vorfahrtyp? Das ist eine gute Frage, ich weiß, dass Sie über zu Frag es. Die Antwort ist, werden keine Ausnahmen geworfen und Nichtigkeiten werden wird wird auf der TextBlock- Ebene angezeigt.
- TemplatedParent
Dieser Modus ermöglicht eine gegebene Eigenschaft Control auf eine Eigenschaft binden die Steuerung, die die Control angewandt wird. Um gut versteht das Problem hier ist 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 Kontrolle ihrer Kontrolle anwenden möchten Vorlage dann kann ich den TemplatedParent-Modus verwenden. Da ist auch ein ähnlich einer zu dieser Auszeichnungs Erweiterung, die das Template ist Das ist eine Art kurzer Hand des ersten, aber die Template wird bei der Kompilierung im Gegensatz der evaluierten TemplatedParent die Gerade nach der ersten Laufzeit ausgewertet. Wie Sie können in der unten Figur, den Hintergrund und den Inhalt bemerke werden innerhalb der Taste an die Steuer Vorlage angewendet.
In WPF RelativeSource
Bindung macht drei properties
zu setzen:
1. Modus: Dies ist ein enum
, die vier Werte haben könnte:
ein. PreviousData (
value=0
): Es weist den vorherigen Wert desproperty
zu das gebundene einb. TemplatedParent (
value=1
): Dies wird verwendet, wenn die Definition dertemplates
von jede Kontrolle und möchte auf einen Wert / Vermögen descontrol
binden.Beispiel: definieren
ControlTemplate
:
<ControlTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
c. Selbst (
value=2
.): Wenn wir von einemself
oderproperty
Selbst binden möchtenBeispiel: Senden Zustand
checkbox
alsCommandParameter
geprüft, während dieCommand
aufCheckBox
Einstellung
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d. FindAncestor (
value=3
): Wenn wollen von einem Elternteilcontrol
binden inVisual Tree
.Beispiel: Binden eines
checkbox
inrecords
wenn eingrid
, wennheader
checkbox
wird geprüft
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. AncestorType: , wenn Modus FindAncestor
dann definieren, welche Art von Vorfahren
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. AncestorLevel: , wenn Modus FindAncestor
dann auf welcher Ebene der Vorfahren (wenn es zwei gleiche Art von Eltern in visual tree
)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
Über alle Anwendungsfälle für
RelativeSource binding
.
Vergessen Sie nicht TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
oder
{Binding RelativeSource={RelativeSource TemplatedParent}}
Es ist bemerkenswert, dass diejenigen, für die über dieses Denken von Silverlight zu stolpern:
Silverlight bietet eine reduzierte Teilmenge nur, diese Befehle
habe ich eine Bibliothek, die die Bindungssyntax von WPF zu vereinfachen, einschließlich macht es einfacher Relative zu verwenden. Hier sind einige Beispiele. Vorher:
{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 Methodenbindung vereinfacht. Vorher:
// 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 können die Bibliothek finden Sie hier: http://www.simplygoodcode.com/2012/ 08 / einfacherer-wpf-binding.html
Hinweis in dem ‚vor‘ Beispiel, das ich für Methode verwenden, die Code-Bindung bereits durch die Verwendung RelayCommand
optimiert wurde, die ich überprüfte zuletzt ist kein nativer Teil von WPF. Ohne dass das ‚vor‘ Beispiel wäre noch länger gewesen.
Einige nützliche Bits und Stücke:
Hier ist, wie es tun vor allem in dem Code ein:
Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);
ich dies weitgehend kopiert von Bindung Relative Quelle in Code Hinter .
Auch die MSDN-Seite ziemlich gut so weit wie Beispiele gehen: Relative Klasse
Ich habe gerade geschrieben eine andere Lösung für das Datacontext eines übergeordneten Elements in Silverlight zugreifen, die für mich funktioniert. Es verwendet Binding ElementName
.
Ich habe nicht jede Antwort lesen, aber ich will nur diese Informationen im Fall eines relativen Quelle Befehl einer Taste Bindung hinzuzufügen.
Wenn Sie eine relative Quelle mit Mode=FindAncestor
verwenden, kann die Bindung muss wie:
Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"
Wenn Sie nicht über Datacontext in Ihrem Pfad hinzufügen, zum Zeitpunkt der Ausführung es die Eigenschaft nicht abrufen kann.
Dies ist ein Beispiel für die Verwendung dieses Musters, das für mich auf leere Datagrids gearbeitet.
<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>
Wenn ein Element nicht Teil der visuellen Struktur ist, dann wird Relative nie funktionieren.
In diesem Fall müssen Sie eine andere Technik, um zu versuchen, Pionierarbeit von Thomas Levesque.
Er hat die Lösung auf seinem Blog unter sein Artikel .
Bitte nicht kommentieren hier, bitte Kommentar direkt auf seiner Blog-Post .
Anhang A: Spiegel der Blog-Post
Die Datacontext-Eigenschaft in WPF ist äußerst praktisch, weil sie automatisch von allen Kindern des Elements geerbt, in dem Sie es zuweisen; deshalb brauchen Sie nicht erneut eingestellt auf jedes Element, das Sie binden möchten. In einigen Fällen jedoch, die Datacontext nicht zugänglich sind: geschieht es für Elemente, die nicht Teil der visuellen oder logischen Struktur sind. Es kann sehr schwierig sein, dann eine Eigenschaft auf die Elemente zu binden ...
Lassen Sie uns mit einem einfachen Beispiel verdeutlichen: Wir haben eine Liste von Produkten in einem Datagrid angezeigt werden soll. Im Netz wollen wir die Preisspalte angezeigt oder verborgen werden zu können, basierend auf dem Wert eines ShowPrice Eigenschaft des Ansichtsmodell ausgesetzt. Der offensichtliche Ansatz ist es, die Sichtbarkeit der Säule an die ShowPrice Eigenschaft zu binden:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding ShowPrice,
Converter={StaticResource visibilityConverter}}"/>
Leider ist der Wert von ShowPrice Veränderung hat keine Wirkung, und die Spalte ist immer sichtbar ... warum? Wenn wir an das Ausgabefenster in Visual Studio suchen, bemerken wir die folgende Zeile:
System.Windows.Data Fehler: 2: Kann nicht regeln Framework oder Framework für Zielelement finden. Binding: Path = ShowPrice; DataItem = null; Zielelement ist ‚Datagridtextcolumn‘ (HashCode = 32.685.253); Zieleigenschaft ist ‚Sichtbarkeit‘ (Typ ‚Sichtbarkeit‘)
Die Botschaft ist ziemlich kryptisch, aber die Bedeutung ist eigentlich ganz einfach: WPF nicht weiß, welche Framework die Datacontext zu verwenden zu bekommen, weil die Spalte auf den visuellen oder logischen Baum gehört nicht des Datagrid
Wir können versuchen, die Bindung zu zwicken das gewünschten Ergebnis, zum Beispiel zu erhalten, indem die Relative an den Datagrid Einstellung selbst:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding DataContext.ShowPrice,
Converter={StaticResource visibilityConverter},
RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
Oder wir können eine CheckBox in dem ShowPrice gebunden ist, und versuchen, die Spalte Sichtbarkeit auf die IsChecked Eigenschaft zu binden, indem das Element Namen angeben:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding IsChecked,
Converter={StaticResource visibilityConverter},
ElementName=chkShowPrice}"/>
Aber keine dieser Abhilfen scheint, arbeiten wir das gleiche Ergebnis immer bekommen ...
An diesem Punkt scheint es, dass der einzig gangbare Weg wäre, die Spalte Sichtbarkeit in Code-behind zu ändern, was wir bevorzugen in der Regel zu vermeiden, wenn das MVVM Muster mit ... Aber ich werde nicht so schnell aufgeben, zumindest nicht, während es gibt auch andere Optionen