0

I expect to see fully initialized UI with a label showing "Data is not loaded", then after 5 seconds "Data is loaded".

I've taken an example from there: What is the best way to autostart an action after OnShow event?

but I see nothing, and then only "Data is loaded", after the delay.

Code is listed below:

StartupAction.dpr

program StartupAction;

uses
  Vcl.Forms,
  MainForm in 'MainForm.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

MainForm.dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 254
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object lbl1: TLabel
    Left = 104
    Top = 40
    Width = 87
    Height = 13
    Caption = 'Data is not loaded'
  end
end

MainForm.pas

unit MainForm;
//Load data after full UI initialization

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    lbl1: TLabel;
  private const
    WM_STARTUP = WM_USER;
  protected
    procedure DoShow(); override;
  private
    procedure WMStartup(var Msg: TMessage); message WM_STARTUP;
    procedure _loadData();
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DoShow();
begin
  inherited;
  PostMessage(Handle, WM_STARTUP, 0, 0);
end;

procedure TForm1.WMStartup(var Msg: TMessage);
begin
  inherited;
  _loadData();
end;

procedure TForm1._loadData();
begin
  Sleep(5000);
  lbl1.Caption := 'Data is loaded';
end;

end.

The only solution that works to some extent is using of TTimer with 500 ms delay, but it is not a true solution.

Paul
  • 25,812
  • 38
  • 124
  • 247
  • 1
    The main thread is busy loading data. So it can't display UI. Load the data in a different thread. – David Heffernan Jan 31 '18 at 07:49
  • @David Heffernan: Of course. But I would like to start data loading after becoming a signal that UI is fully drawn. How to achieve this? – Paul Jan 31 '18 at 08:07
  • 2
    No, that's the wrong approach. Start the data loading in a different thread. If it starts before the UI has finished drawing then it will finish sooner. Once it is in a different thread it won't hold up the UI. – David Heffernan Jan 31 '18 at 08:21
  • @Paul "*I would like to start data loading after becoming a signal that UI is fully drawn. How to achieve this?*" - you already know how to do that. Using `PostMessage()` in the `OnShow` event will work just fine. Simply have the message handler start a worker thread to load the data, instead of loading the data directly. Let the worker thread notify the main UI thread when the load is finished. – Remy Lebeau Jan 31 '18 at 18:16
  • @Remy Lebeau: I could also start a worker thread without involving PostMessage. Why to have an intermediate layer? – Paul Feb 01 '18 at 07:08
  • 1
    @Paul, there's no need for that message handler as you realized. You can simply start that worker thread from `OnShow` event. – Victoria Feb 01 '18 at 12:37
  • 1
    @Paul: "*Why to have an intermediate layer?*" - because you said you require to "*start data loading **after becoming a signal that UI is fully drawn***". The `OnShow` event is fired *before* the window is actually visible onscreen, so `PostMessage()` could delay starting the thread until after the window is actually visible and drawn for the first time. But, I think that is overkill. I agree with Victoria, just start the thread in `OnShow` even though the window is not actually visible yet. It will be by the time the thread is finished and syncs its status back to the UI thread. – Remy Lebeau Feb 01 '18 at 19:49

0 Answers0