delphiflickertframe

How to avoid display "flicker" when creating and assigning TFrames to a parent control


For example: I have a TFrame (called TPageFrame) that has a number of controls, e.g. TreeView aligned Left, splitter, and main clientarea consisting of an edit and RichEdit, as in image below:

enter image description here

Code looks something like this:

type
  TPageFrame = class(TFrame)
    Panel1: TPanel;
    Splitter1: TSplitter;
    Panel2: TPanel;
    Panel3: TPanel;
    Panel4: TPanel;
    Edit1: TEdit;
    RichEdit1: TRichEdit;
    TreeView1: TTreeView;
  private
    { Private declarations }
  public
  end;

In the Main Form I have a a RzTabControl with a few tabs. As I move to a new tab, a new Frame will be created (stored in a Frame array) and it's parent is set to the RzTabControl.

type
  TForm1 = class(TForm)
    RzTabControl1: TRzTabControl;
    procedure RzTabControl1Change(Sender: TObject);
  private
    { Private declarations }
    FFrameArr: array[0..5] of TPageFrame;
  public
    { Public declarations }
  end;

procedure TForm1.RzTabControl1Change(Sender: TObject);
var
  Index: Integer;
  PageFrame: TPageFrame;
begin
  Index := RzTabControl1.TabIndex;
  Self.Caption := Index.ToString;

  if FFrameArr[Index] = nil then
  begin
    PageFrame := TPageFrame.Create(Self);
    PageFrame.Name := 'PageFrame' + Index.ToString;
    PageFrame.Parent := RzTabControl1;
    PageFrame.Align := alClient;
    PageFrame.Visible := True;
    FFrameArr[Index] := PageFrame;
  end;
end;

Problem: While the Frame is being created and having it's parent set, there is a lot of "Display noise":

enter image description here

See how edit control is painted twice in 2 positions. (Would be easier to demonstrate with a video...)

How can this kind of flicker be avoided?


Solution

  • There are a few problems with the code by @RaelB, such as incorrect use of try/finally, not handling any exceptions that might arise to locally created variables, etc.

    The correct (IMO) code should be:

    if not Assigned(FFrameArr[Index]) then begin
      Screen.Cursor := crHourGlass;
      try
        // Defer updates
        SendMessage(Handle, WM_SETREDRAW, WPARAM(False), 0);
        try
          PageFrame := TPageFrame.Create(Self);
          try
            PageFrame.Name := 'PageFrame' + Index.ToString;
            PageFrame.Visible := False;
            PageFrame.Parent := RzTabControl1;
            PageFrame.Align := alClient;
            PageFrame.Visible := True;
            FFrameArr[Index] := PageFrame;
          except
            PageFrame.Free;
            raise
          end;
        finally
          // Make sure updates are re-enabled
          SendMessage(Handle, WM_SETREDRAW, WPARAM(True), 0);
        end;
        PageFrame.Hide;
        PageFrame.Show;
        RzTabControl1.Invalidate;
      finally
        Screen.Cursor := crDefault;
      end;
    end;