code

DataTemplate에서 부모 DataContext에 액세스

codestyles 2020. 8. 20. 18:46
반응형

DataTemplate에서 부모 DataContext에 액세스


나는이 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대한 액세스 권한을 얻는 또 다른 방법 이 있습니다. 그것은 사용 Binding ElementName하고 당신이 당신의 자신의 이름을 존중하고 무거운 재사용이없는만큼, 아주 잘 작동 templates/ styles구성 요소에서를 :

<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}}}"

See this SO question for more details about RelativeSource.


RelativeSource vs. ElementName

These two approaches can achieve the same result,

RelativeSrouce

Binding="{Binding Path=DataContext.MyBindingProperty, 
          RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

This method looks for a control of a type Window (in this example) in the visual tree and when it finds it you basically can access it's DataContext using the Path=DataContext..... The Pros about this method is that you don't need to be tied to a name and it's kind of dynamic, however, changes made to your visual tree can affect this method and possibly break it.

ElementName

Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow}

This method referes to a solid static Name so as long as your scope can see it, you're fine.You should be sticking to your naming convention not to break this method of course.The approach is qute simple and all you need is to specify a Name="..." for your Window/UserControl.

Although all three types (RelativeSource, Source, ElementName) are capable of doing the same thing, but according to the following MSDN article, each one better be used in their own area of specialty.

How to: Specify the Binding Source

Find the brief description of each plus a link to a more details one in the table on the bottom of the page.


I was searching how to do something similar in WPF and I got this solution:

<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>

I hope this works for somebody else. I have a data context which is set automatically to the ItemsControls, and this data context has two properties: MyItems -which is a collection-, and one command 'CustomCommand'. Because of the ItemTemplate is using a DataTemplate, the DataContext of upper levels is not directly accessible. Then the workaround to get the DC of the parent is use a relative path and filter by ItemsControl type.


the issue is that a DataTemplate isn't part of an element its applied to it.

this means if you bind to the template you're binding to something that has no context.

however if you put a element inside the template then when that element is applied to the parent it gains a context and the binding then works

so this will not work

<DataTemplate >
    <DataTemplate.Resources>
        <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

but this works perfectly

<DataTemplate >
    <GroupBox Header="Projects">
        <GroupBox.Resources>
            <CollectionViewSource x:Key="projects" Source="{Binding Projects}" >

because after the datatemplate is applied the groupbox is placed in the parent and will have access to its Context

so all you have to do is remove the style from the template and move it into an element in the template

note that the context for a itemscontrol is the item not the control ie ComboBoxItem for ComboBox not the ComboBox itself in which case you should use the controls ItemContainerStyle instead

참고URL : https://stackoverflow.com/questions/3404707/access-parent-datacontext-from-datatemplate

반응형