Announcement

Collapse
No announcement yet.

Mehrfache Listbox-Komponenten mit selber Quelle

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Mehrfache Listbox-Komponenten mit selber Quelle

    Hallo,

    ich benötige mehrere Komponenten mit TListBoxen. Mehrere deshalb, weil diese in verschiedenen Forms angezeigt werden.

    Alle Listboxen haben jedoch die gleiche Datenbasis.

    Derzeit muß ich bei Änderung einer Listbox, die Änderung in allen restlichen Listboxen nachtragen.

    Gibt es eine Möglichkeit, daß sich die Listboxen automatisch an einer gemeinsamen Datenbasis (Liste) orientieren ?

    Helmut

  • #2
    Delphi 5: Versuche es doch einmal mit Komponenten-Vorlagen: Eine ,Basis-Listbox' mit den aktuellen Eigenschaften versehen, dann anklicken und über Komponenten | Vorlagen als Muster speichern - dann kann diese Vorlage jederzeit übernommen werden, ebenso wie eine ,selbständige' Komponente.

    (Dies funktioniert auf jeden Fall. Ich habe es allerdings noch nicht ausprobiert, bei einer Vorlage nachträglich Eigenschaften zu ändern, und weiß deshalb nicht, ob auch nachträgliche Änderungen an alle Ableitungen vererbt werden.

    Comment


    • #3
      Es geht darum, daß der Inhalt einer der vorhandenen Listboxen sich zur "Laufzeit" ändern kann. Die anderen Listboxen sollen auch von dieser Änderung erfahren.

      Wie gesagt, sie arbeiten alle mit der gleichen Datenbasis (Liste)

      Comment


      • #4
        Hallo Helmut,<p>
        ich möchte deine Frage etwas globaler beantworten. Irgendwo in diesem Forum tauchte mal im Zusammenhang mit neuen Delphi-Versionen die Frage oder Bemerkung auf, was den Borland noch alles einbauen will bzw. soll. Nun eine Möglichkeit wäre zum Beispiel eine Model-View-Controller Architektur, kurz MVC genannt. Bei dieser Architektur geht man davon aus, daß bestimmte Daten an einer Stelle im Hauptspeicher gehalten werden (Model) und daß es für den Benutzer eine oder mehrere Sichten auf diese Daten gibt (View bzw. Views). Jede View ist maximal mit einem Controller gepaart, wobei ein Controller z.B. ein Menu sein könnte über das die Daten geändert werden können. Das Model kennt alle von ihm abhängigen Views und benachrichtigt diese bei Änderungen am Datenbestand. <br>
        VisualWorks, eine Smalltalk-Entwicklungsumgebung von ParcPlace, arbeitet nach diesem Modell. Ein Smalltalk-Programmierer braucht sich also um solche Dinge keine großen Gedanken machen. Da aber Borland es vorgezogen die Linux-Jungs endlich mit einem anständigen Entwicklungswerkzeug zu beglücken anstatt die VCL mit der MVC-Architektur auszustatten müssen wir Windows-Delphi-Programmierer uns selber helfen. <p>
        Ich habe hierfür 2 Lösungen: <p>
        1. Die Quick and dirty-Methode: Das Model sei z.B. eine von TList abgeleitete Klasse. Diese Klasse enthält die Daten, die in einem Formular in einem StringGrid (Name: StringGrid1 in Form1) und in einem anderen Formular in einer ListBox (Name: ListBox1 in Form2) angezeigt werden. Die von TList abgeleitete Klasse erhält dann 2 öffentliche Variablen vom Typ Boolean die dann meinetwegen heißen:<p>

        TModel = class(TList)<br>
        public<br>
        StringGrid1InForm1NeedsUpdate: Boolean;<br>
        ListBox1InForm2NeedsUpdate: Boolean;<p>

        Im Konstruktor von TModel werden beide Variablen auf True gesetzt. Die beiden Formulare Form1 und Form2 erhalten eine
        OnActivate-Procedure und sie müssen beide das TModel-Objekt kennen. <p>

        TForm1 = class(TForm)<br>
        procedure FormActivate(Sender: TObject);<br>
        private<br>
        Model: TModel;<br>
        public<br>
        constructor Create(AModel: TModel); // Im Konstruktor muß Anweisung: Model := AModel; stehen<br>
        procedure SetModel(AModel: TModel); // Alternative.<br>
        ...<br>
        end; <p>

        procedure TForm1.FormActivate(Sender: TObject);<br>
        ...<br>
        if Model.StringGrid1InForm1NeedsUpdate then begin<br>
        FuelleStringGrid1MitDatenAusDemModel; // nur Beispiel<br>
        Model.StringGrid1InForm1NeedsUpdate := False;<br>
        end;<br>
        ...<br>
        end;<br>

        Werden die Formulare zu erstenmal angezeigt, so holen sie die Daten vom Model und zeigen sie in ihrem jeweiligen
        Steuerelement an. Beide ...NeedsUpdate-Variablen sind nun wieder im Zustand False. <br> Angenommen Form2 ist im Vordergrund
        und hat den Fokus und verdeckt Form1. Über Form2 werden die Daten des Models geändert. Nun müssen im Model die beiden
        ...NeedsUpdate-Variablen auf True gesetzt werden. Bei mir erledige ich das immer in der Methode des Models, die die
        Daten des Models auf die Festplatte speichert. Da Formular Form1 den Fokus bereits besitzt muß für dieses das
        OnActivate-Ereignis manuell ausgelöst werden damit die ListBox1 die aktuellen Daten anzeigt. Bei Form2 funktioniert
        dies automatisch sobald es den Fokus erhält. <p>

        Vorteil: Es gibt keinen Aktualisierungs-Overhead. Wenn Formular Form2 den Fokus behält und die Daten 5 mal ändert,
        so zieht Form1 alle Änderungen nur einmal in einem Rutsch durch. Falls die Forderung besteht, daß ein halbverdecktes
        Steuerelement auf einem Formular, das nicht den Fokus hat, immer die aktuellen Daten anzeigen soll, so funktioniert diese
        Methode natürlich nicht.<p>

        Nachteil: Für jede weitere View muß in der TModel-Klasse eine weitere Variable ...NeedsUpdate generiert werden und
        man muß dafür sorgen, daß sie korrekt gehandhabt wird.<p>

        Soweit so gut für heute. Die saubere Methode folgt später.<p>

        Gruß<p>

        Wolfgang Rolle

        Comment


        • #5
          Vielen Dank Wolfgang!

          Ich bin auch sehr interessiert an der sauberen Lösung. Aber ich habe jetzt mal einen Lösungsansatz.

          Gruß
          Helmu

          Comment


          • #6
            Hallo Helmut,<p>

            hier folgt die versprochene saubere Methode. Hat leider etwas länger gedauert, aber ich wollte dir keine Lösung
            anbieten, die ich nicht selber ausprobiert habe. In meinem eigenen Projekt hat bisher auch nur die Quick-and-Dirty-
            Methode exisiert und die saubere nur in meinem Kopf. Ich habe dies hier zum Anlaß genommen und bei mir die
            saubere Lösung implementiert.<p>

            Zur Erläuterung muß ich hier kurz nochmals auf Smalltalk zurückkommen. Dort ist so, daß eine View mit einem Controller
            zu einer Einheit zusammengebunden wird. Wenn z.B. ein Anwender je nach Zugehörigkeit zu einer Benutzergruppe
            unterschiedliche Zugriffsrechte auf einen Datenbestand haben soll, so kann dies in Smalltalk so realisiert werden,
            daß es zwei unterschiedliche Controller gibt, einen mit mehr Steuerungsmöglichkeiten (z.B. mit der Möglichkeit zum
            Löschen.) und einen mit weniger. Je nach Benutzergruppe wird jetzt ein und dieselbe View mit dem entsprechenden
            Controller verbunden und man erhält so ein anderes Programmverhalten. Was ich damit sagen will ist, daß bei der Smalltalk-
            Klassenbilbliothek streng zwischen Anzeigen und Ändern von Daten unterschieden wird. Bei der VCL enhalten die
            Anzeigeklassen wie Fenster und Listboxen normalerweise auch den Code zur Steuerung. Deshalb ist meine Lösung
            eigentlich nur eine Model-View-Architektur, kurz MV-Architektur. Als Bewohner Baden-Württembergs denke ich beim
            Kürzel MV unwillkürlich an Mayer-Vorfelder weshalb ich im Nachfolgenden bei MVC bleiben möchte.<p>

            Ich habe eine Unit namens MVC.pas geschrieben. Diese Unit enthält die Klasse:<p>

            TMVCDependencies = class(TList)
            private
            protected
            public
            destructor Destroy; override;
            procedure ModelHasChanged(AAdrOfModel: Pointer);
            procedure RegisterView(AAdrOfModel: Pointer; AViewForm: TForm; AUpdateProc: TUpdateProcedure);
            procedure RemoveView(AAdrOfModel: Pointer; AViewForm: TForm; AUpdateProc: TUpdateProcedure);
            procedure ViewFormGetsFocus(AViewForm: TForm);
            end;

            und eine globale Variable namens<p>

            Dependencies: TMVCDependencies;<p>

            Dieses TMVCDependencies-Objekt muß beim Programmstart instanziert und am Programmende wieder freigegeben werden.
            Dependencies übernimmt die Verwaltung der Model-Viewer-Beziehungen und sorgt dafür, daß die View-Objekte ihre Anzeige
            updaten sobald das Formular, das sie enthält, den Fokus erhält. Ein Formular das mindestens ein View-Objekt (z.B.
            eine ListBox) enthält nenne ich ein 'ViewForm'.<br>
            Für jedes View-Objekt muß eine Procedure oder Function programmiert werden, die die Daten vom Model holt und dafür
            sorgt, daß die Daten im View-Objekt wie gewünscht angezeigt werden. In meinem Beispiel mit der Quick and Dirty-Methode
            war das z.B. die Procedure 'FuelleStringGrid1MitDatenAusDemModel'. Solche Prozeduren brauchen wir auch weiterhin.<p>

            Das Dependencies-Objekt besitzt 4 öffentliche Methoden, die wie folgt eingesetzt werden:<br>
            1. Jedes ViewForm muß seine View-Objekte beim Dependencies-Objekt anmelden, und zwar jedes View-Objekt einzeln.
            Sinnvollerweise wird dies im FormCreate-Ereignis mit Hilfe der Procedure 'RegisterView()' erledigt. Die Argumente,
            die beim Aufruf übergeben werden sind: Die Adresse des Models, eine Referenz auf das ViewForm und ein Methoden-Zeiger
            auf diejenige Methode mit der die Daten vom Model in die View gelangen.<p>

            z.B. Dependencies.RegisterView(@Namensliste, self, FuelleStringGrid1MitDatenAusDemModel);<p>

            2. Wenn ein ViewForm zerstört wird, so meldet es seine Views beim Dependencies-Objekt ab. Das geschieht im
            FormDestroy-Ereignis mit Hilfe der RemoveView()-Methode. Die Argumente sind gleich wie beim Anmelden.<p>

            3. Wenn das ViewForm den Fokus erhält schickt es die ViewFormGetsFocus-Nachricht an das Dependencies-Objekt.
            Argument ist lediglich eine Referenz auf sich selber.<p>

            4. Immer wenn sich die Daten im Model geändert haben und man will, daß die Views die neuesten Daten anzeigen &#13

            Comment


            • #7
              Teil 2:<p>
              schickt man die Nachricht 'ModelHasChanged()' an das Dependencies-Objekt. Argument für diese Methode ist wieder
              die Adresse des Models. z.B. Dependencies.ModelHasChanged(@Namensliste). Das Dependencies-Objekt sorgt dann dafür,
              daß die Views des Formulars, das augenblicklich den Fokus hat, sofort upgedatet werden und merkt sich für die
              übrigen Views, daß ein Update notwendig ist. Sobald ein anderes Formular den Fokus erhält sendet es die
              ViewFormGetsFocus-Nachricht und das Dependencies-Objekt sorgt dafür, daß die Views dieses Formulars aktualisiert
              werden. Sofern eine Aktualisierung notwendig ist.<p>

              Hier nochmals das Beispiel jetzt mit der neuen Methode. Man sollte es um ein Hauptformular ergänzen, das während
              der ganzen Programmlaufzeit geöffnet ist. Dieses Hauptformular soll 'MainForm' heißen. <p>

              uses ..., MVC, ...;<p>

              procedure MainForm.FormCreate(Sender: TObject);<br>
              begin<br>
              ...<br>
              Dependencies := TMVCDependencies.Create;<br>
              ...<br>
              end<p>

              procedure MainForm.FormDestroy(Sender: TObject);<br>
              begin<br>
              ...<br>
              Dependencies.Free;<br>
              ...<br>
              end<p>

              Das Model, das in der Quick and Dirty-Methode für jede View eine entsprechende Boolean-Variable bereit halten
              mußte, muß hier überhaupt nicht modifiziert werden. Es muß überhaupt nicht über das Dependencies-Objekt, die
              ViewForms und die Views Bescheid wissen. Da das Dependencies-Objekt lediglich die Adresse des Models kennen
              muß, ist es auch völlig unerheblich von welcher Klasse das Model ist. Es muß lediglich schon vor seinen
              Views existieren. Das Model hier soll sein: <p>

              Namensliste: TList;<P>

              // Initalisierung<br>
              Namensliste := TList.Create;<p>

              Die beiden Formular-Klassen TForm1 und TForm2 müssen auch hier das Model kennen. Dies ist dann der Fall, wenn
              das Model global ist oder wenn den Formularen im Konstruktor eine Referenz auf das Model übergeben wird. Das
              Anmelden der Views kann natürlich auch im Konstruktor erfolgen.<p>

              constructor TForm1.Create(AModel: TObject);<br>
              begin<br>
              inherited Create;<br>
              Model := AModel;<br>
              Dependencies.RegisterView(@Model, self, FuelleStringGrid1MitDatenAusDemModel);<br>
              ...<br>
              end;<p>

              Beim Registrieren wird vermerkt, daß das View-Objekt ein Update benötigt. Dies bewirkt, daß das View-Objekt
              bereits beim ersten Anzeigen die Daten des Models anzeigt, sofern bereits Daten vorhanden sind. Wenn die Daten
              des Models über das TForm1-Objekt geändert werden, so könnte das so aussehen:<p>

              procedure TForm1.blabla;<br>
              var<br>
              String1: String;<br>
              begin<br>
              String1 := 'Hugo';<br>
              Model.Add(String);<br>
              Dependencies.ModelHasChanged(@Model);<br>
              end;<p>

              Wenn du willst so stelle ich dir und jedem anderen, der sich dafür interessiert, meine Unit MVC.pas im Quellcode
              zur Verfügung. Da ich noch keine eigene Homepage habe, schicke einfach eine eMail an: [email protected].
              Ich maile dann zurück.<p>

              Wolfgang Roller<br&gt

              Comment

              Working...
              X