none
WPF-TreeView mit Index die Knoten ansteuern statt mit Name RRS feed

  • Frage

  • Ich habe folgendes Problem:

    Als ich meinen Code gebaut habe, nutzte ich noch WinForms und da funktionierte das hier auch einwandfrei:

            private void waregroup()
            {
                DataSet myDataSet = new DataSet();
                DataTable myDataTable = new DataTable();
                string sqlStatement = "SELECT * FROM APWGRUPP ORDER BY WG_WGSCHL ASC";
                string str_key = "";
                string str_desc = "";
                int int_node0 = 0;
                int int_node1= 0;
                int int_node2 = 0;
                int int_node3 = 0;
                int int_node4 = 0;
    
                myDataSet = ImportDBF(sqlStatement);
                myDataTable = myDataSet.Tables["Table"];
                foreach (DataRow row in myDataTable.Rows)
                {
                    str_desc = row.Field<string>(1);
                    str_key = row.Field<string>(2);
                    switch (str_key.Length)
                    {
                        case 1:
                            trv_waregroup.Nodes.Add(str_key + "-" + str_desc);
                            int_node0++;
                            int_node1 = 0;
                            int_node2 = 0;
                            int_node3 = 0;
                            int_node4 = 0;
                            break;
                        case 2:
                            trv_waregroup.Nodes[int_node0 - 1].Nodes.Add(str_key + "-" + str_desc);
                            int_node1++;
                            int_node2 = 0;
                            int_node3 = 0;
                            int_node4 = 0;
                            break;
                        case 4:
                            trv_waregroup.Nodes[int_node0 - 1].Nodes[int_node1 - 1].Nodes.Add(str_key + "-" + str_desc);
                            int_node2++;
                            int_node3 = 0;
                            int_node4 = 0;
                            break;
                        case 5:
                            trv_waregroup.Nodes[int_node0 - 1].Nodes[int_node1 - 1].Nodes[int_node2 - 1].Nodes.Add(str_key + "-" + str_desc);
                            int_node3++;
                            int_node4 = 0;
                            break;
                        case 6:
                            trv_waregroup.Nodes[int_node0 - 1].Nodes[int_node1 - 1].Nodes[int_node2 - 1].Nodes[int_node3 - 1].Nodes.Add(str_key + "-" + str_desc);
                            int_node4++;
                            break;
                        default:
                            trv_waregroup.Nodes[int_node0 - 1].Nodes[int_node1 - 1].Nodes[int_node2 - 1].Nodes[int_node3 - 1].Nodes[int_node4 - 1].Nodes.Add(str_key + "-" + str_desc);
                            break;
                    }
                }
            }
    

    Jetzt versuche ich das auf WPF umzustellen, aber die Logik dahinter funktioniert nicht mehr. Ich kann die Knoten nicht mehr über einen Counter ansteuern. Ich kann hier aber definitiv kein MVVM nutzen, da die Daten aus der DB erst noch logisch sortiert werden müssen in diese Unterknoten, je nach Länge des Keys.

    Ich versuchte es folgendermaßen, kam damit aber nicht weiter, da es nicht so funktioniert wie in der Hilfe angegeben:

    TreeViewItem temp = (TreeViewItem)_WareGroups.Items.GetItemAt(Node0 - 1);
    _WareGroups.Items.Add(GroupKey + "-" + GroupDesc);

    Dabei bleibt das temp leer. Jemand eine Idee wie ich hier die Unterknoten noch ansteuern kann im nächsten For-Durchlauf?

    Freitag, 17. Oktober 2014 14:34

Antworten

  • Hallo,
    auch wenn du kein MVVM verwenden willst, würde ich dir eine Auflistung im Codebehind empfehlen. Zunächst ist das minmal mehr Aufwand, der sich jedoch schnell bezahlt macht, wenn man auf die Daten zugreifen muss.

    Im Moment fügst du die Elemente direkt in die TreeView ein. Besser wäre es eine hierariche Liste einer Klasse zu verwenden. Im einfachsten Fall reicht schon folgendes dafür:

    public class MyItem : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
    
        protected void OnPropertyChanged(string propertyName)
        {
            var evt = this.PropertyChanged;
            if (evt != null)
                evt(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        #endregion
    
        private string _Header = "Item";
        public string Header
        {
            get
            {
                return this._Header;
            }
            set
            {
                if (this._Header != value)
                {
                    this._Header = value;
                    this.OnPropertyChanged("Header");
                }
            }
        }
    
        private ObservableCollection<MyItem> _SubItems = new ObservableCollection<MyItem>();
        public ObservableCollection<MyItem> SubItems
        {
            get
            {
                return this._SubItems;
            }
            set
            {
                if (this._SubItems != value)
                {
                    this._SubItems = value;
                    this.OnPropertyChanged("SubItems");
                }
            }
        }
    }

    Im Codebehind des Fensters deklaarierst du nun eine Liste mit den Items und weißt den DataContext im Konstruktor zu:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }
    
        public ObservableCollection<MyItem> Items { get; set; }

    Durch den DataContext kannst du nun im XAML auf die Auflistung im Codebehind zugreifen. Die ObservableCollection<T> aktualisiert die GUI über eine Änbderung der Items, die INotifyPropertyChanged-Schnittstelle über die Änderung einer Eigenschaft.

    Im XAML wird nun folgender Code benötigt:

    <TreeView ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                <TextBlock Text="{Binding Header}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

    Dadurch wird für jedes Element der Wert der Header-Eigenschaft angezeigt und die Elemente aus der SubItems-Auflistung werden als Unter-Elemente verwendet.

    Statt nun die TreeView zu befüllen, kannst du nun die Liste befüllen und über diese dann auf die Daten zugreifen.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 17. Oktober 2014 14:47
    Moderator

Alle Antworten

  • Hallo,
    auch wenn du kein MVVM verwenden willst, würde ich dir eine Auflistung im Codebehind empfehlen. Zunächst ist das minmal mehr Aufwand, der sich jedoch schnell bezahlt macht, wenn man auf die Daten zugreifen muss.

    Im Moment fügst du die Elemente direkt in die TreeView ein. Besser wäre es eine hierariche Liste einer Klasse zu verwenden. Im einfachsten Fall reicht schon folgendes dafür:

    public class MyItem : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
    
        protected void OnPropertyChanged(string propertyName)
        {
            var evt = this.PropertyChanged;
            if (evt != null)
                evt(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        #endregion
    
        private string _Header = "Item";
        public string Header
        {
            get
            {
                return this._Header;
            }
            set
            {
                if (this._Header != value)
                {
                    this._Header = value;
                    this.OnPropertyChanged("Header");
                }
            }
        }
    
        private ObservableCollection<MyItem> _SubItems = new ObservableCollection<MyItem>();
        public ObservableCollection<MyItem> SubItems
        {
            get
            {
                return this._SubItems;
            }
            set
            {
                if (this._SubItems != value)
                {
                    this._SubItems = value;
                    this.OnPropertyChanged("SubItems");
                }
            }
        }
    }

    Im Codebehind des Fensters deklaarierst du nun eine Liste mit den Items und weißt den DataContext im Konstruktor zu:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }
    
        public ObservableCollection<MyItem> Items { get; set; }

    Durch den DataContext kannst du nun im XAML auf die Auflistung im Codebehind zugreifen. Die ObservableCollection<T> aktualisiert die GUI über eine Änbderung der Items, die INotifyPropertyChanged-Schnittstelle über die Änderung einer Eigenschaft.

    Im XAML wird nun folgender Code benötigt:

    <TreeView ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                <TextBlock Text="{Binding Header}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

    Dadurch wird für jedes Element der Wert der Header-Eigenschaft angezeigt und die Elemente aus der SubItems-Auflistung werden als Unter-Elemente verwendet.

    Statt nun die TreeView zu befüllen, kannst du nun die Liste befüllen und über diese dann auf die Daten zugreifen.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 17. Oktober 2014 14:47
    Moderator
  • Ok, jetzt wird's schwer für mich, da ich keine Ahnung von MVVM habe und Dein Beispiel sieht gut aus, aber lässt viele Fragen bei mir entstehen. Könntest Du mich da vielleicht erleuchten?

    public class MyItem : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
    
        protected void OnPropertyChanged(string propertyName)
        {
            var evt = this.PropertyChanged;
            if (evt != null)
                evt(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        #endregion
    

    Also, eine neue Klasse deklarieren und diese von INotifyPropertyChanged ableiten. Das macht dann PropertyChanged Events erst möglich und benachrichtigt den Client über Werteänderungen, ok.

    Wobei mir hier die Zeile mit dem evt() Kopfzerbrechen bereitet. Erst wird evt als Variable deklariert und mit dem Wert von PropertyChanged belegt und danach wird sie als Methode aufgerufen und man übergibt "this" and diese "Methode" und erstellt einen neuen PropertyChangedEventArgs und übergibt daran etwas was vorher nirgends deklariert wurde (propertyName)?

    Danach kommt folgendes:

    private string _Header = "Item";
        public string Header
        {
            get
            {
                return this._Header;
            }
            set
            {
                if (this._Header != value)
                {
                    this._Header = value;
                    this.OnPropertyChanged("Header");
                }
            }
        }
    

    Hier wird, wenn sich der Header ändert in der Klasse das Item geändert im TreeView, korrekt?

        private ObservableCollection<MyItem> _SubItems = new ObservableCollection<MyItem>();
        public ObservableCollection<MyItem> SubItems
        {
            get
            {
                return this._SubItems;
            }
            set
            {
                if (this._SubItems != value)
                {
                    this._SubItems = value;
                    this.OnPropertyChanged("SubItems");
                }
            }
        }
    }

    Das ist jetzt etwas mit dem ich nicht viel anfangen kann. Eine Observable Collection die in der Klasse deklariert ist, welche von sich selbst abgeleitet ist? Ich gehe mal davon aus, dass eine ObservableCollection so in etwa das gleiche wie eine List<> ist, nur für andere Objekte geeignet und mit Methoden für die Überwachung. Aber wie kann ich denn <MyItem> innerhalb der Klasse "MyItem" verwenden? Und kann dieses SubItems dann auch noch verschachtelt werden? Ich brauche 6 Ebenen, eine Hauptebene und 5 Unterebenen.

    Im Codebehind des Fensters deklaarierst du nun eine Liste mit den Items

    Wie? Ich muss diese ja irgendwie als Items, SubItems, SubSubItems, SubSubSubItems, etc. definieren und dann auch irgendwie diesen Klassen hinzufügen. Wo und wie füge ich das ein? Irgendwo sehe ich noch nicht ganz die Zusammenhänge.

    ---

    und weißt den DataContext im Konstruktor zu:
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }
    
        public ObservableCollection<MyItem> Items { get; set; }

    Ich weise auf den DataContext von this, this zu? Die Logik dahinter erschließt sich mir irgendwie nicht. Würde das nicht zu einer endlosen Schleife führen beim Zugriff auf den DataContext, weil dieser ja this ist und der DataContext von this ist this.DataContext, oder was verstehe ich da falsch?

    <TreeView ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                <TextBlock Text="{Binding Header}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

    Ok, also der TreeView wird erzeugt und bekommt als DatenSource "Items" angebunden, was ja aus der Klasse MyItem stammt, müsste dann aber nicht die Deklaration MyItem.Items sein?

    Dann wird ein ItemTemplate erstellt in dem ein hierarchisches DatenTemplate definiert wird, welches als Source für seine Items "SubItems" angebunden bekommt, was ja auch wieder aus MyItem stammt.

    Und darin wird zum Schluß ein Textblock mit dem Header als Text eingebunden.

    Ich könnte also mit meiner Logik die Listen erstellen und anbinden, aber muss ich dann für jedes SubItem eine neue Liste anlegen? Das würden dann 5953 Listen (so viele unterschiedliche Gruppen und Untergruppen existieren in bis zu 6 Ebenen) werden. Das muss ich irgendwie autoamtisieren.

    Montag, 20. Oktober 2014 08:11
  • Hallo,
    INotifyPropertyChanged ist eine Schnittstelle, welche einfach nur das PropertyChanged-Event vorschreit. Die GUI Klassen von WPF wissen, dass dieses Event ausgelöst wird, wenn sich eine Eigenschaft ändert.

    Zunächst wird das Event der lokalen Variable evt zugewiesen. Die if-Abfrage danach überprüft ob das Event auch wirklich abonniert ist. Falls dem so ist, wird evt wie eine Methode aufgerufen, dass entspricht dann dem Auslösen des Events.
    Das Übergeben von this ist gängige Praxis in .NET, so wird der sender-Parameter im Eventhandler festgelegt. Der 2. Parameter sind die EventArgs, wo PropertyChanged den Eigenschaftennamen erwartet. Diesesn haben wir über den propertyName-Parameter der Methode mit gegeben.
    Im Setter der Eigenschaften wird nun einfach geprüft ob sich der Eigenschaftenwert wirklich geändert hat, wenn ja wird die Hilfsmethode zum Auslösen des Events aufgerufen.

    Es gibt viele Arten von Auflistungen, unter anderem List<T> und ObservableCollection<T>. Sie unterscheiden sich hauptsächlich in den Schnittstellen, die sie implementieren. So hast du richtig erkannt, dass ObservableCollection<T> INotifyCollectionChanged implementiert, der Auflistungsversion der INotifyPropertyChanged-Schnittstelle.
    Sie macht also nichts großartig anders, als eine List<T>, außer natürlich wieder andere über Ändeurngen zu informieren.

    Stelle dir ein TreeViewItem bitte mal als Aktenordner vor. Wenn du nun die TreeView befüllen willst, brauchst du mehrere dieser Ordner. Soll nun ein Item Unterelemenete erhalten, so musst du mehrere Ordner in den übergeordneten Ordner einbringen.
    Es verlangt also jedes Element die Möglichkeit sich Instanzen einer Klasse unterordnen zu können. Das sind die SubItems. Es ist nichts weiter als eine Auflistung, welche wieder Unterelemente enthalten kann.
    Eine Klasse kann sich problemlos selbst verwenden. Wenn du ein TreeViewItem zu den Items eines anderen Items hinzufügst machst du genau das selbe. Ebenen kannst du dabei tausende aufbauen, das interessiert .NET nicht.

    Du brauchst nun also im Codebehind nur eine Auflistung von Items, da jedes Item wieder SubItems haben kann:

    var itemA=new MyItem();
    itemA.Header = "Item A";
    itemA.SubItems.Add(new MyItem(){Header="SubItem A1"});
    itemA.SubItems.Add(new MyItem(){Header="SubItem A2"});
    itemA.SubItems.Add(new MyItem(){Header="SubItem A3"});
    this.Items.Add(itemA);
    var itemB=new MyItem();
    itemB.Header = "Item B";
    itemB.SubItems.Add(new MyItem(){Header="SubItem B1"});
    itemB.SubItems.Add(new MyItem(){Header="SubItem B2"});
    itemB.SubItems.Add(new MyItem(){Header="SubItem B3"});
    this.Items.Add(itemB);

    WPF erwartet als DataContext irgend ein Objekt dessen Eigenschaften im XAML gebundne werden können. Dabei wird nicht versucht so lange zurück zu gehen, bis kein DataContext mehr gefunden wird.
    Das Zuweisen macht einfach nur die Items-Eigenschaft im XAML direkt verfügbar.

    Im XAML bindet sich nun die TreeView an die Items-Eigenschaft im Codebehind (ermöglicht durch den DataContext). Nun wird für jedes Element der Liste das Template angwendet. Der TextBlock in der Mitte zeigt dabei die Header-Eigenschaft von dem jeweiligen Element an. Sollte nun noch eine SubItems-Eigenschaft in dem Element existieren, werden Unter-Elemente aus der Auflistung mit dem selbenTemplate erzeugt.
    Die TreeView außen hat noch den DataContext des MainWindow, also das Fenster selbst und kann somit die Items-Eigenschaft binden. Die TreeView sorgt nun dafür, dass jedes Element als DataContext das Element aus dem Codebehind als DataContext bekommt. Somit ist innerhalb des Templates nur noch Header und SubItems verfügbar.

    Du musst in WPF i.d.R. weniger manuell machen, als es unter WinForms nötig war. Nur muss man dafür die WPF Eigenen Mechanismen verstehen.
    WPF ohne Datenbindungen funktioniert zwar, ist aber unpraktikabel. Du wirst das aber auch nicht von Heute auf Morgen alles lernen, da es ein sehr weit gefächertes Thema ist.
    Der von mir hier gezeigte Code hat noch nicht mal wirklich etwas mit MVVM zu tun, da die Schichten nicht wirklich getrennt von einander sind. Die MSDN hat dazu sehr viel Lesestoff:
    Datenbindung in WPF
    Anpassen der Datenanzeige mit Datenbindung und WPF
    Datenbindung (WPF)Optimieren der Leistung: Datenbindung

    Wenn du die Zeit dazu hast, solltest du dich unbedingt damit beschäftigen. Denn es kann dir viel Arbeit ersparen. MVVM würde es bei großen Projekten nochmals verbessern.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Montag, 20. Oktober 2014 11:13
    Moderator
  • Ok... wow... ich bin ein wenig erschlagen von der Fülle an Infos.

    Erstmal ein ganz großes Danke für die Mühe. Ich versuche mich gerade in MVVM einzuarbeiten, aber ich muss sagen, dass ich da wirklich noch Zeit reinstecken muss und ich fürchte diese habe ich einfach nicht mehr.

    Jetzt schwanke ich zwischen "Ich-versuche-einfach-mal-diesen-Code-hier-zu-implementieren" und "Binde-ich-halt-meinen-alten-WinForms-Code-und-das-passende-System.Windows.Forms-Obejkt-wieder-ein".

    Ich denke ich werde noch ein paar Stunden mit MVVM verbringen und mich dann endgültig entscheiden. Vielleicht gib ich auch erstmal deinem Ansatz einen Versuch. Sieht auf jeden Fall sehr interessant aus.

    Da fällt mir ein, dass ich hier auch noch ein Buch habe, über WPF und XAML, Anfängerwissen. Vielleicht beantwortet mir das auch einige meiner Fragen... auf jeden Fall erstmal zurück zur Schulbank. :)

    Danke nochmal

    MfG

    Marcel

    Montag, 20. Oktober 2014 12:02
  • Ich habe es jetzt versucht, leider steigt mein Programm nach "this.Items.Add(itemA);" einfach aus ohne Fehlermeldung.

    Dabei habe ich mich genau an deine Vorgaben gehalten (Copy&Paste), aber irgendwo ist da der Wurm drin.

    Dienstag, 21. Oktober 2014 07:57
  • Hallo,
    irgend einen Fehler muss es geben wenn das Programm einfach zu geht. Ich gehe mal davon aus, das du das Programm aus Visual Studio heraus im Debug-Modus ausgeführt hast.

    Versuche mal einen try...catch-Block um die Zeilen herum zu setzen und halte den Debugger im catch-Block an. (BreakPoint setzen durch F9-Taste.)
    Dadurch kannst du den egnauen Fehler erfahren. Ggf. musst du auch noch die InnerException mit beachten.

    Im Momentn fallen mir 2 Situationen ein, weswegen das Hinzufügen nicht funktioniert:

    1. Die Liste wurde nicht initialisiert.
    2. Da die Liste gebunden ist, kannst du nur aus der GUI darauf zugreifen. Wilslt du aus einem anderen Thread auf die Liste zugreifen, so musst du das über die Invoke-Methode machen.

    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Dienstag, 21. Oktober 2014 14:10
    Moderator
  • Ok, der Fehler ist:

    "System.NullReferenceException: Der Objektverweis wurde nciht auf deine Objektinstanz festgelegt."

    Wenn ich mit F9 auf this.Items.Add(itemA); anhalte, bekomme ich als Info, dass itemA korrekt initialisiert wurde und die richtigen Daten beinhaltet. this ist in diesem Fall das Window-Objekt, also wohl auch korrekt. Nur das Items ist etwas arg klein geraten, wenn ich es öffne sehe ich nur das this.Items auf null steht (ok, ist ja noch nichts drin) und nur statische Member hat (müssten hier nicht mehre Dinge stehen, so Methoden, etc.?) und darin sind nur 2 Dinge enthalten: CountString = Count und IndexerName = item[].

    Wie soll ich denn in WPF invoke benutzen? Das ist doch nur für WinForms, oder nicht?


    Mittwoch, 22. Oktober 2014 07:20
  • Der genannten Fehlermeldung nach dürfte irgend etwas null, also nicht zugewiesen, sein. Das dürfte hier die Items-Auflistung sein. Diese musst du noch im Konstruktor zuweisen (vor dem DataContext), das hatte ich in meinem Code nicht gezeigt:
    this.Items=new ObservableCollection<MyItem>();
    Wie soll ich denn in WPF invoke benutzen? Das ist doch nur für WinForms, oder nicht?

    Die MSDN ist dein Freund: Dispatcher.Invoke

    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Mittwoch, 22. Oktober 2014 10:29
    Moderator
  • Ok, danke, jetzt hab ich den fehlenden Punkt gefunden. Daher war meine Frage auch weiter oben:
    <Zitat>Ok, also der TreeView wird erzeugt und bekommt als DatenSource "Items" angebunden, was ja aus der Klasse MyItem stammt, müsste dann aber nicht die Deklaration MyItem.Items sein?</Zitat>

    War vielleicht etwas undeutlich ausgedrückt von mir. Aber zumindest kann ich mir das jetzt korrekt anzeigen lassen. Was nur noch fehlt ist jetzt das auslesen bzw. ansteuern der Items. Ich muss noch rausfinden wie ich ein Item an ein anderes anfüge, dass schon seit 23 Item.Add's nicht mehr aufgerufen wurde. Mal schauen ob ich irgendwo was dazu finde, oder müsste ich jetzt hier eine Suchmethode in die Klasse einbauen?

    Edit: Ok, hab zu kompliziert gedacht, kann ja mit "foreach(MyItem NewItem in this.Items)" durchgehen und die Header vergleichen.

    Vielen Dank, Tom. Das hat mir wirklich sehr geholfen :)

    Mittwoch, 22. Oktober 2014 11:45
  • Achso, falls noch wer wissen will wie der Code am Ende bei mir aussieht:

        public partial class AdminConsole : Window
        {
            public ObservableCollection<MyItem> Items { get; set; }
    
            public AdminConsole()
            {
                this.Items = new ObservableCollection<MyItem>();
                this.DataContext = this;
                InitializeComponent();
            }
    
            private void AddWareGroups()
            {
                // Eine Datentabelle erstellen mit dem kompletten Inhalt der WareGroupDB
                DataTable MyTable = DBase.GetWareGroups();
                // Durch jeden Datensatz einzeln durchgehen
                foreach (DataRow MyRow in MyTable.Rows)
                {
                    // Die beiden wichtigen Datenfelder aus diesem Datensatz ziehen und in einzelne Variablen stecken
                    string ItemDescription = MyRow.Field<string>(1);
                    string ItemKey = MyRow.Field<string>(2);
                    // Das neue Item erstellen und mit den Daten befüllen
                    MyItem NewItem = new MyItem();
                    NewItem.Header = ItemKey + " - " + ItemDescription;
                    // Nach der Länge des Keys wird nun das Item sortiert
                    if (ItemKey.Length == 1)
                    {
                        this.Items.Add(NewItem);
                    }
                    else if (ItemKey.Length == 2 || ItemKey.Length == 5 || ItemKey.Length == 6)
                    {
                        IterateSubItems(1, NewItem, ItemKey, this.Items);
                    }
                    else // if (ItemKey.Length == 4 || ItemKey.Length == 8)
                    {
                        IterateSubItems(2, NewItem, ItemKey, this.Items);
                    }
                }
            }
    
            private void IterateSubItems(int shortenLength, MyItem newItem, string itemKey, ObservableCollection<MyItem> myCollection)
            {
                // Durch alle Subitems in der Collection durchgehen
                foreach (MyItem ItemChild in myCollection)
                {
                    // Inhalt des Headers mit dem Key vergleichen, welcher angelegt werden soll
                    if (ItemChild.Header.Substring(0, ItemChild.Header.IndexOf(" - ")).Equals(itemKey.Substring(0, itemKey.Length - shortenLength)))
                    {
                        // Subitem hinzufügen
                        ItemChild.SubItems.Add(newItem);
                        // foreach-Schleife verlassen aus Performancegründen
                        return;
                    }
                    else
                    {
                        // Eine Ebene tiefer suchen, wenn vorhanden
                        if (ItemChild.SubItems.Count > 0)
                        {
                            IterateSubItems(shortenLength, newItem, itemKey, ItemChild.SubItems);
                        }
                    }
                }
            }

    und der XAML sieht so aus:

                            <TreeView ItemsSource="{Binding Items}" Height="{Binding ElementName=AdminConsoleWindow, Path=ActualHeight, 
                                      Converter={StaticResource MathConverter}, ConverterParameter=(@VALUE/3)}">
                                <TreeView.ItemTemplate>
                                    <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                                        <TextBlock Text="{Binding Header}"/>
                                    </HierarchicalDataTemplate>
                                </TreeView.ItemTemplate>
                            </TreeView>
    und natürlich noch am Ende die Klasse MyItem einfügen wie von Tom/Koopakiller oben beschrieben.






    Donnerstag, 23. Oktober 2014 06:06