2010-08-04 8 views
84

Ich habe eine ListBox, die an eine untergeordnete Sammlung auf einem ViewModel bindet. Die Listbox Elemente werden in einem Datatemplate basierend auf einem Grundstück auf der übergeordneten Ansichtsmodell gestylt:Zugriff Eltern DataContext von DataTemplate

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

ich die folgende Ausgabe Fehlermeldung erhalten:

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

Also, wenn ich die die Bindung Ausdruck zu "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified" es funktioniert ändern , aber nur so lange, wie der Datenkontext des übergeordneten Benutzersteuerelements BindingListCollectionView ist. Dies ist nicht akzeptabel, da der Rest des Benutzersteuerelements automatisch an die Eigenschaften des CurrentItem auf dem BindingList bindet.

Wie kann ich den Bindungsausdruck im Stil angeben, so dass er unabhängig vom übergeordneten Datenkontext funktioniert, der eine Sammlungsansicht oder ein einzelnes Element ist?

Antwort

135

Ich hatte Probleme mit der relativen Quelle in Silverlight. Nach dem Suchen und Lesen habe ich keine geeignete Lösung gefunden, ohne eine zusätzliche Binding-Bibliothek zu verwenden. Aber hier ist ein anderer Ansatz für den Zugriff auf den übergeordneten DataContext durch direkten Verweis auf ein Element, von denen Sie den Datenkontext kennen. Es nutzt Binding ElementName und funktioniert recht gut, so lange, wie Sie Ihre eigene Benennung respektieren und haben keine schwere Wiederverwendung von templates/styles über Komponenten:

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

Dies funktioniert auch, wenn Sie den Knopf in Style/Template setzen:

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

zuerst dachte ich, dass die x:Names von Eltern Elemente aus einem Templat-Artikel nicht zugänglich sind, aber da ich keine bessere Lösung gefunden, ich habe gerade versucht, und es funktioniert gut.

+0

Ich habe diesen genauen Code in meinem Projekt, aber es ist ViewModels undicht (Finalizer nicht aufgerufen, scheint Command Bindung DataContext zu behalten). Können Sie überprüfen, ob dieses Problem auch für Sie besteht? –

+0

@Juve das funktioniert, aber ist es möglich, dies zu tun, so dass es für alle itemsControl ausgelöst würde, die die gleiche Vorlage implementieren? Der Name ist eindeutig, so dass wir für jedes eine separate Vorlage benötigen, es sei denn, mir fehlt etwas. – Chris

+1

@Juve missachten meine letzte, ich habe es durch die Verwendung von Verwandten Quelle mit Findancestor und Suche nach Ahnortyp zu arbeiten, (also alle gleich, außer nicht nach Namen suchen). In meinem Fall wiederhole ich die Verwendung von ItemsControls, die jeweils eine Vorlage implementieren, sodass meine so aussieht: Command = "{Binding RelativeSource = {RelativeSource FindAncestor, AncestorType = {x: Type ItemsControl}}, Path = DataContext.OpenDocumentBtnCommand}" – Chris

37

können Sie RelativeSource verwenden das übergeordnete Element zu finden, wie diese -

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

this SO question finden Sie weitere Informationen über RelativeSource.

+8

Ich musste 'Mode = FindAncestor' angeben, damit es funktioniert, aber das funktioniert und ist viel besser in einem MVVM-Szenario, weil es Benennungskontrollen vermeidet. 'Bindung = "{Binding Path = DataContext.CurveSpeedMustBeSpecified, Relative = {Relative Mode = FindAncestor, AncestorType = {x: Typ local: YourParentElementType}}}"' – Aphex

+1

Arbeit wie ein Zauber <3 und musste nicht angeben die Modus, .net 4.6.1 – user2475096

16

Ich war auf der Suche, wie etwas ähnliches in WPF zu tun, und ich habe diese Lösung:

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

Ich hoffe, das für jemand anderen arbeitet. Ich habe einen Datenkontext, der automatisch auf die ItemsControls gesetzt wird, und dieser Datenkontext hat zwei Eigenschaften: MyItems - was eine Collection ist - und einen Befehl 'CustomCommand'. Wegen der ItemTemplate wird eine DataTemplate verwendet, die DataContext der oberen Ebenen ist nicht direkt zugänglich. Dann die Problemumgehung, um die DC des übergeordneten Elements zu erhalten, verwenden Sie einen relativen Pfad und Filter von ItemsControl Typ.

14

Relative vs.Element

Diese beiden Ansätze können das gleiche Ergebnis erzielen,

RelativeSrouce

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

Diese Methode sucht nach einer Kontrolle eines Typ-Fenster (in diesem Beispiel) in der visuellen Baum und wenn es es findet Sie grundsätzlich Zugriff auf es ist DataContext mit der Path=DataContext..... Die Vorteile dieser Methode bestehen darin, dass Sie nicht an einen Namen gebunden sein müssen und dass es dynamisch ist. Änderungen an Ihrem visuellen Baum können diese Methode jedoch beeinträchtigen und möglicherweise sogar brechen.

Element

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

Diese Methode referes an einen festen statischen Name so lange, wie Ihr Umfang kann es sehen, du bist sollten fine.You auf Ihre Namenskonvention kleben nicht zu brechen Diese Methode natürlich. Der Ansatz ist qute einfach und alles, was Sie brauchen, ist eine Name="..." für Ihr Fenster/UserControl.

Obwohl alle drei Typen (RelativeSource, Source, ElementName) in der Lage sind, dasselbe zu tun, aber gemäß dem folgenden MSDN-Artikel, sollte jeder besser in seinem eigenen Fachgebiet verwendet werden.

How to: Specify the Binding Source

Finden Sie die kurze Beschreibung der einzelnen sowie einen Link zu einer mehr Details einer in der Tabelle auf der unten auf der Seite.