Einzelnen Beitrag anzeigen

Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#11

AW: "Bitte warten"-Formular korrekt anzeigen

  Alt 26. Aug 2010, 00:33
Hallo,

auch auf die Gefahr hin, dass ich mich jetzt fürchterlich blamiere, stelle ich diesen Quelltext online. Es ist verdammt spät und ich habe kaum noch Auffassungsgabe.

Also, mein Ziel ist es, einen "Bitte-Warten"-Dialog schnell und einfach zu erstellen, auf dem permanent eine Marquee-Bar oder Statusbar zu sehen ist und ein entsprechendes Aktions-Label. Alle drei Komponenten sollen regelmäßig aktualisiert werden, während im Hauptprogramm Berechnungen durchgeführt werden.

Also habe ich ein vorgebasteltes Formular, einen Thread und eine "Wrapper-Klasse". Das Formular wird nicht automatisch erstellt. Das Dialog-Fenster wird komplett über die Wraptter-Klasse gesteuert. Es wird durch sie ein Thread erzeugt, der das Formular erzeugt. Der Thread wird mit einer while-Schleife am Leben gehalten, bis in der Wrapper-Klasse "Finished:=True" gesetzt ist.

Problem ist, dass die Komponenten nicht aktualisiert werden. Erst, wenn alle Berchnungen fertig sind, werden die Komponenten neu gezeichnet und die Anwendung friert während der Berechnungen ein.

Hier der Quelltext, extra schön kommentiert.

Anwendung des Warten-Dialogs in der Hauptanwendung
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  w: TWaitForm;
  i,j: Integer;
begin
  w:=TWaitForm.Create;
  w.CreateWaitForm;
  for i:=0 to 100000 do
  begin
    randomize();
    j:=random(9999);
    w.AddMessage(IntToStr(j));
  end;
  w.Finished:=True;
  //w.DestroyWaitForm;
end;
Bevor der Quelltext der Unit kommt, eine kurze Erklärung, wie was abläuft:
  1. Wrapper-Objekt wird erstellt (TWaitForm.Create)
  2. CreateWaitForm (Thread wird erstellt => Execute erstellt Form via Synchronize über Wrapper-Fkt "ThreadCreateWaiForm)
  3. Execute zeichnet in Schleife Form über Synchronize neu, bis Wrapper-Finished=True
  4. MainVCL-Aufruf: Wrapper.AddMessage ruft Thread.SetMessage auf, dieser setzt Variable für Aktionslabel und ruft Wrapper-ThreadAddMessage via Synchronize auf

Nun endlich der Code, bei dem durch Test noch veraltete Codefragmente bestehen könnten

Delphi-Quellcode:
type
  //Das ist das vorgefertigte Formular
  TfrmWarten = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
    lblAktion: TLabel;
    Label3: TLabel;
    StatusBar: TProgressBar;
    lblPercent: TLabel;
    MarqueeBar: TUbuntuProgress;
  private
  public

  end;

  //Eine Forward-Deklaration
  TWaitThread = class;

  //Das ist die Wrapper-Klasse, die einfach nur die Handhabung
  //im Hauptquelltext erleichtern soll
  TWaitForm = class(TObject)
  private
    WartenForm: TfrmWarten;
    wt: TWaitThread;
    fFinished: Boolean;
  public
    procedure CreateWaitForm;
    procedure DestroyWaitForm;
    procedure AddMessage(Value: String);

    //Prozeduren, die vom Thread per Synchronize aufgerufen werden
    procedure ThreadCreateWaitForm;
    procedure ThreadShowWaitForm;
    procedure ThreadAddMessage;

    property Finished: Boolean read fFinished write fFinished;
  end;

  //Das ist der Thread, der für die Aktualisierung der Komponenten
  //zuständig ist.
  TWaitThread = class(TThread)
  private
    fCreated: Boolean;
    fwf: TWaitForm;
    ffrmWarten: TFrmWarten;
    fMsg: String;
    procedure ShowWaitForm;
  public
    procedure SetMessage(Value: String);
    property FormCreated: Boolean read fCreated write fCreated;
    property WaitFormWrapper: TWaitForm read fwf write fwf;
    property Msg: String read fMsg write fMsg;
  protected
    procedure Execute; override;
  end;

var
  frmWarten: TfrmWarten;

implementation

{$R *.dfm}

{************** TWaitThread ***************}

procedure TWaitThread.Execute;
begin
  inherited;
  Synchronize(WaitFormWrapper.ThreadCreateWaitForm);
  Synchronize(WaitFormWrapper.ThreadShowWaitForm);
  while not (WaitFormWrapper.Finished) do
  begin
    //Application.ProcessMessages;
    Synchronize(WaitFormWrapper.ThreadShowWaitForm);
  end;
end;

procedure TWaitThread.ShowWaitForm;
begin
  Synchronize(WaitFormWrapper.ThreadShowWaitForm);
end;

procedure TWaitThread.SetMessage(Value: string);
begin
  Msg:=Value;
  Synchronize(WaitFormWrapper.ThreadAddMessage);
end;

{************** TWaitForm ***************}

procedure TWaitForm.CreateWaitForm;
begin
  wt:=TWaitThread.Create(True);
  wt.FormCreated:=False;
  wt.WaitFormWrapper:=Self;
  wt.FreeOnTerminate:=False;

  //Im Execute des Threads wird das Fenster erstellt und angezeigt
  wt.Resume;
  while not (wt.FormCreated) do
    Application.ProcessMessages;
end;

procedure TWaitForm.ThreadCreateWaitForm;
begin
  WartenForm:=TFrmWarten.Create(nil);
  wt.FormCreated:=True;
end;

procedure TWaitForm.DestroyWaitForm;
begin
  FreeAndNil(WartenForm);
  wt.Free;
end;

procedure TWaitForm.AddMessage(Value: String);
begin
  wt.SetMessage(Value);
end;

procedure TWaitForm.ThreadShowWaitForm;
begin
  if not (WartenForm.Visible) then
    WartenForm.Show;
  WartenForm.lblAktion.Caption:=IntToStr(random(999));
  WartenForm.lblAktion.Repaint;
end;

procedure TWaitForm.ThreadAddMessage();
begin
  WartenForm.lblAktion.Caption:=wt.Msg;
end;

end.
Wie gesagt, auch wenn ich mich jetzt blamiere. Vielleicht habe ich einen gewaltigen Denkfehler oder doch nur eine Kleinigkeit vergessen. Aber ich finde den Wald vor lauter Bäumen nicht...

Bitte helft mir und erklärt mir ggf. meinen Gedankenfehler.

Danke und gute Nacht. Ich hau ich jetzt endlich hin
  Mit Zitat antworten Zitat