Komponenten benutzen - WriteComponent / ReadComponent - Teil 1



  • Hallo zusammen... ich habe folgendes Problem: Ich möchte beliebig viele TControls die ich auf meinem Form liegen habe in eine Datei speichern, um diese nachher wieder auszulesen und sie an der selben Stelle im Form wieder anzeigen zu lassen - sozusagen ein Schaubild speichern / Schaubild laden. Ich dachte mir, dass das Beispiel in der BCB Hilfe zu ReadComponent/WriteComponente für mich genau das richtige ist. Wenn ich den Code jedoch kopiere kriege ich immer folgende Fehlermeldung:

    [C++ Fehler] ArbeitsFlaeche.cpp(683): E2459 Klassen im VCL-Stil müssen mit dem Operator new erstellt werden
    

    Diese Fehlermeldung bezieht sich immer auf die return Zeile... Kann mir da vielleicht jemand weiter helfen?
    P.S. Auch wenn ich pc mit new erstelle kommt die gleiche Fehlermeldung...

    TComponent TFArbeitsFlaeche::StringToComponent(AnsiString as)
    {
      TMemoryStream* pms = new TMemoryStream();
      TStringStream* pss = new TStringStream(as);
      TComponent *pc;
      TComponent* temp;
    
      try
      {
        ObjectTextToBinary(pss, pms);
        pms->Seek(0, soFromBeginning);
      }
      catch(...)
      {
        ShowMessage("Streaming error.");
      }
    
      pc = pms->ReadComponent(NULL);
      delete pms;
      delete pss;
      return *pc;
    }
    


  • TComponent *pc;
    ...
    return *pc;
    

    Wie Du sicherlich noch weisst dereferenziert (heisst das wirklich so ? 😉 der * den Zeiger. Um das zurückgeben zu können was Du versuchst müsste der Compiler TComponent als temporäre Variable erstellen. (TComponent temp) . Die VCL Klassen müssen jedoch mit new erzeugt werden. Somit kann auch nur ein Zeiger der Elemente weitergegeben werden.



  • hmm - sorry, irgendwie komm ich noch nicht drauf...

    TComponent *pc;
    ...
    return *pc;
    

    ist doch genau das was in meinem Quelltext steht, und damit gebe ich doch auch nur einen Zeiger weiter, oder? Versteh das ganze noch nicht so ganz :(...

    [ Dieser Beitrag wurde am 24.06.2002 um 15:26 Uhr von DerChristoph editiert. ]





  • Hmm - ne leider nicht... ich muss die Komponenten auch zur Laufzeit erzeugen können - halt so ähnlich wie wenn man bei Corel ne Zeichnung lädt... Die Methode von Bytes and More kann leider nur die schon (zur Entwicklungszeit) erzeugten Objekte neu anordnen... Trotzdem schon mal dankeschön... konnte den Teil zum Verschieben von den Elemente gut gebrauchen :)... Weiss sonst vielleicht noch jemand ne Lösung zu meinem Problem? Brauch das nämlich recht dringend...



  • Original erstellt von DerChristoph:
    [...]und damit gebe ich doch auch nur einen Zeiger weiter, oder? Versteh das ganze noch nicht so ganz :(...

    Nein, genau eben nicht. Du hast bereits einen Zeiger. Wenn du diesen nun dereferenzierst (sternchen vor die Variable) dann gibst du das Objekt und nicht den zeiger zurück!

    Ich würde unbedingt mal die Grundlagen zum Thema Zeiger in C/C++ durchlesen!

    -junix



  • hmm - klingt eigentlich sehr schlüssig - aber trotzdem steht das Beispiel exakt so in der BCB Hilfe... naja, werd das auf jeden Fall morgen früh wenn ich wieder nach Hause komme direkt mal ausprobieren und mir dann wahrscheinlich doch mal die Zeit nehmen so'n Tutorial durchzugehen...



  • Hi,

    http://rad.bytesandmore.de/cpp/snipp/sc02036.php macht genau das!

    ähm nein. Eben nicht. Das Beispiel speichert nur einzelne properties in ini-files. mit ReadComponent/ WriteComponent kann man die Komponenten komplett speichern. Inclusive alle Komponenten, die dieser Komponente untergeordnet sind.

    Das Beispiel aus der Hilfe, welches Christoph meint, ist falsch. Sogesehen kann er da nichts für.

    Grundproblem fes speicherns ist, das man keine VCL-Komponenten dereferenzieren kann und man keinen Pointer speichern kann. Können schon, es macht aber wenig Sinn.

    Um das Problem zu umgehen, benutzt man die Methoden:
    ObjectTextToBinary und ObjectBinaryToText

    Ich hab mir mal erlaubt, das Beispiel aus der Hilfe etwas umzuschreiben:

    //---------------------------------------------------------------------------
    class TForm1 : public TForm
    {
    __published:    // Von der IDE verwaltete Komponenten
       TButton *save;
       TListBox *ListBox1;
       TButton *load;
       void __fastcall saveClick(TObject *Sender);
       void __fastcall loadClick(TObject *Sender);
    private:    // Anwender-Deklarationen
    public:     // Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    
    // cpp
    //***************************************************************************
    #include <vcl.h>
    #pragma hdrstop
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //----------------------------------------------------------------------------
    bool LoadComponentFromFile(AnsiString Name,TComponent* temp);
    bool SaveComponentToFile(TComponent*comp);
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    bool SaveComponentToFile(TComponent*comp)
    {
     AnsiString as;
     TMemoryStream* pms = new TMemoryStream();
     TStringStream* pss = new TStringStream(as);
     TStringList *file=new TStringList;
    try
     {
        pms->WriteComponent(comp);
        pms->Seek(0, soFromBeginning);
        ObjectBinaryToText(pms, pss);
        pss->Seek(0, soFromBeginning);
        file->Clear();
        file->Text=pss->DataString;
        file->SaveToFile((String)"C:\\"+comp->Name);
        delete file;
        delete pms;
        delete pss;
        return true;
     }
    catch(...)
     {
        ShowMessage("Can't save the component");
        if(file) delete file;
        if(pms) delete pms;
        if(pss) delete pss;
        return false;
     }
    }
    //------------------------------------------------------------------------------
    bool LoadComponentFromFile(AnsiString Name,TComponent* temp)
    {
       TStringList*file=new TStringList;
       file->Clear();
       file->LoadFromFile((String)"C:\\"+Name);
       TStringStream* pss = new TStringStream(file->Text);
       TMemoryStream* pms = new TMemoryStream();
    
    try
     {
        ObjectTextToBinary(pss, pms);
        pms->Seek(0, soFromBeginning);
        pms->ReadComponent(temp);
        delete pms;
        delete pss;
        return true;
     }
     catch(...)
     {
        ShowMessage("Can't load the component");
        if(pms) delete pms;
        if(pss) delete pss;
        temp=NULL;
        return false;
     }
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::saveClick(TObject *Sender)
    {
    ListBox1->Items->Add("ich bin eigentlich die Listbox1");
    SaveComponentToFile(ListBox1);
    delete ListBox1;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::loadClick(TObject *Sender)
    {
    TListBox*ListBox2=new TListBox(this);
    ListBox2->Parent=this;    // Parent muss gesetzt werden, da diese Eigenschaft
                            //nicht gespeichert wird und nicht NULL sein darf
    if(LoadComponentFromFile("ListBox1",ListBox2))
       ListBox2->Left+=300;
    }
    //---------------------------------------------------------------------------
    

    so kann man ganze Fromulare speichern.



  • Hey Klasse, dankeschön... das war erstmal genau das was ich brauche 🙂 Aber gibts noch irgendeine Möglichkeit den Parent mitzuspeichern, bzw vor dem Auslesen festzustellen was für ein Objekttyp ausgelesen wird? ich habe in meinem Programm einige eigene Klassen, mit "Unterobjekten" die jetzt im Moment als eigene Objekte behandelt/gespeichert werden, obwohl ich sie ja in einer Klasse kapseln will... geht das auch irgendwie?



  • Hi,

    den Parent zu speichern macht wenig Sinn. Jedesmal, wenn du die Komponenten lädst, ist der Parent ja ein anderer.
    Ausserdem ist der Parent auch nur ein Objekt. Du erntest dadurch nur Zugriffsverletztungen oder Meldungen wie "Objekt hat kein übergeordnetes Fenster".
    Du köntest allerding hingehen und das Parent- Objekt speichern anstatt die Unterkomponenten.
    So sieht eine gespeicherte Datei aus, in der ein Formular gespeichert wurde:

    //..................................................
    object Form1: TForm1
      Left = 192
      Top = 107
      Width = 696
      Height = 480
      Caption = 'Form1'
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'MS Sans Serif'
      Font.Style = []
      OldCreateOrder = False
      PixelsPerInch = 96
      TextHeight = 13
    end
    

    Ja, also exact so wie eine Fromulardatei. Um nun die Variablen deiner Klassen beim Speichervorgang mit zu berücksichtigen müssen diese als __properties unter __published deklariert sein. Ausserdem muss deine Klasse(n) zumindest von TPresistent abgeleitet sein. Dort werden die Methoden für das schreiben von Komponentenn implementiert.

    Wenn du andere Klassen innerhalb deiner Klasse verwendest, so müssen deren Eigenschaften natürlich auch __propierty's sein.
    Im Normalfall werden diese aber nicht mitgespeichert. Um dieses zu ändern gibt es mehrere Möglichkeiten:

    1. du überschreibst die Methode DefineProperties und erstellst Wrieter und Reader-Methoden, die die Unterklassen in der Verschachtelungstiefe abspeichern.
    Ist allerdings Zeitraubend.

    2. Du definierts die unterklassen als SubKomponenten :

    FDataSource=new TDataSource(this);
    FDataSource->Name="DataSource";
    FDataSource->SetSubComponent(this);
    

    So gehst du durch die Verschachtellungstiefe. Jede Klasse die eine Unterklasse besitzt passt du so an. In der Owner- Klasse werden alle Properties gespeichert.

    3. Du definierst die Unterklassen als Eigenschaft selbst und schriebst eigene Property-Editoren, die das setzen der Klasseninstanzen durchführen und das speichern der SubKlassen übernimmt.
    Hierzu leitest du eine Klasse von TPropertyEditor ab.

    Die einfachere Lösung wäre wohl Lösung 2.
    [ Dieser Beitrag wurde am 02.08.2002 um 15:34 Uhr von AndreasW editiert. ]

    <edit>Das Problem mit den Code-Tags behoben</edit>

    [ Dieser Beitrag wurde am 02.08.2002 um 17:18 Uhr von junix editiert. ]


Anmelden zum Antworten