2009-06-07 8 views
9

Ich habe einige Formulare in meiner Anwendung, die verschiedene "Zustände" haben, abhängig davon, was der Benutzer macht; beispielsweise zeigt das Formular beim Auflisten seiner Dateien einige Daten über diese Datei in einem Raster an, aber wenn er auf eine Schaltfläche klickt, wird das Raster durch eine Grafik ersetzt, die sich darauf bezieht. Einfach gesagt, die Steuerelemente im Formular hängen davon ab, was der Benutzer tun möchte.Soll ich Delphi-Tframes für Formulare mit mehreren Seiten verwenden?

Natürlich war das das Zeigen/Verstecken von Steuerelementen nach Bedarf, funktioniert wie ein Zauber für kleine Zahlen, aber sobald Sie 10/15 + Steuerelemente pro Zustand (oder mehr als 3 Zustände wirklich) erreichen, ist es unbrauchbar.

Ich experimentiere gerade mit TFrames: Ich erstelle einen Rahmen für jeden Zustand, ich erstelle dann eine Instanz von jedem Rahmen auf meinem Formular übereinander und dann zeige ich nur die mit Visible - while einige Kontrollen darüber, aus irgendeinem Rahmen, da sie alle sie teilen.

Ist das der richtige Weg, um zu tun, was ich will, oder habe ich etwas auf dem Weg vermisst? Ich dachte, ich könnte nur eine Tframe-Instanz erstellen und dann auswählen, welche in der Tframe-Instanz angezeigt werden soll, aber das sieht nicht so aus.

Dank

Antwort

17

wie Frames Looks ist eine ausgezeichnete Wahl für dieses Szenario. Ich möchte hinzufügen, dass Sie einen Basisrahmen und visuelle Vererbung verwenden können, um eine gemeinsame Schnittstelle zu erstellen.

Und zum zweiten Teil: Sie entwerfen ein Frame wie ein Formular, aber Sie verwenden es wie ein Steuerelement, sehr wenige Einschränkungen. Beachten Sie, dass Sie Create/Free statt Show/Hide verwenden können. Was besser ist, hängt davon ab, wie ressourcenintensiv sie sind.

10

Es gibt eine bessere Möglichkeit, mit Frames umzugehen, die nicht so viel Speicher benötigen. Die dynamische Erstellung der Rahmen kann eine sehr elegante Lösung sein. So habe ich es in der Vergangenheit gemacht.

Auf dem Formular Objekte hinzufügen und einen Setter, die die Platzierung der es auf dem Formular Griffe:

TMyForm = class(TForm) 
private 
    FCurrentFrame : TFrame; 
    procedure SetCurrentFrame(Value : TFrame); 
public 
    property CurrentFrame : TFrame read FCurrentFrame write SetCurrentFrame; 
end; 

procedure TMyForm.SetCurrentFrame(Value : TFrame) 
begin 
    if Value <> FCurrentFrame then 
    begin 
    if assigned(FCurrentFrame) then 
     FreeAndNil(FCurrentFrame); 
    FCurrentFrame := Value; 
    if assigned(FCurrentFrame) then 
    begin 
     FCurrentFrame.Parent := Self; // Or, say a TPanel or other container! 
     FCurrentFrame.Align := alClient; 
    end; 
    end; 
end; 

Dann, es zu benutzen, geben Sie einfach die Eigenschaft auf eine erstellte Instanz des Rahmens festgelegt, zum Beispiel in der OnCreate Ereignis:

MyFrame1.CurrentFrame := TSomeFrame.Create(nil); 

Wenn Sie möchten, des Rahmens, um loszuwerden, einfach nil auf die current Eigenschaft zuweisen:

MYFrame1.CurrentFrame := nil; 

Es funktioniert sehr gut.

+0

+1, aber beachten Sie, dass der Code, wie es lädt leckt Speicher. Es sei denn, MYFrame1.CurrentFrame wird explizit auf Null gesetzt oder wenn MYFrame1 zerstört wird, wird es nicht freigegeben. Es wäre besser, MyFrame1 als Eigentümer an den Konstruktor zu übergeben und die automatische Lebensdauerverwaltung in der VCL zu verwenden. – mghie

+0

@mghie: IIRC das Parent eines Controls übernimmt auch das Lifetime Management, also sollte es keinen Speicherleck in Tims Code geben. –

+0

Eigentlich denke ich, wenn Sie statt TSomeFrame.Create (nil) TSomeFrame.Create (Self) aufrufen, die das Formular zum Eigentümer des Frames machen würden, und das Formular würde es freigeben, wenn es selbst zerstört wird. Alternativ könnten Sie CurrentFrame im OnDestroy-Ereignis auf null setzen. Das Wechseln von einem Bild zum anderen führt nicht zu Speicherverlusten, da der Setter den FCurrentFrame freigibt, bevor er auf ein anderes Bild gesetzt wird. –

2

Ich habe ein Wort für dich: TFrameStack. Einfach, was der Name suggeriert.

Es hat ein paar Methoden: PushFrame (AFrame), PopFrame, PopToTop (AFrame), PopToTop (Index), und ein paar Eigenschaften: StackTop; Frames [Index: Ganzzahl]; Anzahl;

  • Sollte selbsterklärend sein.

Der Frame bei StackTop ist der sichtbare. Wenn Sie Operationen wie Back/Previous ausführen, müssen Sie nicht wissen, welcher Frame vor dem aktuellen war :) Wenn Sie den Frame erstellen, können Sie ihn in einem Rutsch erstellen und schieben FrameStack.Push (TAFrame.Create) etc, die es erstellt ruft den BeforeShow proc und macht es sichtbar und gibt seinen Index im Stapel zurück :)

Aber es ist stark darauf angewiesen, Ihre Frames von einem gemeinsamen Vorfahren geerbt. Diese Frames haben alle (in meinem Fall) Prozeduren: BeforeShow; BeforeFree; Vorher ausblenden; VorVisible. Diese werden vom FrameStack-Objekt während Push, Pop und Top aufgerufen.

Von Ihrem Hauptformular müssen Sie nur auf FrameStack.Stacktop.whatever zugreifen. Ich habe meinen Stack zu einem globalen :) gemacht, so dass es wirklich einfach ist, über zusätzliche Dialoge/Fenster usw. zu gelangen.

Vergessen Sie auch nicht, eine Free Methode Override zu erstellen, um alle Frames freizugeben (wenn der Besitzer null ist) stapeln, wenn die App heruntergefahren ist - ein weiterer Vorteil, den Sie nicht explizit verfolgen müssen :)

Es dauerte nur ein wenig Aufwand, um das TFrameStack List-Objekt zu erstellen. Und in meiner App arbeite ich wie ein Traum.

Timbo

0

Ich benutze auch den Ansatz beschrieben von @ Tim Sullivan mit einigem Zusatz. In jedem Frame definiere ich die Prozedur für die Frame-Initialisierung - Einstellung der Standardeigenschaften seiner Komponenten.

TAnyFrame = class(TFrame) 
    public 
    function initFrame() : boolean; // returns FALSE if somesthing goes wrong 
    ... 
end; 

Und nachdem der Rahmen erstellt wurde, rufe ich diese Prozedur auf.

Auch wenn Sie den sichtbaren Rahmen ändern, müssen Sie den vorherigen nicht löschen, da er nützliche Daten enthalten kann. Stellen Sie sich vor, dass Sie einige Daten in einem ersten Frame/einer ersten Seite eingeben, dann zum nächsten gehen und dann entscheiden, die Daten auf der ersten Seite erneut zu ändern. Wenn Sie den vorherigen Frame zerstören, haben Sie die Daten verloren und müssen sie wiederherstellen. Die Lösung besteht darin, alle erstellten Rahmen beizubehalten und nur dann einen neuen Rahmen zu erstellen, wenn dies erforderlich ist.

page_A : TFrameA; 
page_B : TFrameB; 
page_C : TFrameC; 
current_page : TFrame; 

// User click button and select a frame/page A 
if not assigned(page_A) then begin 
    // create and initialize frame 
    page_A := TFrameA.Create(nil); 
    if not page_A.initFrame() then begin 
    FreeAndNil(page_A); 
    // show error message 
    ... 
    exit; 
    end; 
    // associate frame with form 
    ... 
end; 
// hide previous frame 
if assigned(current_page) then 
    current_page.hide(); 
// show new page on the form 
current_page := page_A; 
current_page.Show(); 
Verwandte Themen