none
TreeView Baumstruktur RRS feed

  • Frage

  • Hallo

    Ich komme leider nicht weiter ich möchte eine einfache TreeView Baumstruktur erstellen.

    Es soll so sein das ich 3 Variablen habe.

    ID

    ChildrenID

    Name

    Die Variablen habe ich nun so in C#

            public Int32 TreeViewRootID { get; set; }
            public Int32 TreeViewRootChildrenID { get; set; }
    
            public string TreeViewName { get; set; }

    Der Aufbau sollte dann so sein

    1, 0, Root1

    2, 1, Children1

    3, 2, Children 1.1

    4, 3, Children 1.1.1

    5, 0, Root2

    6, 5, Children 2

    usw.

    Meine Vermutung für den XAML wäre so.

        <TreeView ItemsSource="{Binding RootItems}">
          <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
              <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
              <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
              <Setter Property="FontWeight" Value="Normal" />
              <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                  <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
              </Style.Triggers>
            </Style>
          </TreeView.ItemContainerStyle>
    
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}">
              <TextBlock Text="{Binding TreeViewName}" />
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
        </TreeView>

    Das habe ich aus einer Seite gefunden.

    Wie erstellt man ein ganz einfaches ViewModels so das man den TreeView so füllen kann.

    MyTreeView.Items.Add(1,0,"Root1");

    und wenn man es in TreeView es markiert soll ein Messenbox erscheinen der mit alle 3 Varianten anzeigt.

    Hoffe mir kann dabei jemand das gut erklären.

    Sonntag, 10. Januar 2021 08:10

Alle Antworten

  • Hi,
    Ich habe dir mal eine kleine Demo zu deinen Fragen erstellt. Im ViewModel wird zu Demonstrationszwecken eine Knotenliste entsprechend deinen Vorgaben erzeugt und zur Anzeige im TreeView gebracht. Für den vom Anwender selektierten Knoten werden im rechten Teil des Fensters die Eigenschaften des Datenobjektes des ausgewählten Knotens angezeigt und derer Werte können verändert werden. Der selektierte Knoten wird mittels Behavior aus dem NuGet-Paket "System.Windows.Interactivity" gefangen und in den ViewModel eingetragen (Details-Eigenschaft). Damit diese Änderung auch in der Oberfläche sichtbar wird, ist INotifyPropertyChanged implementiert.

    XAML:

    <Window x:Class="WpfApp1.Window027"
            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:WpfApp027"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            mc:Ignorable="d"
            Title="Window027" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TreeView ItemsSource="{Binding RootItems}">
          <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
              <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
              <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
              <Setter Property="FontWeight" Value="Normal" />
              <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                  <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
              </Style.Triggers>
            </Style>
          </TreeView.ItemContainerStyle>
          <i:Interaction.Behaviors>
            <local:TreeViewBehavior/>
          </i:Interaction.Behaviors>
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}">
              <TextBlock Text="{Binding TreeViewName}" />
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
        </TreeView>
        <Grid Grid.Column="1" DataContext="{Binding Detail}">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Grid.Column="0" Content="TreeViewRootID: " HorizontalAlignment="Right"/>
          <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding TreeViewRootID}" IsReadOnly="True"/>
          <Label Grid.Row="1" Grid.Column="0" Content="TreeViewName: " HorizontalAlignment="Right"/>
          <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TreeViewName}"/>
        </Grid>
      </Grid>
    </Window>

    ViewModel:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Interactivity;
    
    namespace WpfApp027
    {
      public class ViewModel : INotifyPropertyChanged
      {
        public ViewModel()
        {
          Random rnd = new Random();
          ObservableCollection<Data> col = new ObservableCollection<Data>();
          for (int i = 1; i < 100; i++)
          {
            Data d = new Data(col) { TreeViewRootID = i, TreeViewName = $"Node {i}", TreeViewRootChildrenID = rnd.Next(0, col.Count) };
            col.Add(d);
          }
          cvs.Source = col.Where((d) => d.TreeViewRootChildrenID == 0);
        }
    
        private CollectionViewSource cvs = new CollectionViewSource();
        public ICollectionView RootItems { get => cvs.View; }
    
        private Data _details = null;
        public Data Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    
      public class Data
      {
        public Data(ObservableCollection<Data> col) => this.col = col;
        ObservableCollection<Data> col;
        public Int32 TreeViewRootID { get; set; }
        public Int32 TreeViewRootChildrenID { get; set; }
        public string TreeViewName { get; set; }
        public ObservableCollection<Data> ChildrenItems { get => new ObservableCollection<Data>(col.Where((d) => d.TreeViewRootChildrenID == this.TreeViewRootID)); }
    
        public bool IsExpanded { get; set; } = true;
        public bool IsSelected { get; set; }
    }
    
      public class TreeViewBehavior : Behavior<TreeView>
      {
        protected override void OnAttached() => AssociatedObject.SelectedItemChanged += AssociatedObject_SelectedItemChanged;
    
        private void AssociatedObject_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
          var tv = sender as TreeView;
          if (tv == null) return;
          var vm = tv.DataContext as ViewModel;
          if (vm == null) return;
          vm.Detail = tv.SelectedItem as Data;
        }
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 10. Januar 2021 13:17
  • Hallo

    Danke für deine Antwort werde es heute Abend testen.

    Nun wie kommt man auf diesen Code

    Dieser Code ist klar das ist für füllen der Daten:

          Random rnd = new Random();
          ObservableCollection<Data> col = new ObservableCollection<Data>();
          for (int i = 1; i < 100; i++)
          {
            Data d = new Data(col) { TreeViewRootID = i, TreeViewName = $"Node {i}", TreeViewRootChildrenID = rnd.Next(0, col.Count) };
            col.Add(d);
          }
          cvs.Source = col.Where((d) => d.TreeViewRootChildrenID == 0);

    für was ist dieser Code?

       private CollectionViewSource cvs = new CollectionViewSource();
        public ICollectionView RootItems { get => cvs.View; }
    
        private Data _details = null;
        public Data Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    


    Für das ist das?

        public Data(ObservableCollection<Data> col) => this.col = col;
        ObservableCollection<Data> col;

    und für was ist das?

        public ObservableCollection<Data> ChildrenItems { get => new ObservableCollection<Data>(col.Where((d) => d.TreeViewRootChildrenID == this.TreeViewRootID)); }
    

    geht dieses Model noch einfacher?

    Ich hab gelesen das es mit dem MenuItems vielleicht auch funktionieren würde. Wäre das einfacher?

    Sonntag, 10. Januar 2021 15:58
  • Perfekt das funktioniert Super leider bin ich kein Fan von Nut-Get Paket

    Immer wenn ich mein PC auf Werkeinstellung und Visual Studio neu installiere habe ich immer Probleme das ich meine Projekte nicht mehr öffnen kann selbst wenn ich das Nut-Get Paket danach Installiere.

    das gehört jetzt nicht zum Thema.

    Ich schaue jetzt dein Code genauer an und hoffe das ich diese auch Verstehe. Diesen Beitrag bitte noch nicht schließen ich denke ich habe dazu noch ein paar Fragen.

    MfG.

    Mezzo 

    Sonntag, 10. Januar 2021 16:39
  • Hi,
    hast du den Code mal ausprobiert? Er kann ohne Änderungen genutzt werden. Lediglich, wenn du andere Namensräume und Fensternamen nutzt, musst du dies anpassen.

    Wenn der Code dann läuft, kannst du Haltepunkte setzen und sehen, bei welchen Aktivitäten welche Codeabschnitte durchlaufen werden.

    Zu deinen Frage:

    1. Füllen der Daten: es wird eine Liste von Datenobjekte vom Typ Data erzeugt. Jedes Datenobjekt bekommt im Constructor einen Verweis auf die Liste der Datenobjekte ("col"). Die Elemente der Liste, die TreeViewRootChildrenID=0 haben werden nur bereitgestellt (Root-Ebene).

    2a. RootItems: Das ist die Eigenschaft im ViewModel, die du in deinem Codeschnipsel für die Bindung der im TreeView anzuzeigenden Knoten vorgegeben hast. 

    2b. Details: darin wird das Datenobjekt des aktuell angeklickten Knotens gehalten. OnPropertyChanged bewirkt eine Mitteilung an die Oberfläche, damit sich diese die aktuellen Daten für die Anzeige und Bearbeitung holt (nach Anklicken eines anderen Knotens). Genutzt wird Details als DataContext im rechten Teil des Windows.

    3. public Data(... ist ein Constructor in der Datenklasse, mit welchem ein Verweis auf die Liste des Datenobjekte übergeben wird. Diese Liste wird dann gefiltert, um die einem Knoten untergeordneten Knoten zur Anzeige zu bringen.

    4. ChildrenItems ist die Eigenschaft, die du in deinem Ausgangsposting für die Liste der untergeordneten Knoten vorgegeben hast.

    Ich habe das ViewModel schon sehr einfach gestaltet. Das Laden der Collection für die CollectionViewSource (im Constructor des ViewModels) könnte man in ein Model auslagern, um z.B. anstelle der generierten zufälligen Testdaten diese Daten aus einer externen Datenquelle zu laden.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 10. Januar 2021 16:45
  • Hallo ja dein Code Funktioniert und hab es schon ein mehr verstanden.

    Ich habe das jetzt mal ein Bissen erweitert und schon Funktioniert es nicht mehr. Nun zu meinem Aufbau.

    Ich habe ein DataSet hinzugefügt mit dem Namen DataSet1.xsd

    Da habe ich nun 3 Tabellen drin und sind miteinander Verbunden. Das Ziel ist es wenn man bei TreeView einen Knoten Markiert das es in DataGrid nur die Daten anzeigt die mit dem Knoten verbunden ist. Das habe ich mit der Tabelle "Zuordnung" gemacht.

    Leider kann ich noch kein Bild im Beitrag einfügen weil mein Konto noch geprüft werden muss

    Ich schreibe die 3 Tabellennamen mit Spalten auf

    TreeViewtabelle

    --- ID   int32      Schlüssen Primar

    --- TreeViewRootID  int32

    --- TreeViewRootChildrenID   int32

    --- TreeViewName   string

    DataGridTabelle

    --- ID   int32      Schlüssen Primar

    --- GridColumn1ID  int32

    --- GridColumn2Text1   string

    --- GridColumn3Text2   string

    Zuordnung

    --- ID    int32      Schlüssen Primar

    --- ZuTreeViewRootID    int32

    --- ZuGridColumn1ID     int32

    Hoffe man versteht die DataSet auch ohne Bild

    MainWindow.xaml

    <Window x:Class="TreeView_DataGrid_Test.MainWindow"
            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:TreeView_DataGrid_Test"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Window.DataContext>
            <local:ViewModel/>
        </Window.DataContext>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TreeView ItemsSource="{Binding RootItems}">
                <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                        <Setter Property="FontWeight" Value="Normal" />
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="FontWeight" Value="Bold" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </TreeView.ItemContainerStyle>
                <i:Interaction.Behaviors>
                    <local:TreeViewBehavior/>
                </i:Interaction.Behaviors>
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}">
                        <TextBlock Text="{Binding TreeViewName}" />
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
            <Grid Grid.Column="1" DataContext="{Binding Detail}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Label Grid.Row="0" Grid.Column="0" Content="TreeViewRootID: " HorizontalAlignment="Right"/>
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding TreeViewRootID}" IsReadOnly="True"/>
                <Label Grid.Row="1" Grid.Column="0" Content="TreeViewName: " HorizontalAlignment="Right"/>
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TreeViewName}"/>
                <Label Grid.Row="2" Grid.Column="0" Content="ColumnID: " HorizontalAlignment="Right" Grid.ColumnSpan="2" Margin="0,5,193,336"/>
                <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Column1ID}" IsReadOnly="True" Margin="0,5,0,336"/>
                <Label Grid.Row="2" Grid.Column="0" Content="ColumnText: " HorizontalAlignment="Right" Margin="0,36,0,305"/>
                <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Column2Text1}" Margin="0,36,0,305"/>
                <DataGrid x:Name="DataGrid1" ItemsSource="{Binding DataGridTabelle}" Margin="0,84,0,0" Grid.Row="2" Grid.ColumnSpan="2">
                    <DataGrid.Columns>
    
                        <DataGridTextColumn Width="100" Binding="{Binding GridColumn1ID}" Header="Column1ID"/>
                        <DataGridTextColumn Width="100" Binding="{Binding GridColumn2Text1}" Header="Column2Text1"/>
                        <DataGridTextColumn Width="100" Binding="{Binding GridColumn3Text2}" Header="Column3Text2"/>
                        
                    </DataGrid.Columns>
                </DataGrid>
    
    
            </Grid>
        </Grid>
    </Window>

    Das ViewModel habe ich in einer Klasser ViewModel_TreeView.cs

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Interactivity;
    
    namespace TreeView_DataGrid_Test
    {
        class ViewModel_TreeView
        {
        }
    
        public class ViewModel : INotifyPropertyChanged
        {
            
    
            public ViewModel()
            {
                DataSet1 DatenTabelle = new DataSet1();
    
                DatenTabelle.TreeViewTabelle.Rows.Add(1, 100, 0, "Root1");
                DatenTabelle.TreeViewTabelle.Rows.Add(2, 101, 0, "Root2");
                DatenTabelle.TreeViewTabelle.Rows.Add(3, 102, 0, "Root3");
                DatenTabelle.TreeViewTabelle.Rows.Add(4, 103, 100, "Children1");
                DatenTabelle.TreeViewTabelle.Rows.Add(5, 104, 100, "Children2");
                DatenTabelle.TreeViewTabelle.Rows.Add(6, 105, 104, "Children2.1");
                DatenTabelle.TreeViewTabelle.Rows.Add(7, 106, 101, "Children1");
                DatenTabelle.TreeViewTabelle.Rows.Add(8, 107, 106, "Children1.1");
                DatenTabelle.TreeViewTabelle.Rows.Add(9, 108, 107, "Children1.1.1");
    
                DatenTabelle.DataGridTabelle.Rows.Add(1, 100, "Test1", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(2, 101, "Test2", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(3, 102, "Test3", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(4, 103, "Test4", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(5, 104, "Test5", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(6, 105, "Test6", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(7, 106, "Test7", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(8, 107, "Test8", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(9, 108, "Test9", "Test1");
                DatenTabelle.DataGridTabelle.Rows.Add(10, 109, "Test10", "Test1");
    
                DatenTabelle.Zuordnung.Rows.Add(1, 103, 100);
                DatenTabelle.Zuordnung.Rows.Add(2, 103, 101);
                DatenTabelle.Zuordnung.Rows.Add(3, 103, 102);
                DatenTabelle.Zuordnung.Rows.Add(4, 105, 103);
                DatenTabelle.Zuordnung.Rows.Add(5, 105, 104);
                DatenTabelle.Zuordnung.Rows.Add(6, 105, 105);
                DatenTabelle.Zuordnung.Rows.Add(7, 105, 100);
                DatenTabelle.Zuordnung.Rows.Add(8, 101, 107);
                DatenTabelle.Zuordnung.Rows.Add(9, 101, 108);
    
    
    
                //Random rnd = new Random();
                //ObservableCollection<Data> col = new ObservableCollection<Data>();
                //for (int i = 1; i < 100; i++)
                //{
                //    Data d = new Data(col) { TreeViewRootID = i, TreeViewName = $"Node {i}", TreeViewRootChildrenID = rnd.Next(0, col.Count) };
    
                //    col.Add(d);
                //}
                //cvs.Source = col.Where((d) => d.TreeViewRootChildrenID == 0);
    
                ObservableCollection<Data> col = new ObservableCollection<Data>();
                for (int i = 1; DatenTabelle.TreeViewTabelle.Rows.Count; i++)
                {
                    Data d = new Data(col) { TreeViewRootID = DatenTabelle.TreeViewTabelle.Rows[i].ItemArray[1], TreeViewName = DatenTabelle.TreeViewTabelle.Rows[i].ItemArray[3], TreeViewRootChildrenID = DatenTabelle.TreeViewTabelle.Rows[i].ItemArray[2] };
                    col.Add(d);
    
                }
                cvs.Source = col.Where((d) => d.TreeViewRootChildrenID == 0);
    
            }
    
            public DataSet1 DatenGrid { set; get; }
    
    
            private CollectionViewSource cvs = new CollectionViewSource();
            public ICollectionView RootItems { get => cvs.View; }
    
            private Data _details = null;
            public Data Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } }
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    
        public class Data
        {
            public Data(ObservableCollection<Data> col) => this.col = col;
            ObservableCollection<Data> col;
            public Int32 TreeViewRootID { get; set; }
            public Int32 TreeViewRootChildrenID { get; set; }
            public string TreeViewName { get; set; }
            public ObservableCollection<Data> ChildrenItems { get => new ObservableCollection<Data>(col.Where((d) => d.TreeViewRootChildrenID == this.TreeViewRootID)); }
    
            public bool IsExpanded { get; set; } = false;
            public bool IsSelected { get; set; }
        }
    
        public class TreeViewBehavior : Behavior<TreeView>
        {
            protected override void OnAttached() => AssociatedObject.SelectedItemChanged += AssociatedObject_SelectedItemChanged;
    
            private void AssociatedObject_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
            {
                var tv = sender as TreeView;
                if (tv == null) return;
                var vm = tv.DataContext as ViewModel;
                if (vm == null) return;
                vm.Detail = tv.SelectedItem as Data;
            }
        }
    }
    

    Ich weiß die Befüllung von die Tabellen in DataSet ist nicht grad das beste. Das ist hier jetzt nur ein Beispiel. Normal werden die Tabellen von der Datenbank mit while Schleife befüllt.

    Ich Arbeite gerne mit DataSet, da ich die Tabellen schön anlegen kann und die Tabellen mit einander verbinden kann. So das wenn man in Datagrid was auswählt Automatisch die Daten angezeigt werden die in einer anderen Tabelle verbunden sind.

    Nur bekomme ich das mit WPF nicht so hin. Was habe ich da Falsch?

    Sonntag, 10. Januar 2021 22:46
  • HI,
    anstelle des von mit genutzten NuGet-Pakets "System.Windows.Interactivity" kannst du auch die Funktionalität mit einem eigenen "attached behavior" realisieren. Da wird eine statische DependencyProperty genutzt, mit der während der Instanziierung (im konkreten Fall TreeView) der Verweis auf die TreeView-Instanz "gefangen" wird und damit das SelectedItemChanged Ereignis genutzt werden kann. Diese Technologie ist erforderlich, wenn das MVVM Entwurfsmuster genutzt werden soll, da in diesem Fall der ViewModel die View nicht kennt.

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Montag, 11. Januar 2021 05:35
  • Hi,
    hier mal ein Beispiel mit einem DataSet. Der Übersichtlichkeit halber, damit das Prinzip deutlicher zu erkennen ist, habe ich untypisiertes DataSet genutzt. Mit einem typisierten DataSet (wie bei dir) vereinfacht sich das Ganze etwas. Außerdem habe ich anstelle des NuGet-Paketes eine DependencyProperty eingebaut.

    XAML:

    <Window x:Class="WpfApp1.Window028"
            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:WpfApp028"
            mc:Ignorable="d"
            Title="Window027" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TreeView ItemsSource="{Binding RootItems}" local:ViewModel.TreeViewBevior="True">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
              <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
              <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
              <Setter Property="FontWeight" Value="Normal" />
              <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                  <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
              </Style.Triggers>
            </Style>
          </TreeView.ItemContainerStyle>
          <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}">
              <TextBlock Text="{Binding TreeViewName}" />
            </HierarchicalDataTemplate>
          </TreeView.ItemTemplate>
        </TreeView>
        <Grid Grid.Column="1" DataContext="{Binding Detail}">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Grid.Column="0" Content="TreeViewRootID: " HorizontalAlignment="Right"/>
          <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding TreeViewRootID}" IsReadOnly="True"/>
          <Label Grid.Row="1" Grid.Column="0" Content="TreeViewName: " HorizontalAlignment="Right"/>
          <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TreeViewName}"/>
          <DataGrid Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding DataGridTabelle}"/>
        </Grid>
      </Grid>
    </Window>

    Klassen:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Data;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace WpfApp028
    {
      public class ViewModel : INotifyPropertyChanged
      {
        public ViewModel()
        {
          Model m = new Model();
          var ds = m.DatenTabelle;
          ObservableCollection<Data> col = new ObservableCollection<Data>();
          foreach (DataRow row in ds.Tables["TreeViewTabelle"].Rows) col.Add(new Data(row));
          cvs.Source = col.Where((d) => d.TreeViewRootChildrenID == 0);
        }
    
        private CollectionViewSource cvs = new CollectionViewSource();
        public ICollectionView RootItems { get => cvs.View; }
    
        private Data _details = null;
        public Data Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } }
    
        #region attached behavior
    
        public static readonly DependencyProperty TreeViewBeviorProperty =
          DependencyProperty.RegisterAttached("TreeViewBevior",
            typeof(bool), typeof(TreeView), new UIPropertyMetadata(false, OnTreeViewBevior));
        public static bool GetTreeViewBevior(DependencyObject obj) => (bool)obj.GetValue(TreeViewBeviorProperty);
        public static void SetTreeViewBevior(DependencyObject obj, bool value) => obj.SetValue(TreeViewBeviorProperty, value);
        private static void OnTreeViewBevior(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
          var tv = depObj as TreeView;
          if (tv == null) return;
          if ((e.NewValue is bool) && (bool)(e.NewValue)) tv.SelectedItemChanged += OnLblMouseDown; else tv.SelectedItemChanged -= OnLblMouseDown;
        }
    
        private static void OnLblMouseDown(Object sender, EventArgs e)
        {
          var tv = sender as TreeView;
          if (tv == null) return;
          var vm = tv.DataContext as ViewModel;
          if (vm == null) return;
          vm.Detail = tv.SelectedItem as Data;
        }
    
        #endregion
    
        #region OnPropertyChanged
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    
        #endregion
      }
    
      public class Data
      {
        public Data(DataRow row) { this.row = row; this.dt = row.Table; this.ds = row.Table.DataSet; }
        DataSet ds;
        DataTable dt;
        DataRow row;
        public Int32 TreeViewRootID { get => row.Field<int>("TreeViewRootID"); set { row["TreeViewRootID"] = value; } }
        public Int32 TreeViewRootChildrenID { get => row.Field<int>("TreeViewRootChildrenID"); set { row["TreeViewRootChildrenID"] = value; } }
        public string TreeViewName { get => row.Field<string>("TreeViewName"); set { row["TreeViewName"] = value; } }
        public object ChildrenItems => new ObservableCollection<Data>(from d in dt.AsEnumerable() where d.Field<int>("TreeViewRootChildrenID") == this.TreeViewRootID select new Data(d));
        public DataView DataGridTabelle
        {
          get
          {
            DataTable dt2 = ds.Tables["DataGridTabelle"].Clone();
            var dv = new DataView(ds.Tables["DataGridTabelle"]);
            var req = from dg in ds.Tables["DataGridTabelle"].AsEnumerable()
                      join zu in ds.Tables["Zuordnung"].AsEnumerable()
                        on dg.Field<int>("GridColumn1ID") equals zu.Field<int>("ZuGridColumn1ID")
                      where zu.Field<int>("ZuTreeViewRootID") == TreeViewRootID
                      select dg;
            foreach (var item in req) dt2.Rows.Add(item.ItemArray);
            return dt2.AsDataView();
          }
        }
        public bool IsExpanded { get; set; } = true;
        public bool IsSelected { get; set; }
      }
    
      public class Model
      {
        public Model()
        {
          // generate untyped DataSet
          DatenTabelle = new DataSet();
          DataTable dtTV = new DataTable("TreeViewTabelle");
          DatenTabelle.Tables.Add(dtTV);
          dtTV.Columns.Add("ID", typeof(int));
          dtTV.Columns.Add("TreeViewRootID", typeof(int));
          dtTV.Columns.Add("TreeViewRootChildrenID", typeof(int));
          dtTV.Columns.Add("TreeViewName", typeof(string));
          DataTable dtDG = new DataTable("DataGridTabelle");
          DatenTabelle.Tables.Add(dtDG);
          dtDG.Columns.Add("ID", typeof(int));
          dtDG.Columns.Add("GridColumn1ID", typeof(int));
          dtDG.Columns.Add("GridColumn2Text1", typeof(string));
          dtDG.Columns.Add("GridColumn3Text2", typeof(string));
          DataTable dtZO = new DataTable("Zuordnung");
          DatenTabelle.Tables.Add(dtZO);
          dtZO.Columns.Add("ID", typeof(int));
          dtZO.Columns.Add("ZuTreeViewRootID", typeof(int));
          dtZO.Columns.Add("ZuGridColumn1ID", typeof(int));
          // load data
          LoadData();
        }
    
        public DataSet DatenTabelle { get; set; }
    
        private void LoadData()
        {
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(1, 100, 0, "Root1");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(2, 101, 0, "Root2");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(3, 102, 0, "Root3");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(4, 103, 100, "Children1");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(5, 104, 100, "Children2");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(6, 105, 104, "Children2.1");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(7, 106, 101, "Children1");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(8, 107, 106, "Children1.1");
          DatenTabelle.Tables["TreeViewTabelle"].Rows.Add(9, 108, 107, "Children1.1.1");
    
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(1, 100, "Test1", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(2, 101, "Test2", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(3, 102, "Test3", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(4, 103, "Test4", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(5, 104, "Test5", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(6, 105, "Test6", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(7, 106, "Test7", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(8, 107, "Test8", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(9, 108, "Test9", "Test1");
          DatenTabelle.Tables["DataGridTabelle"].Rows.Add(10, 109, "Test10", "Test1");
    
          DatenTabelle.Tables["Zuordnung"].Rows.Add(1, 103, 100);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(2, 103, 101);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(3, 103, 102);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(4, 105, 103);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(5, 105, 104);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(6, 105, 105);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(7, 105, 100);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(8, 101, 107);
          DatenTabelle.Tables["Zuordnung"].Rows.Add(9, 101, 108);
        }
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Montag, 11. Januar 2021 11:12