WPF TreeView

  • WPF

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von mikeb69.

    Hallo,

    der Einstieg in WPF und dem Treeview fällt mir richtig schwer.
    Seit Tagen schaff ich es nicht zwei Ebenen anzuzeigen.

    Ich hoffe auf eure Hilfe

    Meine Daten sehen wie folgt aus.

    C#-Quellcode

    1. public class Component
    2. {
    3. private string name;
    4. private ObservableCollection<ComponentVersion> versions = new ObservableCollection<ComponentVersion>();
    5. public Component(string filename, string name, string version)
    6. {
    7. this.name = name;
    8. this.versions.Add(new ComponentVersion(version, filename, false));
    9. }
    10. public void Add(ComponentVersion toAdd)
    11. {
    12. versions.Add(toAdd);
    13. }
    14. public string Name
    15. {
    16. get { return this.name; }
    17. set { this.name = value; }
    18. }
    19. public ObservableCollection<ComponentVersion> Versions
    20. {
    21. get { return this.versions; }
    22. set { this.versions = value; }
    23. }
    24. }


    und

    C#-Quellcode

    1. public class ComponentVersion
    2. {
    3. private string version;
    4. private bool choosen;
    5. private string filename;
    6. public ComponentVersion(string version, string filename, bool choosen)
    7. {
    8. this.version = version;
    9. this.choosen = choosen;
    10. this.filename = filename;
    11. }
    12. public string Version
    13. {
    14. get { return this.version; }
    15. set { this.version = value; }
    16. }
    17. public string Name
    18. {
    19. get { return this.version; }
    20. set { this.version = value; }
    21. }
    22. public string Filename
    23. {
    24. get { return this.filename; }
    25. set { this.filename = value; }
    26. }
    27. public bool Choosen
    28. {
    29. get { return this.choosen; }
    30. set { this.choosen = value; }
    31. }
    32. }


    Im ViewModel wird das dann per Property zur Verfügung gestellt

    C#-Quellcode

    1. public ObservableCollection<Component> Components
    2. {
    3. get { return this.sourceComponents.GetComponents; }
    4. }


    Bisher hab ich folgendes XAML

    XML-Quellcode

    1. <TreeView Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="3" ItemsSource="{Binding Components}">
    2. <TreeView.ItemTemplate>
    3. <DataTemplate>
    4. <TextBlock Text="{Binding Name}"></TextBlock>
    5. </DataTemplate>
    6. </TreeView.ItemTemplate>
    7. </TreeView>


    Die Schwierigkeit für mich ist es nun die zweite Ebene darzustellen.
    ​Hier reicht mir als Ausgabe die Version.

    Wäre cool wenn mich jemand in die richtige Richtung schupsen kann.

    Gruss

    mikeb69

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „mikeb69“ ()

    Literatur: rheinwerk-verlag.de/windows-presentation-foundation_3844/ - ganz hilfreich, der Inhalt könnte aber weniger Wiederholungen aufweisen. Dann wäre das Buch vmtl. auch 20% dünner. Gerade das vermittelte Hintergrundwissen ist bei der WPF jedoch wichtig, weil es sehr viele Automatismen gibt, auf die man so gar nicht kommen kann.
    Gruß
    hal2000
    @ErfinderDesRades
    @hal2000

    hat ein wenig gedauert bis ich Zeit für mein Treeview Problem gefunden habe.
    Sitze jetzt wieder Stunden daran und bekomm das einfach nicht gebacken.

    @hal2000 - das Buch hab ich mir noch nicht besorgt.

    Mir würde ein funktionierendes Beispiel eines Treeview der eine Klasse mit ViewModel darstellt reichen.
    Daraus könnte ich das Thema erarbeiten denke ich.

    Gruss

    mikeb69
    @ErfinderDesRades,

    bin jetzt einen Schritt weiter.
    Häng aber noch ein wenig.

    ​Die Daten bekomm ich jetzt in den Treeview, nur nicht gemeinsam.
    Der Bereich 'DataTemplate' enthält die untergeordneten Items, der Bereich im 'HierarchicalDataTemplate' die Übergeordneten.
    Wie bring ich die Beiden nun zusammen ?

    XML-Quellcode

    1. <TreeView ItemsSource="{Binding Components}">
    2. <TreeView.ItemTemplate>
    3. <!--<DataTemplate>
    4. <TreeViewItem ItemsSource="{Binding Components}" Header="{Binding Name}">
    5. <TreeViewItem.ItemTemplate>
    6. <DataTemplate>
    7. <TextBlock Text="{Binding Version}"/>
    8. </DataTemplate>
    9. </TreeViewItem.ItemTemplate>
    10. </TreeViewItem>
    11. </DataTemplate>-->
    12. <HierarchicalDataTemplate DataType="local:ComponentVersion" ItemsSource="{Binding ComponentVersions}">
    13. <StackPanel Orientation="Horizontal">
    14. <TextBlock Text="{Binding Version}"/>
    15. </StackPanel>
    16. </HierarchicalDataTemplate>
    17. </TreeView.ItemTemplate>
    18. </TreeView>


    Gruss

    mikeb69

    mikeb69 schrieb:

    Das Brett will nur nicht weg vom Kopf.

    Eine typische Folge der Automatismen im Hintergrund der WPF. Besorg dir das Buch!

    mikeb69 schrieb:

    DataType="local:ComponentVersion"

    ...sollte DataType="{x:Type local:ComponentVersion}" lauten. Es macht einen Unterschied, ob in der Eigenschaft eine MarkupExtension (Wert ist ein System.Type) oder ein String (Wert ist ein System.String) gesetzt wird. Das steht übrigens im Buch auf Seite 601 :) .
    Gruß
    hal2000
    @ErfinderDesRades
    @hal2000

    wie auch immer ich das drehe und wende, ich komm nicht weiter.
    Weder mit den verlinken Beispielen von EDR noch mit anderen Beispielen aus dem Internet.

    blogs.msdn.microsoft.com/mikeh…datatemplate-step-by-step
    (diesen Link als Link einzufügen klappt irgendwie nicht - egal.

    Das interessante dabei ist, dass ich beim Setzen eines Breakpoints, zwar in den Components im XAML meine Daten sehen, aber nicht im ComponentVersions.
    Die Property im ViewModel gibt mir jedoch alle Daten aus - Breakpoint, Maus drüberfahr.

    Das Ganze ist echt frustrierend.

    XML-Quellcode

    1. <TreeView ItemsSource="{Binding Components}">
    2. <TreeView.ItemTemplate>
    3. <HierarchicalDataTemplate DataType="{x:Type local:Component}" ItemsSource="{Binding Components}">
    4. <TextBlock Text="{Binding Path=Name}" />
    5. <HierarchicalDataTemplate.ItemTemplate>
    6. <HierarchicalDataTemplate DataType="x:Type local:ComponentVersion" ItemsSource="{Binding ComponentVersions}">
    7. <TextBlock Text="{Binding Path=Version}" />
    8. </HierarchicalDataTemplate>
    9. </HierarchicalDataTemplate.ItemTemplate>
    10. </HierarchicalDataTemplate>
    11. </TreeView.ItemTemplate>

    Das lässt mich darauf schließen, dass mit meinem Datenobjekt was nicht in Ordnung ist.

    Im Anhang ein Foto das die Daten zeigt.
    Eigentlich alles da.

    Gruss

    mikeb69
    Bilder
    • 20170219_075904.jpg

      274,18 kB, 1.000×562, 171 mal angesehen
    Du hast die ItemsSource des TreeView an "Components" gebunden. Das heißt, dass ein TreeViewItem ein Element der Components-Auflistung (ein "Component") ist. Das Template definiert nun den Visual Tree von "Component", was du auch richtig als DataType angegeben hast. Das Template selbst gehört jedoch in die Ressourcen (siehe Post 9), weil nur dann automatisch ein DataTemplateKey erzeugt wird, wodurch das Template implizit auf den Component-Typ angewendet wird. Als ItemTemplate macht das HierarchicalDataTemplate keinen Sinn, weil nur ein ItemTemplate möglich ist. Das heißt, dass alle TreeViewItems dasselbe Template haben, was du aber nicht willst.

    Weiterhin ist die ItemsSource des HierDataTemplate an eine Eigenschaft "Components" gebunden. Das würde bedeuten, dass jedes Element vom Typ Component (aus der Components-Auflistung, die schon am TreeView hängt) wiederum eine Eigenschaft "Components" besitzen müsste. Im Codebeispiel deines verlinkten Blogposts wird auch an "League" und "Teams" gebunden, nicht zweimal an "League". Dein XAML setzt also bis jetzt folgende Datenstruktur voraus:

    Components As IEnumerable(Of Component)
    Component.Name As String
    Component.Components As IEnumerable(Of ComponentVersion)
    ComponentVersion.Version As String

    Nun kommt aber dazu, dass du deine HierDataTemplates schachtelst, was zwar erlaubt, aber nicht sinnvoll ist (weil unübersichtlich). Diese Templates sind nicht dafür da, um sie manuell ineinander zu schachteln, sondern dafür, dass die WPF das automatsich für dich tut, basierend auf dem DataType. Definiere für jeden Typ, den du darstellen willst, ein HierDataTemplate in den Ressourcen. Setze den DataType und definiere den Visual Tree. Setze die ItemsSource des Templates nur dann, wenn das Element Kindelemente hat. Im TreeView setzt du nur die ItemsSource. Beispiel:

    XML-Quellcode

    1. <Window x:Class="MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:local="clr-namespace:WpfApplication4"
    5. Title="MainWindow" Height="350" Width="525">
    6. <Window.Resources>
    7. <HierarchicalDataTemplate DataType="{x:Type local:Component}" ItemsSource="{Binding Versions}">
    8. <TextBlock Text="{Binding Name}" />
    9. </HierarchicalDataTemplate>
    10. <HierarchicalDataTemplate DataType="{x:Type local:ComponentVersion}">
    11. <TextBlock Text="{Binding Version}" />
    12. </HierarchicalDataTemplate>
    13. <ObjectDataProvider ObjectType="{x:Type local:ViewModel}" x:Key="vm" />
    14. </Window.Resources>
    15. <Grid DataContext="{StaticResource vm}">
    16. <TreeView ItemsSource="{Binding Components}"/>
    17. </Grid>
    18. </Window>

    ...mit diesem Datenmodell im Hintergrund:

    VB.NET-Quellcode

    1. Class ViewModel
    2. Property Components As New List(Of Component)
    3. Public Sub New()
    4. Components.Add(New Component("c1"))
    5. Components.Add(New Component("c2"))
    6. End Sub
    7. End Class
    8. Class Component
    9. Public Property Name As String
    10. Public Property Versions As New List(Of ComponentVersion)
    11. Public Sub New(name As String)
    12. Me.Name = name
    13. Dim rnd As New Random
    14. Versions.Add(New ComponentVersion(rnd.Next(1, 10)))
    15. Versions.Add(New ComponentVersion(rnd.Next(1, 10)))
    16. End Sub
    17. End Class
    18. Class ComponentVersion
    19. Property Version As Int32
    20. Public Sub New(ver As Int32)
    21. Me.Version = ver
    22. End Sub
    23. End Class


    Edit: Vollständiges Beispiel ergänzt. Der RNG erzeugt immer dieselben Zahlen, aber als Demo sollte es reichen.
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „hal2000“ ()

    @hal2000,

    danke für die ausführliche Erklärung.
    ​Hat jedoch erst geklappt als ich ein komplett neues Projekt erstellt hatte.

    Muss mir unbedingt das von dir empfohlene Buch kaufen.

    ​So wie ich das jetzt sehe pack ich die Treeviews in jeweils ein eigenes UserControl.
    ​Spannend wird dann noch, wenn alles einen eigenen Namespace wegen eigener Ordner bekommen wird usw.

    ​Die nächsten Wochen werden spannend.

    Nochmals vielen vielen Dank für deine Geduld

    mikeb69