none
Hierarchical TreeView durchgehen RRS feed

  • Frage

  • Hallo!

    Ich habe ein TreeView mit einem HierarchicalDataTemplate, dessen Knoten ich aufgeklappt darstellen möchte.

    Bei dem (rekursiven) iterieren durch die TreeViewItems habe ich (durch das HierarchicalDataTemplate) das Problem, dass die TreeViewItems nicht mehr von der Klasse TreeViewItem, sondern von der Klasse des jeweiligen (hierarischen) Datentyps sind.

    public static void TreeView_ExpandAll(ItemsControl treeViewOrTreeViewItem, bool ulAufgeklappt)
    {
        TreeViewItem pTreeViewItem;
    
        pTreeViewItem = treeViewOrTreeViewItem as TreeViewItem;

    (treeViewOrTreeViewItem as TreeViewItem wird immer Null, da das TreeViewItem nicht ein Verweis auf das TreeViewItem des TreeView's sondern auf die (hierarische) Datenklasse ist!)

    Da ich die Funktion aber wieder rekursiv mit der Übergabe des aktuellen TreeViewItems aufrufen möchte, müßte ich das TreeViewItem des TreeViews ermitteln (und nicht die Datenklasse). Wie geht das aber bei einem HierarchicalDataTemplate???

    for (int i = 0; i < treeViewOrTreeViewItem.Items.Count; i++)
    {
        pTreeViewItem = treeViewOrTreeViewItem.Items[i] as TreeViewItem;
        if (pTreeViewItem != null)
        {
            pTreeViewItem.IsExpanded = ulAufgeklappt;            // Akt. Eintrag Zu (oder Auf) geklappt darstellen
            TreeView_ExpandAll(pTreeViewItem, ulAufgeklappt);   // Rekursiver Aufruf für untergeordnete Items
        }
    }





    • Bearbeitet perlfred Mittwoch, 2. Mai 2018 14:38
    Mittwoch, 2. Mai 2018 11:53

Antworten

  • Hi,
    wenn es Dir nur um das Aufklappen geht, dann setze Expanded auf true:

    public class Person : TreeViewItemBase { public Person() { this.Children = new System.Collections.ObjectModel.ObservableCollection<Person>(); this.IsExpanded = true; }

    ...



    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Mittwoch, 2. Mai 2018 19:13
    Mittwoch, 2. Mai 2018 15:03
  • Hi Fred,
    ohne alle Elemente im TreeView zu durchlaufen, kannst Du auch direkt über die Bindung alles aufklappen und wieder zusammenklappen. Dazu mal folgendes Beispiel mit Deinem Grundgerüst:

    XAML:

    <Window x:Class="WpfApp1CS.Window53"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1CS"
            mc:Ignorable="d"
            Title="Window53" Height="300" Width="300">
      <Window.Resources>
        <local:Window53VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition/>
        </Grid.RowDefinitions>
        <CheckBox Content="Expand" IsChecked="{Binding IsExpanded}"/>
        <TreeView Grid.Row="1" ItemsSource="{Binding View}">
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
              <StackPanel Orientation="Horizontal">
                <Image  Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Name}" Margin="0,0,4,0" />
              </StackPanel>
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
          <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
              <Setter Property="IsExpanded" Value="{Binding Source={StaticResource   vm}, Path=IsExpanded}" />
            </Style>
          </TreeView.ItemContainerStyle>
        </TreeView>
      </Grid>
    </Window>
    

    Und dazu der ViewModel und die Datenklasse:

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1CS
    {
      public class Window53VM: INotifyPropertyChanged
      {
        private bool _isExpanded = true;
        public bool IsExpanded { get { return this._isExpanded; }
          set
          {
            this._isExpanded = value;
            OnPropertyChanged(string.Empty);
          }
        }
        private CollectionViewSource cvs = new CollectionViewSource();
        public ICollectionView View
        {
          get
          {
            if (cvs.Source == null) cvs.Source = GetData();
            return cvs.View;
          }
        }
    
        private ObservableCollection<Window53Person> GetData()
        {
          ObservableCollection<Window53Person> col = new ObservableCollection<Window53Person>();
          Window53Person person1 = new Window53Person() { Name = "Schmidt", Age = 42 };
          Window53Person person2 = new Window53Person() { Name = "Meyer", Age = 39 };
          Window53Person person3 = new Window53Person() { Name = "Schulze", Age = 25 };
    
          Window53Person child1 = new Window53Person() { Name = "Angela", Age = 13 };
          Window53Person child2 = new Window53Person() { Name = "Melanie", Age = 13 };
          person1.Children.Add(child1);
          person2.Children.Add(child2);
    
          col.Add(person1);
          col.Add(person2);
          col.Add(person3);
          return col;
        }
    
        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
      }
      public class Window53Person
      {
        public string Name { get; set; }
    
        public int Age { get; set; }
    
        public ObservableCollection<Window53Person> Children { get; set; }= new ObservableCollection<Window53Person>();
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Donnerstag, 3. Mai 2018 16:53
    Donnerstag, 3. Mai 2018 07:21

Alle Antworten

  • Hallo!

    Zur Erklärung habe ich mal ein kleines Beispiel zusammengestellt, in dem ich nur ein hierarichcal TreeView erzeuge, in dem standardmäßig alle TreeViewItems zugeklappt sind:

    <Window x:Class="SQLtoExcel.Window2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:SQLtoExcel"
            mc:Ignorable="d"
            Title="Window2" Height="300" Width="300">
            <TreeView Name="tv">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                        <StackPanel Orientation="Horizontal">
                            <Image  Margin="0,0,5,0" Source="Bilder/table16.png" />
                            <TextBlock Text="{Binding Name}" Margin="0,0,4,0" />
                        </StackPanel>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
                <TreeView.ItemContainerStyle>
                    <Style TargetType="TreeViewItem">
                        <Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
                    </Style>
                </TreeView.ItemContainerStyle>
            </TreeView>
    </Window>
    

    using System.Collections.Generic;
    using System.Windows;
    
    namespace SQLtoExcel
    {
        /// <summary>
        /// Interaktionslogik für Window2.xaml
        /// </summary>
        public partial class Window2 : Window
        {
            public Window2()
            {
                InitializeComponent();
    
                List<Person> persons = new List<Person>();
                Person person1 = new Person() { Name = "Schmidt", Age = 42 };
                Person person2 = new Person() { Name = "Meyer", Age = 39 };
                Person person3 = new Person() { Name = "Schulze", Age = 25 };
    
                Person child1 = new Person() { Name = "Angela", Age = 13 };
                Person child2 = new Person() { Name = "Melanie", Age = 13 };
                person1.Children.Add(child1);
                person2.Children.Add(child2);
    
                persons.Add(person1);
                persons.Add(person2);
                persons.Add(person3);
    
                tv.ItemsSource = persons;
            }
        }
    
        public class Person : TreeViewItemBase
        {
            public Person()
            {
                this.Children = new System.Collections.ObjectModel.ObservableCollection<Person>();
            }
    
            public string Name { get; set; }
    
            public int Age { get; set; }
    
            public System.Collections.ObjectModel.ObservableCollection<Person> Children { get; set; }
        }
    
        public class TreeViewItemBase : INotifyPropertyChanged
        {
            private bool isExpanded;
            public bool IsExpanded
            {
                get { return this.isExpanded; }
                set
                {
                    if (value != this.isExpanded)
                    {
                        this.isExpanded = value;
                        NotifyPropertyChanged("IsExpanded");
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void NotifyPropertyChanged(string propName)
            {
                if (this.PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    
    }
    

    Wie kann ich alle TreeViewItems aufklappen?

    Ein einzelnes TreeViewItem kann ich so aufklappen:

    (tv.SelectedItem as Person).IsExpanded = true

    Mittwoch, 2. Mai 2018 14:35
  • Hi,
    wenn es Dir nur um das Aufklappen geht, dann setze Expanded auf true:

    public class Person : TreeViewItemBase { public Person() { this.Children = new System.Collections.ObjectModel.ObservableCollection<Person>(); this.IsExpanded = true; }

    ...



    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Mittwoch, 2. Mai 2018 19:13
    Mittwoch, 2. Mai 2018 15:03
  • Hallo Peter!

    Ja, in erster Instanz geht es mir nur um das Aufklappen.

    Auf dem nach Hause Weg ist es mir wie Schuppen aus dem Augen gefallen, dass man die TreViewItem Ansicht ganz einfach über die Daten steuern kann, da diese Eigenschaft  (IsExpanded) ja über den ItemContainerStyle gebunden ist.

    Erklärt mir zwar immer noch nicht, wie ich an das eigentliche TreeViewItem im Hierarchical-TreeView heran komme, brauche ich jetzt aber auch nicht mehr. Dafür muss ich mich mal intensiver mit dem iterieren durch hierarische Klassen beschäftigen, wird nicht langweilig :-) ...

    Trotzdem wieder einmal  vielen Dank für deine Lösung!!!

    Fred.



    • Bearbeitet perlfred Mittwoch, 2. Mai 2018 19:13
    Mittwoch, 2. Mai 2018 19:12
  • Hi Fred,
    ohne alle Elemente im TreeView zu durchlaufen, kannst Du auch direkt über die Bindung alles aufklappen und wieder zusammenklappen. Dazu mal folgendes Beispiel mit Deinem Grundgerüst:

    XAML:

    <Window x:Class="WpfApp1CS.Window53"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1CS"
            mc:Ignorable="d"
            Title="Window53" Height="300" Width="300">
      <Window.Resources>
        <local:Window53VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition/>
        </Grid.RowDefinitions>
        <CheckBox Content="Expand" IsChecked="{Binding IsExpanded}"/>
        <TreeView Grid.Row="1" ItemsSource="{Binding View}">
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
              <StackPanel Orientation="Horizontal">
                <Image  Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Name}" Margin="0,0,4,0" />
              </StackPanel>
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
          <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
              <Setter Property="IsExpanded" Value="{Binding Source={StaticResource   vm}, Path=IsExpanded}" />
            </Style>
          </TreeView.ItemContainerStyle>
        </TreeView>
      </Grid>
    </Window>
    

    Und dazu der ViewModel und die Datenklasse:

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1CS
    {
      public class Window53VM: INotifyPropertyChanged
      {
        private bool _isExpanded = true;
        public bool IsExpanded { get { return this._isExpanded; }
          set
          {
            this._isExpanded = value;
            OnPropertyChanged(string.Empty);
          }
        }
        private CollectionViewSource cvs = new CollectionViewSource();
        public ICollectionView View
        {
          get
          {
            if (cvs.Source == null) cvs.Source = GetData();
            return cvs.View;
          }
        }
    
        private ObservableCollection<Window53Person> GetData()
        {
          ObservableCollection<Window53Person> col = new ObservableCollection<Window53Person>();
          Window53Person person1 = new Window53Person() { Name = "Schmidt", Age = 42 };
          Window53Person person2 = new Window53Person() { Name = "Meyer", Age = 39 };
          Window53Person person3 = new Window53Person() { Name = "Schulze", Age = 25 };
    
          Window53Person child1 = new Window53Person() { Name = "Angela", Age = 13 };
          Window53Person child2 = new Window53Person() { Name = "Melanie", Age = 13 };
          person1.Children.Add(child1);
          person2.Children.Add(child2);
    
          col.Add(person1);
          col.Add(person2);
          col.Add(person3);
          return col;
        }
    
        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
      }
      public class Window53Person
      {
        public string Name { get; set; }
    
        public int Age { get; set; }
    
        public ObservableCollection<Window53Person> Children { get; set; }= new ObservableCollection<Window53Person>();
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Donnerstag, 3. Mai 2018 16:53
    Donnerstag, 3. Mai 2018 07:21
  • Hallo Peter!

    Erst einmal, ich habe es nachgestellt und es funktioniert bei mir auch 1a!!!

    Nun habe ich es auch mit unterschiedlichen Klassen für jede Hierarchie-Stufe ausprobiert, alles Super:

    <Window x:Class="SQLtoExcel.Window3"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:SQLtoExcel"
            mc:Ignorable="d"
            Title="Window3" Height="300" Width="300">
        <Window.Resources>
            <local:VM x:Key="vm"/>
        </Window.Resources>
        <Grid DataContext="{StaticResource vm}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <CheckBox Content="Expand" IsChecked="{Binding IsExpanded}"/>
            <TreeView Grid.Row="1" ItemsSource="{Binding View}">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:Person}" ItemsSource="{Binding Children}">
                         <TextBlock Text="{Binding Name}" />
                   </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type local:PersonEigenschaft}" ItemsSource="{Binding}">
                        <TextBlock Text="{Binding Sternzeichen}" Foreground="{Binding Lieblingsfarbe}" Margin="0,0,4,0" />
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
                <TreeView.ItemContainerStyle>
                    <Style TargetType="TreeViewItem">
                        <Setter Property="IsExpanded" Value="{Binding Source={StaticResource vm}, Path=IsExpanded}" />
                    </Style>
                </TreeView.ItemContainerStyle>
            </TreeView>
        </Grid>
    </Window>

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    
    namespace SQLtoExcel
    {
        /// <summary>
        /// Interaktionslogik für Window3.xaml
        /// </summary>
        public partial class Window3 : Window
        {
            public Window3()
            {
                InitializeComponent();
            }
    
        }
    
        public class VM : INotifyPropertyChanged
        {
            private bool _isExpanded = true;
    
            public bool IsExpanded
            {
                get { return this._isExpanded; }
                set
                {
                    this._isExpanded = value;
                    OnPropertyChanged(string.Empty);
                }
            }
    
            private CollectionViewSource cvs = new CollectionViewSource();
    
            public ICollectionView View
            {
                get
                {
                    if (cvs.Source == null) cvs.Source = GetData();
                    return cvs.View;
                }
            }
    
            private ObservableCollection<Person> GetData()
            {
                ObservableCollection<Person> col = new ObservableCollection<Person>();
                Person person1 = new Person() { Name = "Schmidt", Age = 42 };
                Person person2 = new Person() { Name = "Meyer", Age = 39 };
                Person person3 = new Person() { Name = "Schulze", Age = 25 };
    
                PersonEigenschaft child1 = new PersonEigenschaft { Sternzeichen = "Widder", Lieblingsfarbe = System.Windows.Media.Brushes.Red };
                PersonEigenschaft child2 = new PersonEigenschaft { Sternzeichen = "Schütze", Lieblingsfarbe = System.Windows.Media.Brushes.Blue };
                person1.Children.Add(child1);
                person2.Children.Add(child2);
    
                col.Add(person1);
                col.Add(person2);
                col.Add(person3);
                return col;
            }
    
            #region  OnPropertyChanged
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged([CallerMemberName] string propName = "") =>
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
            #endregion
        }
    
        public class Person
        {
            public string Name { get; set; }
    
            public int Age { get; set; }
    
            public ObservableCollection<PersonEigenschaft> Children { get; set; } = new ObservableCollection<PersonEigenschaft>();
        }
    
        public class PersonEigenschaft
        {
            public string Sternzeichen { get; set; }
    
            public System.Windows.Media.SolidColorBrush Lieblingsfarbe { get; set; }
        }
    
    }

    Vielen Dank für deine Lösung und für die Mühe, eine komplettes Beispiel auszuarbeiten!!!





    • Bearbeitet perlfred Samstag, 5. Mai 2018 05:21
    Donnerstag, 3. Mai 2018 16:53