DataTemplate einer zweistufigen Bindung

  • WPF

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    DataTemplate einer zweistufigen Bindung

    Hallo Leute,

    ich steh mal wieder vorm Wald, den ich nicht sehe. Vielleicht habt ihr mal wieder einen kleinen Hinweis, der bei mir den Stein zum Rollen bringen kann.

    ich habe eine Klasse, die ich in einer Collection an eine ListView gebe und die Daten daran binde. Kein Problem soweit. Hier erstelle ich einfach ein DataTemplate und lasse mir die Daten so anzeigen, wie ich das möchte.

    Nun habe ich aber verschiedene Gruppen von Daten, die sich anhand anderer Eigenschaften etwas unterscheiden, aber vom Typ her identisch sind. Ich möchte die speziellen Eigenschaften anders gruppieren und dynamisch anzeigen, deshalb habe ich eine Klasse geschrieben, die nun die Collection enthält und hierzu auch noch weitere Eigenschaften (zB einen Header). Nun möchte ich diese Daten an ein ListView binden. Was auch tadellos klappt. Wenn ich nun aber versuche, die Daten der eingebetteten Collection anzeigen zu lassen, komme ich nicht weiter, weil ich an die Datenbindung irgendwie nicht herankomme. Zumindest tut sich hier nichts.

    folgendes habe ich:

    XML-Quellcode

    1. ​<ListView Grid.Row="1" DataContext="{StaticResource ViewProgramManager}" ItemsSource="{Binding ProgramManagerView }" IsSynchronizedWithCurrentItem="True">
    2. <ListView.ItemTemplate>
    3. <DataTemplate>
    4. <Expander Header="{Binding Header}">
    5. <ListView ItemsSource="{Binding RelativeSource={RelativeSource Mode=Self}, Path=View}" Height="200" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=View}">
    6. <ListView.ItemTemplate>
    7. <DataTemplate>
    8. <StackPanel>
    9. <TextBlock Text="{Binding Description}"/>
    10. <TextBlock Text="{Binding Header}"/>
    11. </StackPanel>
    12. </DataTemplate>
    13. </ListView.ItemTemplate>
    14. </ListView>
    15. </Expander>
    16. </DataTemplate>
    17. </ListView.ItemTemplate>
    18. </ListView>


    Die ListView bekommt vom ProgramMangerView die Collection, die es anzeigen soll. In dieser sind alle Elemente vom Typ "ExpanderApp" gespeichert als Collection (soweit so gut).

    Die Klasse ExpanderApp sieht (erstmal) wie folgt aus:

    C#-Quellcode

    1. ​private string _header;
    2. public string Header { get { return _header; } set { SetField(ref _header, value); } }
    3. private ManagedAppCollection _managedCollection;
    4. public ManagedAppCollection ManagedCollection { get { return _managedCollection; } private set { SetField(ref _managedCollection, value); } }
    5. private ListCollectionView _view;
    6. public ListCollectionView View { get { return _view; } private set { SetField(ref _view, value); } }
    7. public ExpanderApp()
    8. {
    9. }
    10. public ExpanderApp(string header)
    11. {
    12. _header = header;
    13. _managedCollection = new ManagedAppCollection();
    14. _view = new ListCollectionView(_managedCollection);
    15. }


    Nichts ungewöhnliches. Enthält (derzeit) nur die Header-Information sowie die Collection der "ManagedApp".

    Der Expander wird mir auch richtig angezeigt, nur ist sein Inhalt leer. Eine Fehlermeldung bzgl. des DataBindings erhalte ich auch nicht und die View als ItemSource ist auch richtig angegeben, da mir VS auch diese als Vorschlag gibt. Den DataContext habe ich extra gesetzt, aber auch ohne gibt es keinen Unterschied.

    Wie gesagt, wenn ich auf die ManagedApp Collection einfach so referenziere, erhalte ich alles, was ich brauche. Wenn ich auf die ExpanderApp-Collection referenziere, erhalte ich auch hier alles notwendige. Also war mein Gedanke, dass ich ja in den Expander einfach eine weitere ListView packen kann, die auf alle Elemente des Expanders verweist und für diese ListVie ein DataTemplate erstellen kann. Aber irgendwie führt das nicht zum Ziel. Und entweder habe ich etwas übersehen oder diese Möglichkeit ist nicht so einfach wie ich mir das vorgestellt hatte.

    Ja, ich weiß auch, dass man eine Gruppierung über die ListView machen kann, somit wäre Header überflüssig in einer eigenen Klasse und es könnte in die ManagedApp mit hinein, sodass ich nach diesem Kriterien filtern oder gruppieren könnte. Aber so einfach ist das bei meinen Anforderungen nicht, da hier noch einige Variablen hinzukommen und sich damit das Verhalten ändern wird. Aber vielleicht habt ihr ja wieder einen kleinen Wink mit einem großen Zaunpfahl, was vielleicht falsch oder besser sein könnte :)

    Anbei die Bilder: Der Expander, der irgendwie keinen Inhalt hat und die Auflistung, wie die ManagedApp zB aussehen könnte:
    Bilder
    • Expander_ohne_inhalt.png

      15,2 kB, 627×292, 146 mal angesehen
    • managedapp.png

      14,62 kB, 624×177, 158 mal angesehen

    PadreSperanza schrieb:

    Nun habe ich aber verschiedene Gruppen von Daten, die sich anhand anderer Eigenschaften etwas unterscheiden, aber vom Typ her identisch sind.

    Wenn ich diese "real World" Konstellation als Klassen abbilde kommen hier zwei Klassen heraus welche beide eine gemeinsame Basisklasse haben.

    Die Collection ist vom Typ der Basisklasse, somit kannst du diese mit beiden Typen füllen. Im View hast du dann für deine ListView einfach für jeden der beiden Typen ein dataTemplate.
    Die WPF ist dann so intelligent und nimmt für jedes Item das DataTemplate für diesen Typ. Die DataTemplate definierst du einfach in den Resourcen des ListView.

    Grüße
    Sascha

    PS: Genau dafür gibt es auch ein Kapitel in meiner Tutorialreihe.
    Tutorialreihe <WPF lernen/>
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    ja okay. Das habe ich nun insoweit verstanden. Leider bleibt das Problem, dass ich quasi eine Collection habe, in der wiederum weitere Collections verwaltet werden, welche nicht alle vom selben Typ sein müssten. Dass ich nun das DataTemplate je nach Typ definieren kann, ist schon mal klasse. Vielen Dank dafür, das hatte mir tatsächlich auch gefehlt ^^

    Aber wie sorge ich dafür, dass ich anhand eines Datenkontexts (hier ist eine Collection enthalten), auf die Collections zugreifen kann, die sich darunter befinden? Ich möchte quasi dem Nutzer die Möglichkeit geben, die Anzahl an Elementen zu erweitern, wobei er hier auch andere Typen auswählen kann, welche ich dann dynamisch in mehreren Collections speichern möchte. Nur leider weiß ich nicht, wie ich dafür sorgen kann, dass ich auf diese "Untercollections" zugreifen kann mittels Databinding. Wenn es speziell um das Auslesen des SelectedItem ginge, wäre mir das klar. Dann könnte man das im Zusatzinfo anzeigen mittels binding an SelectedItem der Listview. Aber ich bräuchte alle und nicht nur das selektierte
    Ha, dank deiner Hilfe hab ich es hinbekommen. Dein Verweis deines Tutorials mit den x:keys hat den Ausschlag gegeben:

    Ich muss das Binding umschreiben:

    XML-Quellcode

    1. ​<ListView Grid.Row="1" DataContext="{StaticResource ViewProgramManager}" x:Name="OuterListView" ItemsSource="{Binding ProgramManagerView }" IsSynchronizedWithCurrentItem="True">
    2. <ListView.ItemTemplate>
    3. <DataTemplate>
    4. <Expander Header="{Binding Header}">
    5. <ListView ItemsSource="{Binding ManagedCollection}" Height="200" ItemTemplate="{StaticResource InnerTemplateForManagedApps}">
    6. <ListView.ItemsPanel>
    7. <ItemsPanelTemplate>
    8. <WrapPanel/>
    9. </ItemsPanelTemplate>
    10. </ListView.ItemsPanel>
    11. </ListView>
    12. </Expander>
    13. </DataTemplate>
    14. </ListView.ItemTemplate>
    15. </ListView>


    Binding auf "Self" war nicht richtig und die ListView "View" brauche ich auch nicht, sondern dank deines Tricks: Den Templates Namen und Styles zu geben, habe ich ein InnerTemplate erstellen können:

    XML-Quellcode

    1. ​<DataTemplate x:Key="InnerTemplateForManagedApps">
    2. <Border BorderBrush="LightGray" BorderThickness="1" CornerRadius="1,0,0,0" Padding="4" Margin="5">
    3. <DockPanel>
    4. <TextBlock DockPanel.Dock="top" Text="{Binding Description}"/>
    5. <DockPanel DockPanel.Dock="Bottom">
    6. <CheckBox DockPanel.Dock="Right" />
    7. <Button Content="Öffnen"/>
    8. </DockPanel>
    9. <Image Source="pack://application:,,,/images\other\404.png" Width="40"/>
    10. </DockPanel>
    11. </Border>
    12. </DataTemplate>


    und schon führt es zur richtigen Anzeige :)

    Ich sag ja, mir fehlte nur ein Wink mit dem Zaunpfahl... einfach die Templates auslagern und trennen, dann verliert man auch nicht die Übersicht :)

    PadreSperanza schrieb:

    Dein Verweis deines Tutorials mit den x:keys hat den Ausschlag gegeben

    Deshalb hab ichs ja verlinkt :D

    Schön das es klappt.
    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##