ViewModelの子コレクションにバインドするListBox
があります。リストボックスアイテムは、親ViewModelのプロパティに基づいてデータテンプレートでスタイル設定されます。
<Style x:Key="curveSpeedNonConstantParameterCell">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified,
ElementName=someParentElementWithReferenceToRootDataContext}"
Value="True">
<Setter Property="Control.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
次の出力エラーが表示されます。
System.Windows.Data Error: 39 : BindingExpression path error:
'CurveSpeedMustBeSpecified' property not found on
'object' ''BindingListCollectionView' (HashCode=20467555)'.
BindingExpression:Path=DataContext.CurveSpeedMustBeSpecified;
DataItem='Grid' (Name='nonConstantCurveParametersGrid');
target element is 'TextBox' (Name='');
target property is 'NoTarget' (type 'Object')
そのため、バインディング式を"Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified"
に変更すると、親ユーザーコントロールのデータコンテキストがBindingListCollectionView
である限り機能します。これは、ユーザーコントロールの残りの部分がCurrentItem
上のBindingList
のプロパティに自動的にバインドするため、受け入れられません。
コレクション内の親データコンテキストまたは単一のアイテムに関係なく動作するように、スタイル内でバインディング式を指定するにはどうすればよいですか?
Silverlightの相対ソースに問題がありました。検索して読んだ後、追加のバインディングライブラリを使用しないと適切なソリューションが見つかりませんでした。しかし、ここでは親DataContextへのアクセスを取得するための別のアプローチは、データコンテキストを知っている要素を直接参照することです。独自の命名を尊重し、コンポーネント間でtemplates
/styles
を頻繁に再利用しない限り、Binding ElementName
を使用して非常にうまく機能します。
<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content={Binding MyLevel2Property}
Command={Binding ElementName=level1Lister,
Path=DataContext.MyLevel1Command}
CommandParameter={Binding MyLevel2Property}>
</Button>
<DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
これは、ボタンをStyle
/Template
に配置した場合にも機能します。
<Border.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Button Command={Binding ElementName=level1Lister,
Path=DataContext.MyLevel1Command}
CommandParameter={Binding MyLevel2Property}>
<ContentPresenter/>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Border.Resources>
<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding MyLevel2Property}"
Style="{StaticResource buttonStyle}"/>
<DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
最初は、テンプレート要素内から親要素のx:Names
にアクセスできないと思っていましたが、より良い解決策が見つからなかったので、試したところ、うまくいきました。
次のように、RelativeSource
を使用して親要素を見つけることができます-
Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified,
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}"
RelativeSource
の詳細については、 this SO question を参照してください。
RelativeSourcevs.ElementName
これら2つのアプローチは同じ結果を達成できますが、
RelativeSrouce
Binding="{Binding Path=DataContext.MyBindingProperty,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
このメソッドは、ビジュアルツリーで(この例では)型ウィンドウのコントロールを探し、それが見つかった場合、Path=DataContext....
を使用してDataContext
にアクセスできます。このメソッドの長所は、名前に縛られる必要がなく、一種の動的であるということですが、ビジュアルツリーに加えられた変更はこのメソッドに影響を与え、場合によっては破損する可能性があります。
ElementName
Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}
このメソッドは、静的なName
を参照します。したがって、スコープがそれを見ることができる限り、問題ありません。もちろん、このメソッドを壊さないように、命名規則に固執する必要があります。必要なのは、Window/UserControlにName="..."
を指定することです。
3つのタイプ(RelativeSource, Source, ElementName
)はすべて同じことを実行できますが、次のMSDNの記事によると、それぞれの専門分野で使用する方が適切です。
ページの下部にある表で、それぞれの簡単な説明と詳細へのリンクを見つけます。
私はWPFで似たようなことをする方法を探していましたが、この解決策を得ました:
<ItemsControl ItemsSource="{Binding MyItems,Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
Content="{Binding}"
Command="{Binding Path=DataContext.CustomCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ItemsControl}} }"
CommandParameter="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
これが他の人に役立つことを願っています。 ItemsControlsに自動的に設定されるデータコンテキストがあり、このデータコンテキストには2つのプロパティがあります:MyItems
-これはコレクションです-、および1つのコマンド 'CustomCommand'。 ItemTemplate
はDataTemplate
を使用しているため、上位レベルのDataContext
は直接アクセスできません。次に、親のDCを取得する回避策は、相対パスを使用し、ItemsControl
タイプでフィルター処理します。
問題は、DataTemplateが適用される要素の一部ではないことです。
つまり、テンプレートにバインドすると、コンテキストを持たないものにバインドすることになります。
ただし、テンプレート内に要素を配置すると、その要素が親に適用されるとコンテキストが取得され、バインディングが機能します
これは機能しません
<DataTemplate >
<DataTemplate.Resources>
<CollectionViewSource x:Key="projects" Source="{Binding Projects}" >
しかし、これは完全に動作します
<DataTemplate >
<GroupBox Header="Projects">
<GroupBox.Resources>
<CollectionViewSource x:Key="projects" Source="{Binding Projects}" >
データテンプレートが適用された後、グループボックスは親に配置され、そのコンテキストにアクセスできるため
そのため、テンプレートからスタイルを削除し、テンプレート内の要素に移動するだけです
注 itemscontrolのコンテキストはコントロールではなくアイテムです。つまり、ComboBox自体ではなくComboBoxのComboBoxItemです。この場合、代わりにコントロールのItemContainerStyleを使用する必要があります。