回答:
オブジェクトの別のプロパティにバインドする場合:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
祖先のプロパティを取得したい場合:
{Binding Path=PathToProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
テンプレート化された親のプロパティを取得する場合(したがって、ControlTemplateで双方向のバインディングを実行できます)
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
または、より短い(これはOneWayバインディングでのみ機能します):
{TemplateBinding Path=PathToProperty}
AncestorType
。
FindAncestor
前AncestorType
にを省略すると、「RelativeSourceがFindAncestorモードではありません」というエラーが表示されます。(VS2013では、コミュニティバージョン)
{Binding Path=DataContext.SomeProperty, RelativeSource=...
。これは、DataTemplate内で親のDataContextにバインドしようとしたときに、初心者としては少し予想外でした。
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
のデフォルト属性RelativeSource
はMode
プロパティです。有効な値の完全なセットをここに示します(MSDNから):
PreviousData表示されているデータ項目のリスト内の前のデータ項目(データ項目を含むコントロールではない)をバインドできます。
TemplatedParentテンプレート(データバインド要素が存在する)が適用される要素を参照します。これは、TemplateBindingExtensionの設定に似ており、Bindingがテンプレート内にある場合にのみ適用されます。
Selfバインディングを設定する要素を参照し、その要素のあるプロパティを同じ要素の別のプロパティにバインドできるようにします。
FindAncestorデータバインドされた要素の親チェーン内の祖先を参照します。これを使用して、特定のタイプまたはそのサブクラスの祖先にバインドできます。これは、AncestorTypeまたはAncestorLevel、あるいはその両方を指定する場合に使用するモードです。
以下は、MVVMアーキテクチャのコンテキストでのより視覚的な説明です。
{Binding Message}
もう少し単純です...)
Path=DataContext.Message
バインディングを機能させるには、明示的に設定する必要がありました。これは、width / height /などへの相対バインディングを実行できることを考えると、理にかなっています。コントロールの。
Bechir Bejaouiは、WPFでのRelativeSourcesの使用例を彼の記事で公開しています。
RelativeSourceは、特定のオブジェクトのプロパティをオブジェクト自体の別のプロパティにバインドしようとするとき、オブジェクトのプロパティをその親の別の親にバインドしようとするときに、特定のバインドの場合に使用されるマークアップ拡張機能です。カスタムコントロール開発の場合、および最後に一連のバインドされたデータの差分を使用する場合に、依存関係プロパティ値をXAMLの一部にバインドするとき。これらの状況はすべて、相対ソースモードとして表されます。これらのケースを1つずつ公開します。
- モードセルフ:
この場合を想像してみてください。長方形の高さが常に幅と等しいことを望んでいます。正方形だとしましょう。要素名を使用してこれを行うことができます
<Rectangle Fill="Red" Name="rectangle" Height="100" Stroke="Black" Canvas.Top="100" Canvas.Left="100" Width="{Binding ElementName=rectangle, Path=Height}"/>
しかし、上記の場合、バインディングオブジェクトの名前、つまり長方形を示す必要があります。RelativeSourceを使用して、同じ目的に異なる方法で到達できます。
<Rectangle Fill="Red" Height="100" Stroke="Black" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
その場合、バインディングオブジェクトの名前について言及する義務はありません。また、高さが変更されるたびに、幅は常に高さに等しくなります。
Widthを高さの半分になるようにパラメーター化したい場合は、バインディングマークアップ拡張機能にコンバーターを追加することでこれを行うことができます。ここで別のケースを想像してみましょう:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
上記のケースは、特定の要素の特定のプロパティを直接の親プロパティの1つに関連付けるために使用されます。この要素は、Parentと呼ばれるプロパティを保持するためです。これにより、FindAncestorである別の相対ソースモードにつながります。
- モードFindAncestor
この場合、特定の要素のプロパティはその親の1つであるOf Corseに関連付けられます。上記のケースとの主な違いは、プロパティを結び付けるために階層内の祖先のタイプと祖先のランクを決定するのはあなた次第であるという事実です。ちなみに、この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>
上記の状況は、一連のボーダー内に埋め込まれた2つのTextBlock要素と、階層的な親を表すcanvas要素の状況です。2番目のTextBlockは、相対ソースレベルで指定された親の名前を表示します。
したがって、AncestorLevel = 2をAncestorLevel = 1に変更して、何が起こるかを確認してください。次に、祖先のタイプをAncestorType = BorderからAncestorType = Canvasに変更して、何が起こるかを確認してください。
表示されるテキストは、祖先のタイプとレベルに応じて変化します。次に、祖先レベルが祖先のタイプに適さない場合はどうなりますか?これは良い質問です。あなたが尋ねようとしているのはわかっています。応答は例外ではなく、TextBlockレベルでは何も表示されません。
- TemplatedParent
このモードでは、特定のControlTemplateプロパティを、ControlTemplateが適用されているコントロールのプロパティに関連付けることができます。ここで問題をよく理解するには、以下の例をご覧ください
<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>
特定のコントロールのプロパティをそのコントロールテンプレートに適用する場合は、TemplatedParentモードを使用できます。このマークアップ拡張機能に似たものもあります。これは、最初のものの一種であるTemplateBindingですが、TemplateBindingは、最初の実行直後に評価されるTemplatedParentとは対照的に、コンパイル時に評価されます。下の図で説明したように、背景とコンテンツはボタン内からコントロールテンプレートに適用されます。
ListView
。親には、そのListView
下にさらに2つのレベルがあります。これは私が各後続の各VMにデータを渡す防ぐ助けListView
さんDataTemplate
WPF RelativeSource
バインディングでは3つを公開しますproperties
セットをします。
1.モード:これはenum
4つの値を持つ可能性があります。
a。PreviousData(
value=0
):property
バインドされた値に以前の値を割り当てますb。TemplatedParent(
value=1
): これは、templates
、任意のコントロールのをし、の値/プロパティにバインドするcontrol
ます。たとえば、次のように定義します
ControlTemplate
。
<ControlTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
c。Self(
value=2
):からバインドしたいときself
またはaproperty
自己の。例:オンに設定
checkbox
しCommandParameter
ながら、チェック状態を送信Command
CheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d。FindAncestor(
value=3
):親からバインドしたいですcontrol
でVisual Tree
。例: if がチェックさ
checkbox
れているrecords
場合にバインドしますgrid
header
checkbox
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. AncestorType: モードがFindAncestor
祖先のタイプを定義するとき
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. AncestorLevel: モードがFindAncestor
祖先のレベルである場合(に同じタイプの親が2つある場合visual tree
)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
上記はすべてのユースケースです
RelativeSource binding
。
こちらが参考リンクです。
Silverlightのこの考え方に出くわした人にとって、注目に値します。
Silverlightは、これらのコマンドの一部のみを提供します
ライブラリを作成して、WPFのバインディング構文を簡素化し、RelativeSourceを使いやすくしました。下記は用例です。前:
{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}
後:
{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}
メソッドバインディングを簡略化する方法の例を次に示します。前:
// 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}
後:
// C# code
private void SaveObject() {
// do something
}
// XAML
{BindTo SaveObject()}
ライブラリはここにあります:http : //www.simplygoodcode.com/2012/08/simpler-wpf-binding.html
メソッドバインディングに使用する 'BEFORE'の例でRelayCommand
は、最後にチェックしたWPFのネイティブ部分ではないコードを使用してコードが既に最適化されていることに注意してください。それがなければ、「BEFORE」の例はさらに長くなるでしょう。
いくつかの便利な小片:
主にコードでこれを行う方法は次のとおりです。
Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);
コードビハインドのバインディング相対ソースからこれを大幅にコピーしました。
また、例としては、MSDNページは非常に優れています。RelativeSourceクラス
これは、空のデータグリッドで機能するこのパターンの使用例です。
<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>
要素がビジュアルツリーの一部でない場合、RelativeSourceは機能しません。
この場合、Thomas Levesqueが開拓した別のテクニックを試す必要があります。
彼のブログには、[WPF] DataContextが継承されていない場合にデータにバインドする方法のソリューションがあります。そして、それは絶対に見事に機能します!
万が一、彼のブログがダウンした場合、付録Aには、 彼の記事の。
ここにはコメントしないでください。 。彼のブログ投稿に直接コメントして。
WPFのDataContextプロパティは、割り当てた要素のすべての子によって自動的に継承されるため、非常に便利です。したがって、バインドする各要素に再度設定する必要はありません。ただし、DataContextにアクセスできない場合もあります。これは、ビジュアルツリーまたは論理ツリーの一部ではない要素で発生します。これらの要素にプロパティをバインドするのは非常に難しい場合があります…
簡単な例で説明しましょう。DataGridに製品のリストを表示します。グリッドで、ViewModelによって公開されたShowPriceプロパティの値に基づいて、Price列を表示または非表示にできるようにします。明らかなアプローチは、列のVisibilityをShowPriceプロパティにバインドすることです。
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding ShowPrice,
Converter={StaticResource visibilityConverter}}"/>
残念ながら、ShowPriceの値を変更しても効果はなく、列は常に表示されます。なぜですか?Visual Studioの[出力]ウィンドウを見ると、次の行がわかります。
System.Windows.Dataエラー:2:ターゲット要素の管理FrameworkElementまたはFrameworkContentElementが見つかりません。BindingExpression:Path = ShowPrice; DataItem = null; ターゲット要素は 'DataGridTextColumn'(HashCode = 32685253);です。ターゲットプロパティは 'Visibility'(タイプ 'Visibility')です
メッセージはかなり不可解ですが、意味は実際には非常に簡単です。列がDataGridの視覚的または論理的なツリーに属していないため、WPFはDataContextを取得するために使用するFrameworkElementを知りません。
たとえば、RelativeSourceをDataGrid自体に設定することで、バインディングを微調整して目的の結果を得ることができます。
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding DataContext.ShowPrice,
Converter={StaticResource visibilityConverter},
RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
または、ShowPriceにバインドされたCheckBoxを追加し、要素名を指定して列の可視性をIsCheckedプロパティにバインドしようとすることができます。
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding IsChecked,
Converter={StaticResource visibilityConverter},
ElementName=chkShowPrice}"/>
しかし、これらの回避策はどれも機能していないようです。常に同じ結果が得られます…
この時点で、実行可能な唯一のアプローチは、コードビハインドで列の可視性を変更することであると思われます。MVVMパターンを使用する場合は、通常、これを避けたいと思います…しかし、少なくともそれほどあきらめたくないでしょう考慮すべき他のオプションがありますが😉
私たちの問題の解決策は実際には非常に単純で、Freezableクラスを利用しています。このクラスの主な目的は、変更可能で読み取り専用の状態のオブジェクトを定義することですが、この場合の興味深い機能は、Freezableオブジェクトがビジュアルツリーまたは論理ツリーにない場合でもDataContextを継承できることです。この動作を可能にする正確なメカニズムはわかりませんが、バインディングを機能させるためにそれを利用します…
アイデアは、Freezableを継承し、Data依存プロパティを宣言するクラス(私はすぐに明らかになるはずの理由でBindingProxyと呼びました)を作成することです。
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));
}
次に、DataGridのリソースでこのクラスのインスタンスを宣言し、Dataプロパティを現在のDataContextにバインドします。
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
最後のステップは、このBindingProxyオブジェクト(StaticResourceで簡単にアクセス可能)をバインディングのソースとして指定することです。
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding Data.ShowPrice,
Converter={StaticResource visibilityConverter},
Source={StaticResource proxy}}"/>
パスがBindingProxyオブジェクトに相対するようになったため、バインディングパスの前に「Data」が付いていることに注意してください。
バインディングが正しく機能し、ShowPriceプロパティに基づいて列が適切に表示または非表示になります。