Should I use delphi tframes for multi-pages forms?

前端 未结 4 1359
轮回少年
轮回少年 2021-02-04 15:04

I have some forms in my application which have different \"states\" depending on what the user is doing; for exemple when listing through his files the form displays some data a

相关标签:
4条回答
  • 2021-02-04 15:25

    I have a word for you : TFrameStack. Simply what the name suggests.

    It has a few methods: PushFrame(AFrame), PopFrame, PopToTop(AFrame), PopToTop(Index), and a few Properties: StackTop; Frames[Index: Integer]; Count;

    • Should be self explanatory.

    The Frame at StackTop is the visible one. When doing ops like Back/Previous you don't need to know what frame was before the current one :) When creating the Frame you can create and push it in one go FrameStack.Push(TAFrame.Create) etc, which creates it calls the BeforeShow proc and makes it visible, returning its index in the stack :)

    But it does rely heavily on Inheriting your frames from a common ancestor. These frames all (in my Case) have procedures: BeforeShow; BeforeFree; BeforeHide; BeforeVisible. These are called by the FrameStack Object during push, pop and top;

    From your main form you just need to access FrameStack.Stacktop.whatever. I made my Stack a global :) so it's really easy to access from additional dialogs/windows etc.

    Also don't forget to create a Free method override to free all the frames ( if the owner is nil) in the stack when the app is shut down - another advantage you don't need to track them explicitly:)

    It took only a small amount of work to create the TFrameStack List object. And in my app work like a dream.

    Timbo

    0 讨论(0)
  • 2021-02-04 15:27

    Looks like Frames are an excellent choice for this scenario. I'd like to add that you can use a Base Frame and Visual Inheritance to create a common interface.

    And to the second part: You design a Frame like a Form but you use it like a Control, very few restrictions. Note that you could just as easily use Create/Free instead of Show/Hide. What is better depends on how resource-heavy they are.

    0 讨论(0)
  • 2021-02-04 15:27

    I also use the approach described by @Tim Sullivan with some addition. In every frame I define the procedure for the frame initialization - setting default properties of its components.

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

    And after the frame was created I call this procedure.

    aFrame := TAnyFrame.Create(nil);
    if not aFrame.initFrame() then
      FreeAndNil(aFrame)
    else
      ... // Associate frame with form and do somthing usefull
    

    Also when you change visible frame it is not necessary to destroy previous one because it can contains useful data. Imagine that you input some data in a first frame/page, then go to the next, and then decide to change data on the first page again. If you destroy previous frame you lost the data it contains and need to restore them. The solution is to keep all created frames and creates new frame only when it is necessary.

    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();
    
    0 讨论(0)
  • 2021-02-04 15:33

    There's a better way to handle dealing with the frames that won't take up nearly as much memory. Dynamically creating the frames can be a very elegant solution. Here's how I've done it in the past.

    On the form, add a property and a setter that handles the placement of it on the form:

    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;
    

    Then, to use it, you simply set the property to a created instance of the frame, for example in the OnCreate event:

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

    If you want to get rid of the frame, simply assign nil to the CurrentFrame property:

    MYFrame1.CurrentFrame := nil;
    

    It works extremely well.

    0 讨论(0)
提交回复
热议问题