Benutzer mit den meisten Antworten
TreeView mit mehrere Daten

Frage
-
Hallo
Ich bin immer noch dabei den TreeView richtig zu lernen. Den Baumstruktur kann ich jetzt schon sehr gut.
Ich habe meine Daten für TreeView so geteilt
TreeView-Nummer
TreeView-UnterNummer
TreeView-Name
Das mein TreeView mit die Daten richtig angezeigt wird funktioniert schon mal sehr gut.
Nun möchte ich aber das wenn man mit der Linken Maustaste auf ein Konten Klickt. das ein MessageBox erscheint und er mir dann die TreeView_Nummer, TreeView_UnterNummer und den TreeView_Name ausgibt der ausgewählt ist.
Weiß leider nicht wie ich das anstellen soll.
Hier schon mal mein Code
XAML:
<Window x:Class="TreeView.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" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <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="ExtraBold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding Row._TreeView_Name}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window>
CS-Code
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace TreeView { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { DataSet1 DataSet_TreeView = new DataSet1(); public MainWindow() { InitializeComponent(); this.DataContext = this; DataSet_TreeView.Data_Treeview.Rows.Add("1", "10", "", "test0"); DataSet_TreeView.Data_Treeview.Rows.Add("2", "11", "", "test1"); DataSet_TreeView.Data_Treeview.Rows.Add("3", "12", "10", "test01"); DataSet_TreeView.Data_Treeview.Rows.Add("1", "13", "12", "test02"); ObservableCollection<Data_Treeview> col = new ObservableCollection<Data_Treeview>(); foreach (DataRow row in DataSet_TreeView.Tables["Data_Treeview"].Rows) col.Add(new Data_Treeview((DataSet1.Data_TreeviewRow)row)); cvs.Source = col.Where((d) => d.TreeViewUnterNummer == ""); } private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } } public class Data_Treeview { public Data_Treeview(DataSet1.Data_TreeviewRow row) { this.Row = row; this.dt = (DataSet1.Data_TreeviewDataTable)row.Table; this.ds = (DataSet1)row.Table.DataSet; } DataSet1 ds; DataSet1.Data_TreeviewDataTable dt; public DataSet1.Data_TreeviewRow Row { get; set; } public string TreeViewNummer { get => Row._TreeView_Nummer; set { Row._TreeView_Nummer = value; } } public string TreeViewUnterNummer { get => Row._TreeView_UnterNummer; set { Row._TreeView_UnterNummer = value; } } public string TreeViewName { get => Row._TreeView_Name; set { Row._TreeView_Name = value; } } public object ChildrenItems => new ObservableCollection<Data_Treeview>(from d in dt where d._TreeView_UnterNummer == this.TreeViewNummer select new Data_Treeview(d)); public bool IsExpanded { get; set; } = true; public bool IsSelected { get; set; } } }
Wäre super wenn mir einer ein kleines Beispiel geben könnte.
Gruß
Mezzo
Antworten
-
Hallo Mezzo,
man kann im TreeViewItem-Style EventSetter unterbringen. Im C# kannst du im Handler eine MessageBox anzeigen. Dabei kannst du den Tag des 'sender' auswerten:
<Style TargetType="TreeViewItem"> <Setter Property="Tag" Value="{Binding}" /> <EventSetter Event="MouseLeftButtonUp" Handler="treeViewItem_MouseLeftButtonUp" /> </Style>
private void treeViewItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { // Achtung!
Gruß
// Das Event wird für das Element, auf dem geklickt wurde,
// und für all seine Parent-Elemente bis einschließlich des Root-Knotens aufgerufen.
// Diese weiteren Aufrufe können unterbunden werden, indem 'e.Handled = true' gesetzt wird.
e.Handled = true; Data_Treeview item = (sender as TreeViewItem)?.Tag as Data_Treeview; if (item == null) return; // MessageBox anzeigen... }
Heiko- Bearbeitet Heiko65456465 Dienstag, 10. August 2021 15:37
- Als Antwort markiert Mezzo80 Samstag, 14. August 2021 10:52
-
Das SelectionChanged-Ereignis zu verwenden, geht auch.
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { Data_Treeview item = treeStoreDirectories.SelectedItem as Data_Treeview; if (item == null) return; MessageBox.Show("Nummer: " + item.TreeViewNummer);
}
- Als Antwort markiert Mezzo80 Sonntag, 15. August 2021 09:42
-
Hallo,
TreeViewRoot ist der Name des TreeView. Da du in deinem obigen Beispiel folgende Zeile schriebst:
<TreeView x:Name="TreeViewRoot" ItemsSource="{Binding RootItems}" SelectedItemChanged="TreeView_SelectedItemChanged">
... ist innerhalb des MainWindow im C# eine Variable TreeViewRoot vorhanden, über die du auf den TreeView zugreifen kannst.
Ist TreeViewRoot nicht in deiner MainPage verfügbar, hast dur vermutlich aktuell keinen Namen mehr für den TreeView im XAML vergeben.
Falls der Name im XAML dennoch vergeben wurde, kannst du mal probiren, das 'x:Name' durch 'Name' zu ersetzen, oder caste den 'sender' zu TreeView:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { TreeView treeView = sender as TreeView; Data_Treeview item = treeView.SelectedItem as Data_Treeview; if (item == null) return; MessageBox.Show("Nummer: " + item.TreeViewNummer); }
Sollte dein TreeView innerhalb einer Auflistung existieren (z.B. als Eintrag in einer ComboBox), kann man nicht mehr über einen eventuell vergebenen Namen auf ihn zugreifen. Dann sollte man den 'sender' im Event-Handler zu TreeView casten.
Gruß
Heiko
- Bearbeitet Heiko65456465 Dienstag, 17. August 2021 12:01
- Als Antwort markiert Mezzo80 Samstag, 11. September 2021 09:20
Alle Antworten
-
Hallo Mezzo,
man kann im TreeViewItem-Style EventSetter unterbringen. Im C# kannst du im Handler eine MessageBox anzeigen. Dabei kannst du den Tag des 'sender' auswerten:
<Style TargetType="TreeViewItem"> <Setter Property="Tag" Value="{Binding}" /> <EventSetter Event="MouseLeftButtonUp" Handler="treeViewItem_MouseLeftButtonUp" /> </Style>
private void treeViewItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { // Achtung!
Gruß
// Das Event wird für das Element, auf dem geklickt wurde,
// und für all seine Parent-Elemente bis einschließlich des Root-Knotens aufgerufen.
// Diese weiteren Aufrufe können unterbunden werden, indem 'e.Handled = true' gesetzt wird.
e.Handled = true; Data_Treeview item = (sender as TreeViewItem)?.Tag as Data_Treeview; if (item == null) return; // MessageBox anzeigen... }
Heiko- Bearbeitet Heiko65456465 Dienstag, 10. August 2021 15:37
- Als Antwort markiert Mezzo80 Samstag, 14. August 2021 10:52
-
Hallo
Danke für deine Antwort würde das nicht auch mit SelectedItemChanged gehen?
Ich habe das mal getestet, so kann ich des MouseLeftButtonUp für was anderes verwenden.
Meine frage ist nun wie lese ich jetzt aus die Variablen den Inhalt aus?
TreeViewNummer
TreeViewUnterNummer
TreeViewNameHier nochmal mein Code wie er jetzt ist.
XAML:
<Window x:Class="TreeView.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" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <TreeView x:Name="TreeViewRoot" ItemsSource="{Binding RootItems}" SelectedItemChanged="TreeView_SelectedItemChanged"> <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="Tag" Value="{Binding}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="ExtraBold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}"> <Grid> <TextBlock x:Name="TreeViewTextBlock_Name" Text="{Binding Row._TreeView_Name}" /> <TextBlock x:Name="TreeViewTextBlock_Nummer" Text="{Binding Row._TreeView_Nummer}" Visibility="Hidden"/> </Grid> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window>
C#:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace TreeView { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { DataSet1 DataSet_TreeView = new DataSet1(); ObservableCollection<Data_Treeview> col = new ObservableCollection<Data_Treeview>(); public MainWindow() { InitializeComponent(); this.DataContext = this; DataSet_TreeView.Data_Treeview.Rows.Add("1", "10", "", "test0"); DataSet_TreeView.Data_Treeview.Rows.Add("2", "11", "", "test1"); DataSet_TreeView.Data_Treeview.Rows.Add("3", "12", "10", "test01"); DataSet_TreeView.Data_Treeview.Rows.Add("1", "13", "12", "test02"); foreach (DataRow row in DataSet_TreeView.Tables["Data_Treeview"].Rows) col.Add(new Data_Treeview((DataSet1.Data_TreeviewRow)row)); cvs.Source = col.Where((d) => d.TreeViewUnterNummer == ""); } private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { MessageBox.Show(sender.ToString()); } } public class Data_Treeview { public Data_Treeview(DataSet1.Data_TreeviewRow row) { this.Row = row; this.dt = (DataSet1.Data_TreeviewDataTable)row.Table; this.ds = (DataSet1)row.Table.DataSet; } DataSet1 ds; DataSet1.Data_TreeviewDataTable dt; public DataSet1.Data_TreeviewRow Row { get; set; } public string TreeViewNummer { get => Row._TreeView_Nummer; set { Row._TreeView_Nummer = value; } } public string TreeViewUnterNummer { get => Row._TreeView_UnterNummer; set { Row._TreeView_UnterNummer = value; } } public string TreeViewName { get => Row._TreeView_Name; set { Row._TreeView_Name = value; } } public object ChildrenItems => new ObservableCollection<Data_Treeview>(from d in dt where d._TreeView_UnterNummer == this.TreeViewNummer select new Data_Treeview(d)); public bool IsExpanded { get; set; } = true; public bool IsSelected { get; set; } } }
-
Das SelectionChanged-Ereignis zu verwenden, geht auch.
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { Data_Treeview item = treeStoreDirectories.SelectedItem as Data_Treeview; if (item == null) return; MessageBox.Show("Nummer: " + item.TreeViewNummer);
}
- Als Antwort markiert Mezzo80 Sonntag, 15. August 2021 09:42
-
Hallo,
TreeViewRoot ist der Name des TreeView. Da du in deinem obigen Beispiel folgende Zeile schriebst:
<TreeView x:Name="TreeViewRoot" ItemsSource="{Binding RootItems}" SelectedItemChanged="TreeView_SelectedItemChanged">
... ist innerhalb des MainWindow im C# eine Variable TreeViewRoot vorhanden, über die du auf den TreeView zugreifen kannst.
Ist TreeViewRoot nicht in deiner MainPage verfügbar, hast dur vermutlich aktuell keinen Namen mehr für den TreeView im XAML vergeben.
Falls der Name im XAML dennoch vergeben wurde, kannst du mal probiren, das 'x:Name' durch 'Name' zu ersetzen, oder caste den 'sender' zu TreeView:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { TreeView treeView = sender as TreeView; Data_Treeview item = treeView.SelectedItem as Data_Treeview; if (item == null) return; MessageBox.Show("Nummer: " + item.TreeViewNummer); }
Sollte dein TreeView innerhalb einer Auflistung existieren (z.B. als Eintrag in einer ComboBox), kann man nicht mehr über einen eventuell vergebenen Namen auf ihn zugreifen. Dann sollte man den 'sender' im Event-Handler zu TreeView casten.
Gruß
Heiko
- Bearbeitet Heiko65456465 Dienstag, 17. August 2021 12:01
- Als Antwort markiert Mezzo80 Samstag, 11. September 2021 09:20
-
Hallo
ich habe nun bei diesen Beispiel ein folgendes Problem und zwar das Aktualisieren.
Bei Start des Programm wir die Funktion Kategorien geladen hier der Code.
private void Kategorien_Laden () { Tabellekomplett.Kategorie.Clear(); SQLiteConnection SQLconnect = new SQLiteConnection(); SQLiteCommand SQLcommand; string DBVerzeichnis_Dateiname = Properties.Settings.Default.DatenbankVerzeichnis + "\\DB.sql"; SQLconnect.ConnectionString = "Data Source=" + DBVerzeichnis_Dateiname + ";"; SQLconnect.Open(); SQLcommand = SQLconnect.CreateCommand(); SQLcommand.CommandText = "SELECT * FROM Kategorie"; SQLiteDataReader SQLreader = SQLcommand.ExecuteReader(); while (SQLreader.Read()) Tabellekomplett.Kategorie.Rows.Add(SQLreader[0], SQLreader[1], SQLreader[2], SQLreader[3]); SQLcommand.Dispose(); SQLconnect.Close(); }
Beim Programmstart wird es die DB geladen das klappt auch gut. Nun zu mein Problem.
Ich habe ein WPF Form um eine neue Kategorie hinzufügen. mit dem Klick von Button Speichern wird es in der DB gespeichert und danach wird die DB nochmal neu ausgelesen in das Dataset.
Aber die neu Ausgelesene Dataset wird nicht neu angezeigt. Es bleibt immer so beim Programm Start.
Wie kann man das TreeView aktualisieren?
Gruß
Mezzo
-
Hallo,
wenn dein aktueller Code ähnlich dem von deinem Posting vom 13.08.21 ist, vermute ich, daß 'ChildrenItems' immer wieder neu erzeugt wird. Es wird jedoch nur die erste ChildrenItems-Instanz an die TreeViewItem-ItemsSource gebunden. Deshalb solltest du die erste ChildrenItems-Instanz (ObservableCollection) beibehalten und diese ObservableCollection beim Aktualisieren der Ansicht ebenfalls aktualisieren, statt sie einfach durch eine neue ObservableCollection-Instanz zu ersetzen.
Alternativ kannst du im C# der TreeView-ItemsSource explzit eine neue ObservableCollection-Instanz zuweisen und damit das Binding von TreeView-ItemsSource umgehen/ausschalten.
Gruß
Heiko -
Hier mal mein Code
xaml:
<Window x:Class="Treeview3.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:Treeview3" mc:Ignorable="d" Title="MainWindow" Height="450" Width="643.818"> <Grid> <TreeView x:Name="TreeViewKategorieRoot" HorizontalAlignment="Left" SelectedItemChanged="TreeViewKategorieRoot_SelectedItemChanged" Height="399" Margin="10,10,0,0" VerticalAlignment="Top" Width="284" 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" /> <Setter Property="Tag" Value="{Binding}" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="ExtraBold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding Row.KategorieName}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <TextBox x:Name="Textbox1" HorizontalAlignment="Left" Height="23" Margin="299,25,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="315"/> <Button Content="Speichern" Click="Button_Click_1" HorizontalAlignment="Left" Margin="299,78,0,0" VerticalAlignment="Top" Width="315"/> </Grid> </Window>
c#:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Data.SQLite; using System.IO; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Runtime.CompilerServices; namespace Treeview3 { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { DataSet1 Tabellekomplett = new DataSet1(); string Selected_KategorieNummer; string Selected_UnterKategorieNummer; string Selected_KategorieName; public MainWindow() { InitializeComponent(); this.DataContext = this; Kategorien_Laden(); ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); foreach (DataRow row in Tabellekomplett.Tables["Kategorie"].Rows) col.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); cvs.Source = col.Where((d) => d.KategorieUnterNummer == ""); } private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } private Data_Kategorie _details = null; public Data_Kategorie Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } } #region attached behavior #endregion #region OnPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); #endregion private void TreeViewKategorieRoot_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { Data_Kategorie TreeView_item_Selected = TreeViewKategorieRoot.SelectedItem as Data_Kategorie; if (TreeView_item_Selected == null) return; Selected_KategorieNummer = TreeView_item_Selected.KategorieNummer; Selected_UnterKategorieNummer = TreeView_item_Selected.KategorieUnterNummer; Selected_KategorieName = TreeView_item_Selected.KategorieName; MessageBox.Show("Nummer: " + TreeView_item_Selected.KategorieNummer); } private void Button_Click(object sender, RoutedEventArgs e) { SQLiteConnection connect = new SQLiteConnection(); string DBVerzeichnis_Dateiname = "DB.sql"; connect.ConnectionString = "Data Source=" + DBVerzeichnis_Dateiname + ";"; connect.Open(); connect.Close(); SQLiteCommand command; connect.Open(); command = connect.CreateCommand(); command.CommandText = "CREATE TABLE Kategorie(id INTEGER PRIMARY KEY AUTOINCREMENT, KategorieNummer VARCHAR(250), KategorieUnterNummer VARCHAR(250), KategorieName VARCHAR(250));"; command.ExecuteNonQuery(); command.Dispose(); connect.Close(); } private void Button_Click_1(object sender, RoutedEventArgs e) { string KategorieUnterNummer; string KategorieName = Textbox1.Text; DateTime InNummer = DateTime.Now; string Jahr = InNummer.Year.ToString(); string Mon = InNummer.Month.ToString().PadLeft(2, '0'); string tag = InNummer.Day.ToString().PadLeft(2, '0'); string Stu = InNummer.Hour.ToString().PadLeft(2, '0'); string Min = InNummer.Minute.ToString().PadLeft(2, '0'); string Sek = InNummer.Second.ToString().PadLeft(2, '0'); string mse = InNummer.Millisecond.ToString().PadLeft(3, '0'); string KategorieNummer = "Kat" + Jahr + Mon + tag + Stu + Min + Sek + mse; if (Selected_KategorieNummer == "") { KategorieUnterNummer = ""; } else { KategorieUnterNummer = Selected_KategorieNummer; } SQLiteConnection connect = new SQLiteConnection(); connect.ConnectionString = "Data Source=DB.sql;"; SQLiteCommand command; connect.Open(); command = connect.CreateCommand(); command.CommandText = "INSERT INTO Kategorie (KategorieNummer, KategorieUnterNummer, KategorieName) VALUES ('" + KategorieNummer + "', '" + KategorieUnterNummer + "', '" + KategorieName + "')"; command.ExecuteNonQuery(); command.Dispose(); connect.Close(); Kategorien_Laden(); } private void Kategorien_Laden() { Tabellekomplett.Kategorie.Clear(); SQLiteConnection SQLconnect = new SQLiteConnection(); SQLiteCommand SQLcommand; string DBVerzeichnis_Dateiname = "db.sql"; SQLconnect.ConnectionString = "Data Source=" + DBVerzeichnis_Dateiname + ";"; SQLconnect.Open(); SQLcommand = SQLconnect.CreateCommand(); SQLcommand.CommandText = "SELECT * FROM Kategorie"; SQLiteDataReader SQLreader = SQLcommand.ExecuteReader(); while (SQLreader.Read()) Tabellekomplett.Kategorie.Rows.Add(SQLreader[0], SQLreader[1], SQLreader[2], SQLreader[3]); SQLcommand.Dispose(); SQLconnect.Close(); } } public class Data_Kategorie { public Data_Kategorie(DataSet1.KategorieRow row) { this.Row = row; this.dt = (DataSet1.KategorieDataTable)row.Table; this.ds = (DataSet1)row.Table.DataSet; } DataSet1 ds; DataSet1.KategorieDataTable dt; public DataSet1.KategorieRow Row { get; set; } public string KategorieNummer { get => Row.KategorieNummer; set { Row.KategorieNummer = value; } } public string KategorieUnterNummer { get => Row.KategorieUnterNummer; set { Row.KategorieUnterNummer = value; } } public string KategorieName { get => Row.KategorieName; set { Row.KategorieName = value; } } public object ChildrenItems => new ObservableCollection<Data_Kategorie>(from d in dt where d.KategorieUnterNummer == this.KategorieNummer select new Data_Kategorie(d)); public bool IsExpanded { get; set; } = true; public bool IsSelected { get; set; } } }
Der TreeView wird bei mir nicht Aktuallisiert wenn man auf dem Button Clickt.
Gruß
Mezzo
-
Hallo,
im ctor weist du cvs.Source eine temporär erzeugte Enumeration zu. Stattdessen solltest du eine ObservableCollection-Instanz zuweisen, die du dir in einer Klassenvariablen merkst. Später kannst dur dieser ObservableCollection-Instanz weitere Root-Elemente (Typ Data_Kategorie) hinzufügen. Etwa so:
public MainWindow() { //... ObservableCollection<Data_Kategorie> MyRootItems = new ObservableCollection<Data_Kategorie>(); foreach (DataRow row in Tabellekomplett.Tables["Kategorie"].Rows) { if (KategorieUnterNummer == "") MyRootItems.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); } cvs.Source = MyRootItems; } private void Kategorien_Laden() { //... while (SQLreader.Read()) { Tabellekomplett.Kategorie.Rows.Add(SQLreader[0], SQLreader[1], SQLreader[2], SQLreader[3]); var row = Tabellekomplett.Kategorie.Rows[Tabellekomplett.Kategorie.Rows.Count - 1]; MyRootItems.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); } //... }
Gruß
Heiko- Bearbeitet Heiko65456465 Donnerstag, 7. Oktober 2021 11:48
-
Hallo
Danke jetzt wird es zwar gleich Aktualiert aber die unterknoten stimmen nicht mehr. Es wird einfach in einer Reiche angezeigt.
xaml:
<Window x:Class="Treeview3.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:Treeview3" mc:Ignorable="d" Title="MainWindow" Height="450" Width="643.818"> <Grid> <TreeView x:Name="TreeViewKategorieRoot" HorizontalAlignment="Left" SelectedItemChanged="TreeViewKategorieRoot_SelectedItemChanged" Height="399" Margin="10,10,0,0" VerticalAlignment="Top" Width="284" 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" /> <Setter Property="Tag" Value="{Binding}" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="ExtraBold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding Row.KategorieName}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <TextBox x:Name="Textbox1" HorizontalAlignment="Left" Height="23" Margin="299,25,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="315"/> <Button Content="Speichern" Click="Button_Click_1" HorizontalAlignment="Left" Margin="299,78,0,0" VerticalAlignment="Top" Width="315"/> </Grid> </Window>
c#:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Data.SQLite; using System.IO; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Runtime.CompilerServices; namespace Treeview3 { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { //ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); ObservableCollection<Data_Kategorie> MyRootItems = new ObservableCollection<Data_Kategorie>(); DataSet1 Tabellekomplett = new DataSet1(); string Selected_KategorieNummer; string Selected_UnterKategorieNummer; string Selected_KategorieName; string KategorieUnterNummer; public MainWindow() { InitializeComponent(); this.DataContext = this; Kategorien_Laden(); foreach (DataRow row in Tabellekomplett.Tables["Kategorie"].Rows) { if (KategorieUnterNummer == "") MyRootItems.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); } cvs.Source = MyRootItems; } private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } private Data_Kategorie _details = null; public Data_Kategorie Detail { get => this._details; set { this._details = value; OnPropertyChanged(); } } #region attached behavior #endregion #region OnPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); #endregion private void TreeViewKategorieRoot_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { Data_Kategorie TreeView_item_Selected = TreeViewKategorieRoot.SelectedItem as Data_Kategorie; if (TreeView_item_Selected == null) return; Selected_KategorieNummer = TreeView_item_Selected.KategorieNummer; Selected_UnterKategorieNummer = TreeView_item_Selected.KategorieUnterNummer; Selected_KategorieName = TreeView_item_Selected.KategorieName; MessageBox.Show("Nummer: " + TreeView_item_Selected.KategorieNummer); } private void Button_Click(object sender, RoutedEventArgs e) { SQLiteConnection connect = new SQLiteConnection(); string DBVerzeichnis_Dateiname = "DB.sql"; connect.ConnectionString = "Data Source=" + DBVerzeichnis_Dateiname + ";"; connect.Open(); connect.Close(); SQLiteCommand command; connect.Open(); command = connect.CreateCommand(); command.CommandText = "CREATE TABLE Kategorie(id INTEGER PRIMARY KEY AUTOINCREMENT, KategorieNummer VARCHAR(250), KategorieUnterNummer VARCHAR(250), KategorieName VARCHAR(250));"; command.ExecuteNonQuery(); command.Dispose(); connect.Close(); } private void Button_Click_1(object sender, RoutedEventArgs e) { string KategorieName = Textbox1.Text; DateTime InNummer = DateTime.Now; string Jahr = InNummer.Year.ToString(); string Mon = InNummer.Month.ToString().PadLeft(2, '0'); string tag = InNummer.Day.ToString().PadLeft(2, '0'); string Stu = InNummer.Hour.ToString().PadLeft(2, '0'); string Min = InNummer.Minute.ToString().PadLeft(2, '0'); string Sek = InNummer.Second.ToString().PadLeft(2, '0'); string mse = InNummer.Millisecond.ToString().PadLeft(3, '0'); string KategorieNummer = "Kat" + Jahr + Mon + tag + Stu + Min + Sek + mse; if (Selected_KategorieNummer == "") { KategorieUnterNummer = ""; } else { KategorieUnterNummer = Selected_KategorieNummer; } SQLiteConnection connect = new SQLiteConnection(); connect.ConnectionString = "Data Source=DB.sql;"; SQLiteCommand command; connect.Open(); command = connect.CreateCommand(); command.CommandText = "INSERT INTO Kategorie (KategorieNummer, KategorieUnterNummer, KategorieName) VALUES ('" + KategorieNummer + "', '" + KategorieUnterNummer + "', '" + KategorieName + "')"; command.ExecuteNonQuery(); command.Dispose(); connect.Close(); Kategorien_Laden(); } private void Kategorien_Laden() { Tabellekomplett.Kategorie.Clear(); SQLiteConnection SQLconnect = new SQLiteConnection(); SQLiteCommand SQLcommand; string DBVerzeichnis_Dateiname = "db.sql"; SQLconnect.ConnectionString = "Data Source=" + DBVerzeichnis_Dateiname + ";"; SQLconnect.Open(); SQLcommand = SQLconnect.CreateCommand(); SQLcommand.CommandText = "SELECT * FROM Kategorie"; SQLiteDataReader SQLreader = SQLcommand.ExecuteReader(); MyRootItems.Clear(); while (SQLreader.Read()) { Tabellekomplett.Kategorie.Rows.Add(SQLreader[0], SQLreader[1], SQLreader[2], SQLreader[3]); var row = Tabellekomplett.Kategorie.Rows[Tabellekomplett.Kategorie.Rows.Count - 1]; MyRootItems.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); } //Tabellekomplett.Kategorie.Rows.Add(SQLreader[0], SQLreader[1], SQLreader[2], SQLreader[3]); SQLcommand.Dispose(); SQLconnect.Close(); //col.Clear(); //foreach (DataRow row in Tabellekomplett.Tables["Kategorie"].Rows) col.Add(new Data_Kategorie((DataSet1.KategorieRow)row)); //cvs.Source = col.Where((d) => d.KategorieUnterNummer == ""); } } public class Data_Kategorie { public Data_Kategorie(DataSet1.KategorieRow row) { this.Row = row; this.dt = (DataSet1.KategorieDataTable)row.Table; this.ds = (DataSet1)row.Table.DataSet; } DataSet1 ds; DataSet1.KategorieDataTable dt; public DataSet1.KategorieRow Row { get; set; } public string KategorieNummer { get => Row.KategorieNummer; set { Row.KategorieNummer = value; } } public string KategorieUnterNummer { get => Row.KategorieUnterNummer; set { Row.KategorieUnterNummer = value; } } public string KategorieName { get => Row.KategorieName; set { Row.KategorieName = value; } } public object ChildrenItems => new ObservableCollection<Data_Kategorie>(from d in dt where d.KategorieUnterNummer == this.KategorieNummer select new Data_Kategorie(d)); public bool IsExpanded { get; set; } = true; public bool IsSelected { get; set; } } }
Wie mach ich es wieder das die Anordnung von die Unterknoten wieder Richtig sind.
Gruß
Mezzo
-
Hallo,
die ObservableCollection, die du in der Klasse Data_Kategorie erzeugst, mußt du dir in einer Variablen merken, damit du ihr später Unterkategorien hinzufügen kannst.
Über das TreeViewKategorieRoot.SelectedItem kommst du an die aktuelle Data_Kategorie heran und fügst dieser eine neue Unterkategorie hinzu.
public class Data_Kategorie { public ObservableCollection<Data_Kategorie> ChildrenItems { get; private set; } public Data_Kategorie(DataSet1.KategorieRow row) { this.Row = row; this.dt = (DataSet1.KategorieDataTable)row.Table; this.ds = (DataSet1)row.Table.DataSet; ChildrenItems = new ObservableCollection<Data_Kategorie>(from d in dt where d.KategorieUnterNummer == this.KategorieNummer select new Data_Kategorie(d)); } internal void AddUnterkategorie(DataSet1.KategorieRow row) { ChildrenItems.Add(new Data_Kategorie(row)); } } public partial class MainWindow : Window { private void button_Click() { // Erzeugen einer neuen Row. (TreeViewKategorieRoot.SelectedItem as Data_Kategorie).AddUnterkategorie(newRow); } }
Gruß
Heiko
- Bearbeitet Heiko65456465 Freitag, 8. Oktober 2021 17:50
-
Hallo,
ich habe den ctor (public Data_Kategorie(DataSet1.KategorieRow row)) aus deinem Code kopiert. Wenn es rot unterstrichen wird, wird sicher auch eine Fehlermeldung dazu ausgegeben, vom Compiler und/oder Intellisense, wenn man die Maus auf die rote Linie bewegt.
Gruß
Heiko
-
Hi Mezzo,
ich habe den Thread mehrmals gelesen und nicht verstanden, was die von dir gewünschte einfache Lösung machen soll.Die erste Frage ist, warum nutzt du nicht EF sondern das DataSet.
Die zweite Frage ist, wie soll die Hierarchie aussehen auf Basis der Datenbankdaten.
Die dritte Frage ist, warum nutzt du nicht eine übersichtlichere Programmierung wie z.B. das MVVM Entwurfsmuster.
Hier mal ein ganz einfaches Beispiel, wie ich deine Daten verstanden habe:
<Window x:Class="WpfApp1.Window084" 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:WpfApp084" mc:Ignorable="d" Title="Window084" Height="450" Width="800"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <TreeView ItemsSource="{Binding RootItems}"> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </Grid> </Window>
Und dazu die Klassen:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Data; namespace WpfApp084 { public class ViewModel { public ViewModel() => cvs.Source = (new Model()).GetData(); private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } } internal class Model { internal ObservableCollection<Data_Kategorie> GetData() { ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); Data_Kategorie level1; Data_Kategorie level2; Data_Kategorie level3; level1 = new Data_Kategorie() { KategorieName = "test0" }; col.Add(level1); level2 = new Data_Kategorie() { KategorieName = "test01" }; level1.ChildrenItems.Add(level2); level3 = new Data_Kategorie() { KategorieName = "test001" }; level2.ChildrenItems.Add(level3); level1 = new Data_Kategorie() { KategorieName = "test1" }; col.Add(level1); return col; } } public class Data_Kategorie { public string KategorieNummer { get; set; } public string KategorieUnterNummer { get; set; } public string KategorieName { get; set; } public ObservableCollection<Data_Kategorie> ChildrenItems { get; set; } = new ObservableCollection<Data_Kategorie>(); public bool IsExpanded { get; set; } = true; public bool IsSelected { get; set; } } }
Ergebnis:
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks
- Bearbeitet Peter Fleischer Mittwoch, 17. November 2021 06:05
-
Hallo
Danke Peter, durch dein Beispiel ist mir mehr klar geworden.
Aber ich sehe auch das dein Beispiel Level erhält.
Level1 = Root
Level2 = 1. Unterknoten
Level3 = 2. Unterknoten
Daraus verstehe ich das nur max 2 unterknoten erstellt werden kann.
Bei meinen Beispiel soll der Unterknoten bei dem KategorieNummer eingefügt werden. des könnte auch des 20. Unterknoten sein.
- Die erste Frage ist, warum nutzt du nicht EF sondern das DataSet
Du hast recht mir hat persönlich das DataSet gefallen aber nach der seit hab ich gemerkt das DataSet nicht mehr wichtig ist.
- Die zweite Frage ist, wie soll die Hierarchie aussehen auf Basis der Datenbankdaten.
In der Datenbank ist es so gespeichert KategorieNummer (das ist die Hauptnummer die jede Kategorie bekomme ob Root oder unterknoten), in KategorieUnterNummer (Hier wird die Nummer gespeichert die darüber ist wenn der eintrag Leer ist dann ist ein ein Hauptknoten) z.b.
"100", "", "Hauptknoten"
"101", "100", "Unterknoten 1 zum Hauptknoten"
"102", "100", "Unterknoten 2 zum Hauptknoten"
"103", "101", "Unterknoten 3 zum Unterknoten 1"
"104", "103", "Unterknoten 4 zum Unterknoten 3"
usw. das geht unändlich.
- Die dritte Frage ist, warum nutzt du nicht eine übersichtlichere Programmierung wie z.B. das MVVM Entwurfsmuster.
Ich habe gerade in NuGet Manager geschauft und MVVM eingegeben da gibt es so viele. Welches sollte man dafür nehmen?
Ich habe schon öfter MVVM gelesen aber verstehe nicht richtig wür was man das überhaupt braucht. ist es dann leichter zu erstellen? wird da schon fertige codes vorgeschrieben? ich bin kein fan von Fertige vorlagen da es immer auch sachen drin sind die man nicht braucht.
Peter: Kannst du mir bitte ein Beispiel geben nicht auf Level sonder mit die Nummern. und wenn man in TreeView ein Knoten Markiert das ein Messenbox auf geht und mit dann die KategorieNummer, KategorieUnterNummer und KategorieName anzeigt.
Danke im Vorraus.
Gruß
Mezzo
-
Hi Mezzo,
du kann beliebig viele Knotenebenen nutzen. Mein Beispiel war nur auf 3 Ebenen begrenzt, um das Prinzip zu zeigen.Für die variable Hierarchie habe ich mal eine Demo mit zufällig generierten Demo-Daten erstellt:
<Window x:Class="WpfApp1.Window085" 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:WpfApp085" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" mc:Ignorable="d" Title="Mezzo 17.11.21" Height="450" Width="800"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <TreeView ItemsSource="{Binding RootItems}"> <i:Interaction.Behaviors> <local:TreeViewBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </Grid> </Window>
Und der Code dazu:
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Interactivity; namespace WpfApp085 { public class ViewModel { public ViewModel() => cvs.Source = (new Model()).GetData(0); private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } public Data_Kategorie Detail { set { MessageBox.Show($"KategorieNummer: {value.KategorieNummer}\nKategorieUnterNummer: {value.KategorieUnterNummer}\nKategorieName: {value.KategorieName}"); } } } internal class Model { public Model() { // Demo-Daten erzeugen Random rnd = new Random(); for (int i = 100; i < 1000; i++) col.Add(new Data_Kategorie(col) { KategorieNummer = i, KategorieUnterNummer = (rnd.NextDouble() > .5) ? 0 : rnd.Next(100, i), KategorieName = $"Node {i}" }); } // Datenspeicher internal ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); // Sicht für Root-Level internal ObservableCollection<Data_Kategorie> GetData(int levelID) => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == 0)); } public class Data_Kategorie { public Data_Kategorie(ObservableCollection<Data_Kategorie> data) => col = data; private ObservableCollection<Data_Kategorie> col; public int KategorieNummer { get; set; } public int KategorieUnterNummer { get; set; } public string KategorieName { get; set; } public ObservableCollection<Data_Kategorie> ChildrenItems { get => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == this.KategorieNummer)); } 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_Kategorie; } } }
Die Demo habe ich mit dem MVVM Entwurfsmuster erstellt. Die Grundidee von MVVM besteht in WPF darin, dass Sicht und Code vollständig getrennt werden, d.h. in der Sicht gibt es keinen CodeBehind. Es werden lediglich Eigenschaftsbindung, Befehlsbindung und angehängtes Verhalten (attached behavior) genutzt. Irgendwelche fertigen Codes (Frameworks) sind dafür nicht erforderlich. Für das angehängte Verhalten bietet sich die Nutzung der interactivity-dll aus Nuget an. Damit entfällt die Notwendigkeit der Programmierung von DependencProperties im ViewModel.
Das Ergebnis kann dann so aussehen:
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo
Ich habe Ihre beispiel mal bei mir eingefügt aber in WPF wird mir das local:ViewModel und das local:TreeViewBehavior blau unterstrichen und kann es nicht ausführen.
Es wird gesagt das es nicht gefunden werden kann.
Hier mein code
XAML:
<Window x:Class="Treeview3.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:Treeview3" mc:Ignorable="d" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="450" Width="643.818"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <TreeView ItemsSource="{Binding RootItems}"> <i:Interaction.Behaviors> <local:TreeViewBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </Grid> </Window>
ViewModel.cs
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Interactivity; namespace Treeview3 { public class ViewModel { public ViewModel() => cvs.Source = (new Model()).GetData(0); private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } public Data_Kategorie Detail { set { MessageBox.Show($"KategorieNummer: {value.KategorieNummer}\nKategorieUnterNummer: {value.KategorieUnterNummer}\nKategorieName: {value.KategorieName}"); } } } internal class Model { public Model() { // Demo-Daten erzeugen Random rnd = new Random(); for (int i = 100; i < 1000; i++) col.Add(new Data_Kategorie(col) { KategorieNummer = i, KategorieUnterNummer = (rnd.NextDouble() > .5) ? 0 : rnd.Next(100, i), KategorieName = $"Node {i}" }); } // Datenspeicher internal ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); // Sicht für Root-Level internal ObservableCollection<Data_Kategorie> GetData(int levelID) => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == 0)); } public class Data_Kategorie { public Data_Kategorie(ObservableCollection<Data_Kategorie> data) => col = data; private ObservableCollection<Data_Kategorie> col; public int KategorieNummer { get; set; } public int KategorieUnterNummer { get; set; } public string KategorieName { get; set; } public ObservableCollection<Data_Kategorie> ChildrenItems { get => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == this.KategorieNummer)); } 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_Kategorie; } } }
Was ist bei mir falsch?
Gruß
Mezzo
-
Hi Mezzo,
C# ist case-sensitive. Bitte nutze die Bezeichner aus meinem Code unverändert!Dein Code erkennt nicht den Namensraum "TreeView3", weil du ihn "Treeview3" genannt hast.
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo
Danke jetzt funktioniert es.
Jetzt noch eine Frage und zwar mit der Sortierung.
Ich habe mir gedacht das ich noch eine Variande dazu mache wo dann die Reihenfolge gespeichert wird.
- KategorieNummer
- KategorieUnterNummer
- KategorieName
- KategorieReihenfolge
Wie würde das dann funktionieren?
Gruß
Mezzo
-
Hier mein Code
XAML:
<Window x:Class="TreeView4.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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:TreeView4" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <TreeView ItemsSource="{Binding RootItems}" Margin="0,0,530,0"> <i:Interaction.Behaviors> <local:TreeViewBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </Grid> </Window>
C#
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Interactivity; namespace TreeView4 { public class ViewModel { public ViewModel() => cvs.Source = (new Model()).GetData(0); private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } public Data_Kategorie Detail { set { MessageBox.Show($"KategorieNummer: {value.KategorieNummer}\nKategorieUnterNummer: {value.KategorieUnterNummer}\nKategorieName: {value.KategorieName}"); } } } internal class Model { public Model() { // Demo-Daten erzeugen Random rnd = new Random(); for (int i = 100; i < 110; i++) col.Add(new Data_Kategorie(col) { KategorieNummer = i, KategorieUnterNummer = (rnd.NextDouble() > .5) ? 0 : rnd.Next(100, i), KategorieName = $"Node {i}", KategorieReihenfolge = i }); col.Add(new Data_Kategorie(col) { KategorieNummer=10, KategorieUnterNummer=0, KategorieName="Hallo", KategorieReihenfolge=10 } ); } // Datenspeicher internal ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); // Sicht für Root-Level internal ObservableCollection<Data_Kategorie> GetData(int levelID) => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == 0)); } public class Data_Kategorie { public Data_Kategorie(ObservableCollection<Data_Kategorie> data) => col = data; private ObservableCollection<Data_Kategorie> col; public int KategorieNummer { get; set; } public int KategorieUnterNummer { get; set; } public string KategorieName { get; set; } public int KategorieReihenfolge { get; set; } public ObservableCollection<Data_Kategorie> ChildrenItems { get => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == this.KategorieNummer)); } 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_Kategorie; } } }
Gruß
Mezzo
-
Hi,
füge einfach eine SortDescription hinzu.--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hi Mezzo,
das kann man im XAML oder im Code machen, z.B. in meiner Demo im Constructor des ViewModels:public ViewModel() { cvs.Source = (new Model()).GetData(0); cvs.SortDescriptions.Add(new SortDescription("KategorieNummer", ListSortDirection.Ascending)); }
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo
Cool und Danke Peter jetzt hab ich daraus auch gleich 2 sachen gelernt.
Ich hatte vorher nicht verstanden warum das => stehen soll jetzt weiß ich das
public ViewModel() => cvs.Source = (new Model()).GetData(0);
das ersetzt die { } und ich kann damit die funktion verkürzen wenn es nur 1 befehl ist. super danke.
Jetzt wäre es noch Super wie das verschieben der Noden mit der Maus funktioniert. So das man die Anordnung mit der Maus verschieben kann und das es sich die Reihenvolge auch in Variable KategorieReihenfolge verändert.
Kannst du mir da auch ein Beispiel geben wie man es mit Drag and Drop es lösen kann?
Gruß
Mezzo
-
Hi Mezzo,
das Verschieben der Knoten im TreeView ist recht trivial. Vorher ist aber zu definieren, wie die Verschiebetechnologie zu funktionieren hat.
1. Wie auf ersten Platz verschieben?
2. Wie nach einem Knoten zu platzieren?
3. Wie in Unterknoten einordnen?
Der prinzipielle Code sieht so aus:
public class DragDropBehavior : Behavior<UIElement> { protected override void OnAttached() { AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown; AssociatedObject.Drop += Tv_Drop; } private void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (sender is TreeView) { TreeView dragSource = sender as TreeView; object data = GetDataFromTreeView(dragSource, e.GetPosition(AssociatedObject)); if (data != null) DragDrop.DoDragDrop(dragSource, data, DragDropEffects.Move); } } private static object GetDataFromTreeView(TreeView source, Point point) { UIElement element = source.InputHitTest(point) as UIElement; if (element != null) { object data = DependencyProperty.UnsetValue; while (data == DependencyProperty.UnsetValue) { data = source.ItemContainerGenerator.ItemFromContainer(element); if (data == DependencyProperty.UnsetValue) element = VisualTreeHelper.GetParent(element) as UIElement; if (element == source) return null; } if (data != DependencyProperty.UnsetValue) return data; } return null; } private void Tv_Drop(object sender, DragEventArgs e) { if (sender is TreeView) { e.Effects = DragDropEffects.None; e.Handled = true; // Verify that this is a valid drop and then store the drop target Data_Kategorie targetItem = (e.OriginalSource as TextBlock)?.DataContext as Data_Kategorie; ViewModel vm = (sender as TreeView).DataContext as ViewModel; if (targetItem != null && vm != null) { object data = e.Data.GetData(typeof(Data_Kategorie));
// Hier konkrete Verschiebung programmieren
e.Effects = DragDropEffects.Move; } } } }
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo
Irgendwie funktioniert das Verschieben mit dem DragDrop nicht.
Dazu habe ich noch ein anderes Problem. Ich habe in mein MainWindow noch ein Textbox, Combobox und ein Button hingefügt um da einen neuen Eintrag in TreeView hinzufügen. Aber irgendwie kann ich mit der Action von Button_Click nicht auf col.add zugreifen.
Das nächste Problem ist bei Combobox wird mir beim öffnen des Combobox nicht das TreeView angezeigt.
Hier mal mein Code.
MainWindows.xaml
<Window x:Class="TreeView4.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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:TreeView4" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <TreeView ItemsSource="{Binding RootItems}" Margin="0,0,609,0" > <i:Interaction.Behaviors> <local:TreeViewBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> <TextBox HorizontalAlignment="Left" Height="23" Margin="188,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="594"/> <Button Content="Hinzufügen" Click="Button_Add_Click" HorizontalAlignment="Left" Margin="188,73,0,0" VerticalAlignment="Top" Width="594"/> <ComboBox HorizontalAlignment="Left" Margin="188,38,0,0" VerticalAlignment="Top" Width="594"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Margin="3" Foreground="Blue" FontWeight="Bold" Text="{Binding KategorieName}"/> <TreeView ItemsSource="{Binding RootItems}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </Grid> </Window>
MainWindow.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace TreeView4 { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Add_Click(object sender, RoutedEventArgs e) { } } }
ViewModel.cs
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Interactivity; using System.Windows.Media; namespace TreeView4 { public class ViewModel { public ViewModel() { cvs.Source = (new Model()).GetData(0); cvs.SortDescriptions.Add(new SortDescription("KategorieReihenfolge", ListSortDirection.Ascending)); } private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView RootItems { get => cvs.View; } public Data_Kategorie Detail { set { MessageBox.Show($"KategorieNummer: {value.KategorieNummer}\nKategorieUnterNummer: {value.KategorieUnterNummer}\nKategorieName: {value.KategorieName}"); } } } internal class Model { public Model() { // Demo-Daten erzeugen Random rnd = new Random(); for (int i = 100; i < 110; i++) col.Add(new Data_Kategorie(col) { KategorieNummer = i, KategorieUnterNummer = (rnd.NextDouble() > .5) ? 0 : rnd.Next(100, i), KategorieName = $"Node {i}", KategorieReihenfolge = i }); col.Add(new Data_Kategorie(col) { KategorieNummer=10, KategorieUnterNummer=0, KategorieName="Hallo", KategorieReihenfolge=10 } ); col.Add(new Data_Kategorie(col) { KategorieNummer = 11, KategorieUnterNummer = 0, KategorieName = "Hallo1", KategorieReihenfolge = 9 }); } // Datenspeicher internal ObservableCollection<Data_Kategorie> col = new ObservableCollection<Data_Kategorie>(); // Sicht für Root-Level internal ObservableCollection<Data_Kategorie> GetData(int levelID) => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == 0)); } public class Data_Kategorie { public Data_Kategorie(ObservableCollection<Data_Kategorie> data) => col = data; private ObservableCollection<Data_Kategorie> col; public int KategorieNummer { get; set; } public int KategorieUnterNummer { get; set; } public string KategorieName { get; set; } public int KategorieReihenfolge { get; set; } public ObservableCollection<Data_Kategorie> ChildrenItems { get => new ObservableCollection<Data_Kategorie>(col.Where((d) => d.KategorieUnterNummer == this.KategorieNummer)); } 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_Kategorie; } } public class DragDropBehavior : Behavior<UIElement> { protected override void OnAttached() { AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown; AssociatedObject.Drop += Tv_Drop; } private void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (sender is TreeView) { TreeView dragSource = sender as TreeView; object data = GetDataFromTreeView(dragSource, e.GetPosition(AssociatedObject)); if (data != null) DragDrop.DoDragDrop(dragSource, data, DragDropEffects.Move); } } private static object GetDataFromTreeView(TreeView source, Point point) { UIElement element = source.InputHitTest(point) as UIElement; if (element != null) { object data = DependencyProperty.UnsetValue; while (data == DependencyProperty.UnsetValue) { data = source.ItemContainerGenerator.ItemFromContainer(element); if (data == DependencyProperty.UnsetValue) element = VisualTreeHelper.GetParent(element) as UIElement; if (element == source) return null; } if (data != DependencyProperty.UnsetValue) return data; } return null; } private void Tv_Drop(object sender, DragEventArgs e) { if (sender is TreeView) { e.Effects = DragDropEffects.None; e.Handled = true; // Verify that this is a valid drop and then store the drop target Data_Kategorie targetItem = (e.OriginalSource as TextBlock)?.DataContext as Data_Kategorie; ViewModel vm = (sender as TreeView).DataContext as ViewModel; if (targetItem != null && vm != null) { object data = e.Data.GetData(typeof(Data_Kategorie)); // Hier konkrete Verschiebung programmieren e.Effects = DragDropEffects.Move; } } } } }
Gruß
Mezzo
-
Hi Mezzo,
hier mal der geänderte XAML:<Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition/> </Grid.RowDefinitions> <TreeView Grid.Row="3" ItemsSource="{Binding RootItems}" AllowDrop="True"> <i:Interaction.Behaviors> <local:TreeViewBehavior/> <local:DragDropBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> <TextBox Grid.Row="0" HorizontalAlignment="Right" Height="23" Margin="5" TextWrapping="Wrap" Text="" Width="594"/> <Button Grid.Row="1" Content="Hinzufügen" HorizontalAlignment="Right" Margin="5" Width="594" Command="{Binding}"/> <ComboBox Grid.Row="2" HorizontalAlignment="Right" Margin="5" Width="594"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Margin="3" Foreground="Blue" FontWeight="Bold" Text="{Binding KategorieName}"/> <TreeView ItemsSource="{Binding RootItems}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:Data_Kategorie}" ItemsSource="{Binding ChildrenItems}"> <TextBlock Text="{Binding KategorieName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </Grid>
Und der ViewModel dazu:
public class ViewModel : ICommand, INotifyPropertyChanged { /// <summary> /// contructor /// </summary> public ViewModel() { cvs.Source = m.GetData(0); cvs.SortDescriptions.Add(new SortDescription("KategorieReihenfolge", ListSortDirection.Ascending)); } Model m = new Model(); private CollectionViewSource cvs = new CollectionViewSource(); /// <summary> /// View /// </summary> public ICollectionView RootItems { get => cvs.View; } /// <summary> /// Selected node /// </summary> public Data_Kategorie Detail { set { MessageBox.Show($"KategorieNummer: {value.KategorieNummer}\nKategorieUnterNummer: {value.KategorieUnterNummer}\nKategorieName: {value.KategorieName}"); } } #region Command public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) => true; /// <summary> /// execute Button /// </summary> /// <param name="parameter">CommandParameter</param> public void Execute(object parameter) { m.col.Add(new Data_Kategorie(m.col) { KategorieNummer = 12, KategorieUnterNummer = 0, KategorieName = "Hallo2", KategorieReihenfolge = 8 }); cvs.Source = m.GetData(0); OnPropertyChanged(nameof(RootItems)); } #endregion #region NotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); #endregion }
Damit funktioniert das Hinzufügen problemlos.
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hi Mezzo,
ich habe mich nach deinen Code gerichtet. Wenn du den Inhalt der TextBox im ViewModel haben willst, musst du die Texteigenschaft an eine Eigenschaft im ViewModel binden.--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hi Mezzo,
was hast du gebunden? (im XAML: Text="{Binding xxx}")?--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo Peter
Ich hab es so probiert
<TextBox x:Name="txt_Add" Grid.Row="0" HorizontalAlignment="Right" Height="23" Margin="5" TextWrapping="Wrap" Text="{Binding KategorieName}" Width="594"/>
Aber wenn ich jetzt auf den Button Hinzufügen klicke wird zwar ein Node hingefügt aber ohne Text, es wird einfach ein leeres Node Hinzugefügt
-
Hi Mezzo,
werden im "Immediate Window" Fehler angezeigt (z.B. Bindungsfehler)?--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks -
Hallo
Nein es wird kein Fehler angezeigt.
Ich habe mir grad überlegt diese mit den Binding würde dann nicht funktionieren wenn ich das TreeView als UserControl mache.
Weil wie ich gemerkt habe bräuchte ich diese Art von TreeView öfter somit wäre es doch besser dieses TreeView model als UserControl aufzubauen somit ist dieser Code dann nur 1mal die dann öfter verwendet wird sehe ich das Richtig.
Gruß
Mezzo
-
Hi Mezzo,
da kein Bindungsfehler angezeigt wird, funktioniert die Bindung auch und der in der Textbox eingegeben Text wird auch in die Eigenschaft "KategorieName" im ViewModel abgelegt. Das bedeutet, dass die Execute-Methode im ViewModel, die im Ergebnis des Klicks abgearbeitet wird, nicht auf den Eigenschaftswert zugreift. Ohne konkreten Code ist das Problem nicht nachvollziehbar.Natürlich kann man anstelle des TreeViews auch eine UserControl mit TreeView nutzen. Konzeptionell muss man sich dabei aber genau überlegen, wer ist DataContext, arbeitet man im UserContral auch mit einem separaten VieModel oder mit CodeBehind usw.
--
Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks