Klasse an TTreeNode hängen. (Besser: Instanz einer Klasse)



  • Hallo zusammen,

    ich arbeite im Moment an einer Datenbank-Anwendung, bei der Kategorien aus einer Datenbank gelesen werden. Die Kategorien basieren auf 'Nested-Sets'.
    Ich hab es bereits hinbekommen dass ein TTreeView mit den Kategorien gefüllt wird, bis auf eine Kleinigkeit und dazu benötige ich den Right-Wert von den vorherigen Kategorien.

    Jetzt stehe ich vor dem Problem, dass ich an TTreeNode->Data eine Klasse mit dem Left-Wert und Right-Wert der jeweiligen Kategorie anhängen muss.
    Das eigentliche Anhängen (Zeiger zu Void casten, anhängen und vice versa) bereitet mit keine Probleme. Ein Problem hab ich allerdings mit der anzuhängenden Klasse und zwar habe ich die Vermutung das ich ständig die selbe Klasse (Zeiger in TTreeNode hat immer den gleichen Wert) an den Node hänge.

    void TfrMain::RedrawTreeView (void)
    {
        TKategorie *KategorieTemp = new TKategorie;
    
        TTreeNode *tnKategorie, *tnKategorieNew;
        tnKategorie = NULL;
        tnKategorieNew = NULL;
        KategoriePos = Kategorie.begin();
    
        while (KategoriePos < Kategorie.end()){
    
            if (KategoriePos->GetNestedLeft() == KategoriePos->GetNestedRight() - 1){                           // Kategorie hat keine Child-Kategorie
                tnKategorieNew = tvKategorie->Items->AddChild(tnKategorie, KategoriePos->GetKategorie());
                }
            else {
                tnKategorieNew = tnKategorie = tvKategorie->Items->AddChild(tnKategorie, KategoriePos->GetKategorie());
                }
    
            KategorieTemp->SetIndexKategorie(KategoriePos->GetIndexKategorie());                                 // Daten in temporärer Klasse speichern
            KategorieTemp->SetKategorie(KategoriePos->GetKategorie());
            KategorieTemp->SetNestedLeft(KategoriePos->GetNestedLeft());
            KategorieTemp->SetNestedRight(KategoriePos->GetNestedRight());
            tnKategorieNew->Data = (void*) KategorieTemp;                                                        // Klasse nach void casten und an TreeNode anhängen
    
            KategoriePos++;
            }
        delete KategorieTemp;
    }
    

    Es ist mir inzwischen Klar, das es nicht wie in meinem Code funktionieren kann, da ich die Elementvariablen in meiner Klasse nur überschreibe und ständig die gleiche Klasse anhänge.
    Leider bringt es auch nichts wenn ich die Funktion innerhalb der Schleife generiere und lösche.

    Wie bekomme ich es hin, dass jeweils eine neue oder eben eine Kopie der Klasse an den Node geheftet wird?

    Vielen Dank im voraus

    Arno



  • Indem du diese Zeile

    TKategorie *KategorieTemp = new TKategorie;
    

    mit in die while-Schleife nimmst und somit bei jedem Durchlauf eine neue Instanz erzeugst, statt die bestehende zu ändern.

    Gruß KK



  • Danke für die schnelle Antwort, aber wie gesagt, dass funkioniert eben auch nicht.



  • Hab ich glatt übersehen... Wieso löschst du die überhaupt wieder?!? Du hängst doch an die Node nur einen Zeiger. Wenn Du die Instanz löschst, ist der Zeiger, den Du an das Node gehängt hast, ungültig. Du darfst die Instanz erst löschen, wenn du sie nicht mehr brauchst.

    Gruß KK



  • Hallo

    Btw hängst du keine Klasse an TTreeNode, sondern Instanzen deiner Klasse an Instanzen der Klasse TTreeNode.

    bis bald
    akari



  • Vielen Dank für die Antworten.

    @K-K: Jo werd das mal ohne delete versuchen.

    @Akari: Ok, wenn ich mir meine Source so ansehe werden in meinem Code nirgendwo Instanzen von TTreeNode gespeichert. D.h. dann ich brauche außerdem noch ein TTreeNodes um die Instanzen zu verwalten, richtig?

    Gruß

    Arno



  • Hallo

    Oje, du hast noch immer keinen Durchblick über die Speicherverwaltung.

    @K-K: Jo werd das mal ohne delete versuchen.

    Nein, das ist nicht die Lösung. Das delete ist nur an dieser Stelle falsch.

    Ok, wenn ich mir meine Source so ansehe werden in meinem Code nirgendwo Instanzen von TTreeNode gespeichert. D.h. dann ich brauche außerdem noch ein TTreeNodes um die Instanzen zu verwalten, richtig?

    Um die TTreeNode-Instanzen brauchst du dich nicht zu kümmern. Die werden durch AddChild erstellt und vom TTreeCtrl gelöscht wenn es nötig ist. Das ist nicht das Problem.

    Sondern du must dich um die Instanzen von TKategorie kümmern, die du erstellen und löschen must. Killer-Kobold hat ja schon gesagt das du pro Node eine TKategorie brauchst, nicht nur eine für den ganzen Tree!
    Und diese Instanzen darfst du auch nicht gleich wieder im RedrawTreeView löschen, sondern erst wenn die einzelnen Nodes gelöscht werden.

    bis bald
    akari



  • Zum Löschen der Instanzen bietet sich das Ereignis TTreeView::OnDeletion() an. Dieses wird, beim Löschen des TreeViews, für jede Node einmal aufgerufen.



  • @Akari: Jo da hapert es noch sehr.

    @Joe: Danke für den Tipp.

    Mal ne blöde Frage, ist es möglich ein Vektor-Element nach Void zu casten, denn ich hab ja bereits Instanzen meiner Klasse in einem Vektor.
    KategoriePos ist:
    vector <TKategorie>:: iterator ....?

    Dieser Vektor wird in FormShow mit Instanzen meiner Klasse TKategorie aus der Datenbank gefüllt.

    Gruß

    Arno



  • Hmmm, ok, das vector::iterator nach void geht nicht weil vector::iterator kein pointer ist. Habs gerade gefunden.



  • Jetzt ist mir gerade eingefallen, dass man mit Pointern ja auf alles Zeigen kann und somit eine Klasse auch auf sich selbst.

    Was haltet Ihr von diesem Ansatz:
    Eine Elementfunktion die einen Pointer auf die eigene Instanz der Klasse zurück gibt.

    TKategorie* TKategorie::GetPtr (void)
    {
        return this;
    {
    

    Funktionieren tut es soweit ich es jetzt auf die schnelle getestet habe.

    Ist das so Ok, oder ist das totaler Murks?

    Danke im Voraus

    Gruß

    Arno

    Edit:

    Das hab ich noch vergessen:

    TTreeNode *tnKategorie, *tnKategorieNew;
        TTreeNodes *tnsKategorie = tvKategorie->Items;
    
        tnKategorie = NULL;
        tnKategorieNew = NULL;
        KategoriePos = Kategorie.begin();
    
        tvKategorie->Items->Clear();
    
        while (KategoriePos < Kategorie.end()){
    
            if (KategoriePos->GetNestedLeft() == KategoriePos->GetNestedRight() - 1){                               // Kategorie hat keine Child-Kategorie
                tnKategorieNew = tnsKategorie->AddChild(tnKategorie, KategoriePos->GetKategorie());
                }
            else {
                tnKategorieNew = tnKategorie = tnsKategorie->AddChild(tnKategorie, KategoriePos->GetKategorie());
                }
            tnKategorieNew->Data = (void*) KategoriePos->GetPtr();                                                  // Klasse nach void casten und an TreeNode anhängen
    
            KategoriePos++;
            }
    

    Wie gesagt die Instanzen meiner Klasse sind schon in einem Vektor.



  • Hallo

    Was haltet Ihr von diesem Ansatz:
    Eine Elementfunktion die einen Pointer auf die eigene Instanz der Klasse zurück gibt.

    Falsch ist es nicht, notwendig in deinem Fall aber auch nicht. Du kannst auch direkt aus dem Iterator heraus casten, wenn du es richtig machst. Und da wir C++ programmieren, nehmen wir auch den C++ cast-Operator :

    tnKategorieNew->Data = static_cast<void*>(&*KategoriePos); 
    // Mit &*KategoriePos bekommst du einen normalen Zeiger aus dem Iterator
    // Mit static_cast<void*>(...) castest du den Zeiger auf void
    

    Ansonsten sehe ich in deinem Codeausschnitt keinen Fehler mehr. Allerdings noch eine Anmerkungen :
    Offenbar verwendest du noch globale Variablen, wie zum Beispiel tnKategorie. Wenn es keinen zwingenden Grund gibt (und den gibt es selten), dann solltest du daraus lokale Variablen machen. Ansonsten sind auch Membervariablen besser als globale.

    bis bald
    akari



  • Vielen Dank.

    Ich werd mal sehen ob ich auch ohne globale Variablen auskomme.

    Gruß

    Arno


Anmelden zum Antworten