none
Anfängerprobleme mit dem HierarchicalDataTemplate

    Frage

  • Hallo zusammen,

    ich kapiere die Verwendung der HierarchicalDataTemplate nicht wirklich. Kann mir da mal jemand helfen?

    Mein Code:

    Basisklasse

    Public Class Basisklasse
    
    End Class
    

    Gruppe

    Imports System.Collections.ObjectModel
    
    Public Class Gruppe
        Inherits Basisklasse
    
        Private _Bezeichnung As String
        Public Property Bezeichnung() As String
            Get
                Return _Bezeichnung
            End Get
            Set(ByVal value As String)
                _Bezeichnung = value
            End Set
        End Property
    
        Private _Children As ObservableCollection(Of Basisklasse)
        Public Property Children() As ObservableCollection(Of Basisklasse)
            Get
                If _Children Is Nothing Then
                    _Children = New ObservableCollection(Of Basisklasse)
                End If
                Return _Children
            End Get
            Set(ByVal value As ObservableCollection(Of Basisklasse))
                _Children = value
            End Set
        End Property
    
    End Class
    

    Tier

    Public Class Tier
        Inherits Basisklasse
    
        Private _Tierart As String
        Public Property Tierart() As String
            Get
                Return _Tierart
            End Get
            Set(ByVal value As String)
                _Tierart = value
            End Set
        End Property
    
    
    End Class
    


    MainWindowViewModel

    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    
    Public Class MainWindowViewModel
        Implements INotifyPropertyChanged
        Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Protected Overridable Sub OnPropertyChanged(propertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
    
        Private _TreeviewDaten As Gruppe
        Public Property TreeviewDaten() As Gruppe
            Get
                Return _TreeviewDaten
            End Get
            Set(ByVal value As Gruppe)
                _TreeviewDaten = value
                OnPropertyChanged("TreeviewDaten")
            End Set
        End Property
    
        Private Function generiereTestdaten() As Gruppe
            Dim erg As Gruppe
            Dim untergruppe As Gruppe
            Dim untergruppe2 As Gruppe
    
            erg = New Gruppe With {.Bezeichnung = "Hauptgruppe"}
            untergruppe = New Gruppe With {.Bezeichnung = "Reptilien"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Frösche"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 2"})
            untergruppe2 = New Gruppe With {.Bezeichnung = "Schlangen"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 2"})
            untergruppe = New Gruppe With {.Bezeichnung = "Säugetiere"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Pferde"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 2"})
    
    
            Return erg
        End Function
    
        Public Sub New()
            TreeviewDaten = generiereTestdaten()
        End Sub
    
    End Class
    


    Beim Debugen sehe ich die Testdaten, so wie ich sie mir Vorstellen, aber der Treeview bleibt leer. Warum?

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:Übung.ObservableCollectionInWpfTreeviewAnzeigen">
        <Window.DataContext>
            <local:MainWindowViewModel/>
        </Window.DataContext>
        <Grid>
            <TreeView DataContext="{Binding Path=TreeviewDaten}">
                <TreeView.ItemTemplate >
                       <HierarchicalDataTemplate DataType="{x:Type local:Gruppe}" ItemsSource="{Binding Children}">
                        <TextBlock Text="{Binding Path=Bezeichnung}"/>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
        </Grid>
    </Window>
    

    Über Hilfe würde ich mich sehr freuen

    Grüße

    Jürgen

    Samstag, 13. Juli 2013 10:41

Antworten

Alle Antworten

  • Hi Jürgen,
    Deine Deklaration ViewModel ist etwas eigenartig. Im ViewModel hast Du keine Eigenschaft mit einer Liste, die Du auf oberster Ebene binden könntest. Deshalb musst Du Dich eine Ebene tiefer begeben und dort anfangen darzustellen. Das könnte dann im Markup so aussehen:

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
            xmlns:local="clr-namespace:WpfApplication1">
      <Window.Resources>
        <local:MainWindowViewModel x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{Binding Source={StaticResource vm}}">
        <TreeView ItemsSource="{Binding TreeviewDaten.Children}">
          <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:Gruppe}"
                                        ItemsSource="{Binding Children}">
              <TextBlock Text="{Binding Path=Bezeichnung}"/>
            </HierarchicalDataTemplate>
          </TreeView.Resources>
        </TreeView>
      </Grid>
    </Window>

    --
    Peter 

    Samstag, 13. Juli 2013 13:24
  • Hallo Peter,

    ich hab mein Viewmodel nun angepasst.

    Imports System.Collections.ObjectModel
    
    Public Class MainWindowViewModel
    
        Private _TreeviewDaten As ObservableCollection(Of Gruppe) = New ObservableCollection(Of Gruppe)
        Public Property TreeviewDaten() As ObservableCollection(Of Gruppe)
            Get
                Return _TreeviewDaten
            End Get
            Set(ByVal value As ObservableCollection(Of Gruppe))
                _TreeviewDaten = value
            End Set
        End Property
    
        Private Function generiereTestdaten() As Gruppe
            Dim erg As Gruppe
            Dim untergruppe As Gruppe
            Dim untergruppe2 As Gruppe
    
            erg = New Gruppe With {.Bezeichnung = "Hauptgruppe"}
            untergruppe = New Gruppe With {.Bezeichnung = "Reptilien"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Frösche"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 2"})
            untergruppe2 = New Gruppe With {.Bezeichnung = "Schlangen"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 2"})
            untergruppe = New Gruppe With {.Bezeichnung = "Säugetiere"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Pferde"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 2"})
    
    
            Return erg
        End Function
    
        Public Sub New()
            TreeviewDaten.Add(generiereTestdaten())
        End Sub
    
    End Class
    

    Das View sieht nun so aus.

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:Übung.ObservableCollectionInWpfTreeviewAnzeigen">
        <Window.Resources>
            <local:MainWindowViewModel x:Key="vm"/>
        </Window.Resources>
        <Grid DataContext="{Binding Source={StaticResource vm}}">
            <TreeView ItemsSource="{Binding TreeviewDaten}">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:Gruppe}"
                                        ItemsSource="{Binding Children}">
                        <TextBlock Text="{Binding Path=Bezeichnung}"/>
                    </HierarchicalDataTemplate>
                    <DataTemplate DataType="{x:Type local:Tier}">
                        <TextBlock Text="{Binding Path=Tierart}"/>
                    </DataTemplate>
                </TreeView.Resources>
            </TreeView>
        </Grid>
    </Window>
    

    Nun ist das Ergebnis so, wie ich es wollte. Aber ich verstehe nicht, warum. Denn in der Informatik hat doch jeder Baum genau eine Wurzel. Aber in diesem Viewmodel könnte nun n Wurzel für den Treeview anlegen. Ist  das wirklich so richtig?

    Grüße

    Jürgen

    • Bearbeitet Jürgen Z Samstag, 13. Juli 2013 13:57 Frage umformuliert
    Samstag, 13. Juli 2013 13:35
  • Hi Jürgen,
    da Du Deine Listen erst unterhalb Deines "Wurzelknotens" beginnst, kannst Du die Bezeichnung des "Wurzelknotens" als Header einfügen.

    --
    Peter

    Samstag, 13. Juli 2013 14:08
  • Hallo Peter,

    ich glaub, ich hab's

    Ich habe mein Viewmodel wieder auf eine Wurzel gebaut.

    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    
    Public Class MainWindowViewModel
        Implements INotifyPropertyChanged
        Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Protected Overridable Sub OnPropertyChanged(propertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
    
        Private _TreeviewDaten As Gruppe
        Public Property TreeviewDaten() As Gruppe
            Get
                Return _TreeviewDaten
            End Get
            Set(ByVal value As Gruppe)
                _TreeviewDaten = value
                OnPropertyChanged("TreeviewDaten")
            End Set
        End Property
    
        Private Function generiereTestdaten() As Gruppe
            Dim erg As Gruppe
            Dim untergruppe As Gruppe
            Dim untergruppe2 As Gruppe
    
            erg = New Gruppe With {.Bezeichnung = "Hauptgruppe"}
            untergruppe = New Gruppe With {.Bezeichnung = "Reptilien"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Frösche"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Frosch 2"})
            untergruppe2 = New Gruppe With {.Bezeichnung = "Schlangen"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Schlange 2"})
            untergruppe = New Gruppe With {.Bezeichnung = "Säugetiere"}
            erg.Children.Add(untergruppe)
            untergruppe2 = New Gruppe With {.Bezeichnung = "Pferde"}
            untergruppe.Children.Add(untergruppe2)
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 1"})
            untergruppe2.Children.Add(New Tier With {.Tierart = "Pferd 2"})
    
    
            Return erg
        End Function
    
        Public Sub New()
            TreeviewDaten = generiereTestdaten()
        End Sub
    
    End Class
    

    und im View

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:Übung.ObservableCollectionInWpfTreeviewAnzeigen">
        <Window.Resources>
            <local:MainWindowViewModel x:Key="vm"/>
        </Window.Resources>
        <Grid DataContext="{Binding Source={StaticResource vm}}">
            <TreeView >
                <TreeViewItem Header="{Binding Path=TreeviewDaten.Bezeichnung}" ItemsSource="{Binding Path=TreeviewDaten.Children}">
                    <TreeViewItem.Resources>
                        <HierarchicalDataTemplate DataType="{x:Type local:Gruppe}"
                                        ItemsSource="{Binding Children}">
                            <TextBlock Text="{Binding Path=Bezeichnung}"/>
                        </HierarchicalDataTemplate>
                        <DataTemplate DataType="{x:Type local:Tier}">
                            <TextBlock Text="{Binding Path=Tierart}"/>
                        </DataTemplate>
                    </TreeViewItem.Resources>
                </TreeViewItem>
            </TreeView>
        </Grid>
    </Window>
    

    Danke

    Jürgen

    Samstag, 13. Juli 2013 14:51
  • Hi Jürgen,
    gut, dass Du eine Lösung gefunden hast. Wenn Du das so machst, dann ist unklar, warum im ViewModel nicht gleich eine Liste anstelle eines Objektes "Gruppe" in der TreeViewDaten-Eigenschaft bereitgestellt wird.

    --
    Peter

    Samstag, 13. Juli 2013 15:16
  • Hallo Peter,

    wie meinst Du das? Kannst Du mir mal ein Viewmodel und ein view liefern. Allerding bin ich nun für heute Offline. Bis morgen.

    Jürgen

    Samstag, 13. Juli 2013 15:30
  • Hi Jürgen,
    leider kann ich nicht sagen, wie Dein Datenmodell auszusehen hat und wie dieses Datenmodell in der Oberfläche darzustellen ist. Wenn es nur einen Rootknoten geben soll, bleibt die Frage, warum er als TreeViewItem überhaupt darzustellen ist. Das ist verschwendeter Platz und die Möglichkeit, diesen Knoten auf- und zuzuklappen, wird auch kein Anwender verstehen.

    --
    Peter

    Samstag, 13. Juli 2013 15:55
  • Hallo Peter,

    vielen Dank für den Hinweis

    Jürgen

    • Als Antwort markiert Jürgen Z Sonntag, 14. Juli 2013 06:54
    Sonntag, 14. Juli 2013 06:51