[wpf] 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}}}"

자세한 내용은 이 SO 질문 을 참조하십시오.RelativeSource .


답변

RelativeSourceElementName

이 두 가지 접근 방식은 동일한 결과를 얻을 수 있습니다.

RelativeSrouce

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

이 방법은 시각적 트리에서 (이 예에서는) 타입 윈도우의 컨트롤을 찾고 그것을 발견하면 당신은 기본적으로는 접근 할 수 DataContext를 사용하여 Path=DataContext..... 이 방법의 장점은 이름에 묶일 필요가 없으며 일종의 동적이지만 시각적 트리를 변경하면이 방법에 영향을 미치고 깨질 수 있다는 것입니다.

ElementName

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

이 방법은 솔리드 정적 Name 은 스코프가 볼 수 을 참조하므로 괜찮습니다. 물론이 방법을 위반하지 않도록 명명 규칙을 고수해야합니다. 접근 방식은 간단하며 필요한 것은 지정하는 것입니다. ㅏName="..." 창 / UserControl에 대한 .

세 가지 유형 모두 (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에 자동으로 설정된 데이터 컨텍스트가 있으며이 데이터 컨텍스트에는 두 가지 속성이 있습니다.MyItems 있으며이 컬렉션 인-와 하나의 명령 ‘CustomCommand’라는 이 있습니다. (가)의 때문에 ItemTemplate을 사용하고 DataTemplateDataContext상위 레벨의 직접 액세스 할 수 없습니다. 그런 다음 부모의 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에 대한 컨텍스트가 콤보 상자의 항목이 아닌 컨트롤 즉, ComboBoxItem인지하지 당신이 컨트롤 대신 ItemContainerStyle을 사용해야하는 경우에는 콤보 자체


답변

예, 다음을 사용하여 해결할 수 있습니다. ElementName=Something Juve가 제안한대로을 .

그러나!

이러한 종류의 바인딩을 사용하는 자식 요소가 부모 컨트롤에서 지정한 것과 동일한 요소 이름을 사용하는 사용자 컨트롤 인 경우 바인딩은 잘못된 개체로 이동합니다 !!

이 게시물이 해결책이 아니라는 것을 알고 있지만 가능한 런타임 버그이므로 바인딩에서 ElementName을 사용하는 모든 사람이 이것을 알아야한다고 생각했습니다.

<UserControl x:Class="MyNiceControl"
             x:Name="TheSameName">
   the content ...
</UserControl>

<UserControl x:Class="AnotherUserControl">
        <ListView x:Name="TheSameName">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <MyNiceControl Width="{Binding DataContext.Width, ElementName=TheSameName}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
</UserControl>


답변