2010-08-04 110 views
84

我有一個ListBox綁定到ViewModel上的子集合。從DataTemplate訪問父DataContext

<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"它的工作列表框項目在一個DataTemplate基於父視圖模型的屬性風格但是隻要父級用戶控件的datacontext是BindingListCollectionView。這是不可接受的,因爲用戶控件的其餘部分會自動綁定到BindingList上的CurrentItem的屬性。

如何在樣式中指定綁定表達式,以便它可以工作,而不管父數據上下文是集合視圖還是單個項目?

回答

135

我在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不是從模板項目中進行訪問,但因爲我沒有找到更好的解決辦法,我只是嘗試,並能正常工作。

+0

我在我的項目中有這個確切的代碼,但它泄漏ViewModels(終結者未調用,命令綁定似乎保留DataContext)。你能證實這個問題也存在嗎? – 2013-01-22 11:05:21

+0

@Juve這個作品,但它可能做到這一點,以便它會觸發實現相同模板的所有itemscontrol?名稱是獨一無二的,所以我們需要爲每個模板分別設置一個模板,除非我錯過了一些東西。 – Chris 2014-11-03 17:53:35

+1

@Juve無視我的最後一個,我通過使用relativeanceource和findancestor並通過祖先類型進行搜索(除非不按名稱搜索,所有這些都一樣)。在我的情況下,我重複使用ItemsControls每一個實現模板,所以我看起來像這樣:Command =「{Binding RelativeSource = {RelativeSource FindAncestor,AncestorType = {x:Type ItemsControl}},Path = DataContext.OpenDocumentBtnCommand}」 – Chris 2014-11-03 18:15:17

37

您可以使用RelativeSource尋父元素,這樣的 -

Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}" 

詳情請參閱this SO questionRelativeSource

+8

我不得不指定'Mode = FindAncestor'來使它工作,但是這樣可以工作,並且在MVVM場景中更好,因爲它避免了命名控制。 ''綁定=「{綁定路徑= DataContext.CurveSpeedMustBeSpecified, RelativeSource = {RelativeSource Mode = FindAncestor,AncestorType = {x:Type local:YourParentElementType}}}'' – Aphex 2011-08-12 15:37:20

+1

像魅力<3一樣工作,並且不必指定模式,.net 4.6.1 – user2475096 2017-12-01 22:00:04

16

我正在尋找如何做到在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正在使用DataTemplate,因此上層的DataContext不能直接訪問。然後,獲取父級的DC的解決方法是使用相對路徑並按ItemsControl類型進行篩選。

14

RelativeSource vs.的ElementName

這兩種方法可以實現相同的結果,

RelativeSrouce

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

此方法查找一個類型窗口的控制(在本示例中),在視覺樹,當它發現它時,你基本上可以使用Path=DataContext....訪問它的DataContext。這種方法的優點是你不需要被綁定到一個名稱上,它是一種動態的方式,但是對你的可視化樹所做的改變會影響這個方法並且可能會破壞它。

的ElementName

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

這種方法referes以雄厚的靜態Name所以只要你的範圍可以看到它,你fine.You應該堅持你的命名約定不打破這種方法當然。這種方法非常簡單,你只需要爲你的Window/UserControl指定一個Name="..."

雖然所有三種類型(RelativeSource, Source, ElementName)都能夠做同樣的事情,但根據以下MSDN文章,每個更好地用於他們自己的專業領域。

How to: Specify the Binding Source

查找每個簡要描述再加上頁面底部的更多詳細信息的鏈接一個表中。