Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Eine Art Semi-ShowModal? (https://www.delphipraxis.net/186351-eine-art-semi-showmodal.html)

sh17 26. Aug 2015 12:06

Eine Art Semi-ShowModal?
 
Hallo,

wenn man ShowModal mit einem Dialog macht, ist z.B. das Hauptmenü nicht mehr zu bedienen. Klar.

Code:
procedure OnClick;
begin
  MachDies;
  Form.ShowModal;
  MachDas;
end;
Wenn ich das ganze durch Show ersetze, ist klar, rauscht er sofort durch MachDas;

Was wäre die eleganteste Variante, um das ShowModal durch Show zu ersetzen (um im Hintergrund das Hauptmenü nutzen zu können) und doch eine Art ShowModal zu haben, die wartet und dann MachDas; aufruft?

Threads, Monitor,... Muss ja sicher im MainThread laufen

Daniel 26. Aug 2015 12:12

AW: Eine Art Semi-ShowModal?
 
Eine Art OnClose-Handler als anonyme Methode? So ähnlich, wie es auf den mobilen Plattform für die MessageBox umgesetzt wurde. Das Form selbst würde dann mittels .Show() angezeigt und hätte je nach Bedarf sowas wie StayOnTop aktiv.

mm1256 26. Aug 2015 12:35

AW: Eine Art Semi-ShowModal?
 
Und warum nicht einfach "MachDas" in eine separate Unit auslagern, und dann aus der "modalen" Form - die ganz normal mit Show angezeigt wird - aufrufen?

sh17 26. Aug 2015 12:43

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von mm1256 (Beitrag 1313555)
Und warum nicht einfach "MachDas" in eine separate Unit auslagern, und dann aus der "modalen" Form - die ganz normal mit Show angezeigt wird - aufrufen?

Hmm, ja, ne. Ist mir jetzt zu unflexibel. Mal schauen, wenn es gar nicht anders geht.

Mavarik 26. Aug 2015 12:49

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von sh17 (Beitrag 1313559)
Zitat:

Zitat von mm1256 (Beitrag 1313555)
Und warum nicht einfach "MachDas" in eine separate Unit auslagern, und dann aus der "modalen" Form - die ganz normal mit Show angezeigt wird - aufrufen?

Hmm, ja, ne. Ist mir jetzt zu unflexibel. Mal schauen, wenn es gar nicht anders geht.

oder Du nimmst einfach den Vorschlag aus #2

idefix2 27. Aug 2015 10:18

AW: Eine Art Semi-ShowModal?
 
Delphi-Quellcode:
class TSemimodalForm (TForm);
  private
    finished: boolean;  

  public
    procedure WaitForMe;
    procedure show; override;
    procedure close; override;
 
  end;

implementation

procedure TSemimodalForm.WaitForMe;
begin
repeat sleep(200);
       application.processmessages; // bin nicht sicher, ob das hier nötig ist
       until finished;
end;

procedure TSemimodalForm.show;    
begin
inherited;
finished:=false;
end;

procedure TSemimodalForm.close;
inherited;
finished:=true;
end;

Die Forms, bei denen du das brauchst (hier Form2), von TSemiModalForm ableiten
und aufrufen mit
Delphi-Quellcode:
  Form2.Show;
  Form2.WaitForMe;

Mavarik 27. Aug 2015 10:43

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von idefix2 (Beitrag 1313690)
[DELPHI]
Delphi-Quellcode:
  Form2.Show;
  Form2.WaitForMe;

Autsch...

Delphi-Quellcode:
begin
  Form2.MyShowModal(Procedure
                      begin
                        // Mach hier das was Du nach dem Showmodal machen wolltest...  
                      end);
end.

Photoner 27. Aug 2015 10:52

AW: Eine Art Semi-ShowModal?
 
Evtl. Einen Timer verwenden der mit .OnShow gestartet wird?

Delphi-Quellcode:
procedure TFormNichtModal.FormShow(Sender: TObject);
begin
  MyTimer.Interval := 100;
  MyTimer.Enabled := True;
end
Delphi-Quellcode:
procedure TFormNichtModal.MyTimerTimer(Sender: TObject);
begin
 if not FormNichtModal.Showing then begin
   Machdas;
   MyTimer.Enabled := False;
 end;
end;

idefix2 27. Aug 2015 10:55

AW: Eine Art Semi-ShowModal?
 
Es geht sogar noch einfacher, weil die Eigenschaft visible ohnehin durch show und close gesetzt wird.
Also kein Überschreiben von show und close nötig, sondern nur repeat ... until not visible.

Die Form sollte aber nicht mit caFree geschlossen werden, sonst kann es knallen.

idefix2 27. Aug 2015 11:09

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von Mavarik (Beitrag 1313697)
Autsch...

Was soll um Himmels Willen an der Variante, die du vorschlägst, besser sein?
Es gibt schon Situationen, wo anonyme Methode etwas bringen, aber hier? Nur, um endlich einmal eine anonyme Methode verwenden zu können?

Programmcode sollte meines Erachtens vor allem LESBAR sein, man sollte wenn möglich beim ersten Hinsehen erkennen, was passiert. Das ist bei Code, der als anonyme Methode im Parameter eines MyShowModal übergeben wird, sicher nicht im gleichen Ausmaß gegeben, als wenn man mittel Form.WaitForMe explizit auf das Beenden der Form wartet und dann im Code normal weitermacht.

Wenn dich der Aufruf stört, dann könnte man eine Methode ShowSemiModal in Betracht ziehen, die erst zurückkehrt, wenn die Form geschlossen ist, die also den Code von WaitForMe am Ende beinhaltet. Abstrakte Methoden an die Form zu übergeben, trägt hier nichts dazu bei, den Code besser oder lesbarer zu machen. Auch aus der Sicht von OOP ist der Ansatz nicht gut, denn es ist sicher nicht die "Aufgabe" der Form, das zu erledigen, was passieren soll, nachdem die Form geschlossen wurde.

Mavarik 27. Aug 2015 11:10

AW: Eine Art Semi-ShowModal?
 
Warum zum Geier wollt Ihr alle CPU-Zeit verschwenden mit Abfragen, Timer, Sleep und Application.Processmessages?

Einfach eine {Anonyme}Procedure aufrufen wenn das Fenster geschlossen wird/worden ist.

Es gibt doch keinen Grund rumzubasteln.

Der schöne Günther 27. Aug 2015 11:15

AW: Eine Art Semi-ShowModal?
 
Sehe ich genauso. Das Gebastel macht die Sache doch wirklich nicht einfacher oder weniger fehleranfällig. Auf OnClose reagieren ist doch genau was wir wollen.

Ob man da jetzt eine anonyme Methode oder eine normale oder was auch immer nimmt. Das muss doch keinen kümmern.

mkinzler 27. Aug 2015 11:16

AW: Eine Art Semi-ShowModal?
 
Es gab Zeiten, da gab es keine Türklingeln und man hat periodisch nachschauen müssen, ob jemand vor der Tür steht.
Türklingeln sind also überflüssig. :mrgreen:

Mavarik 27. Aug 2015 11:17

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von idefix2 (Beitrag 1313701)
Was soll um Himmels Willen an der Variante, die du vorschlägst, besser sein?
Es gibt schon Situationen, wo anonyme Methode etwas bringen, aber hier? Nur, um endlich einmal eine anonyme Methode verwenden zu können?

Ganz einfach... Weil es so schon in der RTL enthalten ist.


Zitat:

Zitat von RTL_XE8
..
Delphi-Quellcode:
    function ShowModal: TModalResult; overload;
    procedure ShowModal(const ResultProc: TProc<TModalResult>); overload;
..

Delphi-Quellcode:
procedure TCommonCustomForm.ShowModal(const ResultProc: TProc<TModalResult>);
begin
  FResultProc := ResultProc;
  Show;
end;

procedure TCommonCustomForm.SetModalResult(Value: TModalResult);
begin
  FModalResult := Value;
  if Assigned(FResultProc) then
  begin
    FResultProc(FModalResult);
    FResultProc := nil;
    Close;
  end;
end;


Sir Rufo 27. Aug 2015 11:27

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von idefix2 (Beitrag 1313701)
Auch aus der Sicht von OOP ist der Ansatz nicht gut, denn es ist sicher nicht die "Aufgabe" der Form, das zu erledigen, was passieren soll, nachdem die Form geschlossen wurde.

Falsch, das nennt man IoC Inversion of Control.

Das etwas passieren soll steht fest, aber was exakt passieren soll wird injiziert.

Und jeder hat IoC schon verwendet, indem er einen Event belegt hat - ist also ein alter Hut.

Mavarik 27. Aug 2015 11:51

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von mkinzler (Beitrag 1313705)
Es gab Zeiten, da gab es keine Türklingeln und man hat periodisch nachschauen müssen, ob jemand vor der Tür steht.
Türklingeln sind also überflüssig. :mrgreen:

Ja das kenne ich auch noch...

Ich habe immer gewartet bis ich das Sync-Loch der Diskette gesehen habe und dann meine Daten geschrieben...

idefix2 27. Aug 2015 12:35

AW: Eine Art Semi-ShowModal?
 
Meines Erachtens ist ein grundlegender Unterschied zwischen der asynchronen Reaktion auf ein beliebiges Ereignis, und dem Warten auf die Beendigung eines Tasks, um dann mit dem normalen Programmablauf fortzufahren.

Und die Übergabe als Prozedur funktioniert überhaupt nicht, oder nur mit gröbsten Verrenkungen bzw. völlig unnötigen Rekursionen, wenn der Aufruf der Form in einer Schleife stattfindet.

Delphi-Quellcode:
repeat
   Berechne;
   Form1.ShowSemiModal;
   berechneweiter;
   until BefriedigendesErgebnis;

Zitat:

Warum zum Geier wollt Ihr alle CPU-Zeit verschwenden mit Abfragen, Timer, Sleep und Application.Processmessages?
Erfreulicherweise ist die CPU-Zeit, die dadurch "veschwendet" wird, völlig vernachlässigbar. Bei einer modernen CPU wird da alle 200 Millisekunden nicht einmal eine Mikrosekunde verbraten.

Mavarik 27. Aug 2015 12:45

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von idefix2 (Beitrag 1313714)
Delphi-Quellcode:
repeat
   Berechne;
   Form1.ShowSemiModal;
   Form1.WaitForMe;
   berechneweiter;
until BefriedigendesErgebnis;

Ja, so programmiere ich auch... Ne Stopp das war noch zu DOS Zeiten...

Seit Borland Pascal für Windows programmiert man aber Event-Orientiert...

Sir Rufo 27. Aug 2015 12:50

AW: Eine Art Semi-ShowModal?
 
In so einem Fall sollte man sich auch von einer Schleife verabschieden und das mit etwas ersetzen, was zu dem Use-Case besser passt: Eine State-Machine.

Damit bekommt man dann auch die kompliziertesten Verschachtelungen sauber in den Griff.

http://melander.dk/delphi/statemachine/
https://github.com/malcolmgroves/TStateMachine (nicht so tolle Portierung von https://github.com/nblumhardt/stateless (C#))

baumina 27. Aug 2015 12:51

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von idefix2 (Beitrag 1313714)
Meines Erachtens ist ein grundlegender Unterschied zwischen der asynchronen Reaktion auf ein beliebiges Ereignis, und dem Warten auf die Beendigung eines Tasks, um dann mit dem normalen Programmablauf fortzufahren.

Und die Übergabe als Prozedur funktioniert überhaupt nicht, oder nur mit gröbsten Verrenkungen bzw. völlig unnötigen Rekursionen, wenn der Aufruf der Form in einer Schleife stattfindet.

Delphi-Quellcode:
repeat
   Berechne;
   Form1.ShowSemiModal;
   berechneweiter;
   until BefriedigendesErgebnis;

Du solltest vielleicht nochmal nachlesen was der TE eigentlich wollte. Er möchte in dem Formular, das dieses SemiModalDingens aufrufen wird, z.B. das Menü weiterhin bedienbar haben und eben nicht in einer Schleife landen, in der der Benutzer nur durch Schließen des SemiFensters irgendwie fortfahren kann.

sh17 27. Aug 2015 13:03

AW: Eine Art Semi-ShowModal?
 
Code:
function ShowModal: TModalResult; overload;
    procedure ShowModal(const ResultProc: TProc<TModalResult>); overload;
ich glaub ich versuch mich mal mit dieser Variante, ich geb dem Formular eine Funktion mit,
die nach dem Schließen aufgerufen werden muss. Da muss ich zwar etwas umbauen, sieht aber
besser aus.

Code:
Show;
WaitForMe;
wäre auch eine Option, nur gefallen mir die ganzen Konstrukte, wie gewartet wird, nicht.

Danke erst mal für die Diskussion

idefix2 27. Aug 2015 13:17

AW: Eine Art Semi-ShowModal?
 
@Baumina
Inwieweit widerspricht das eine dem anderen? Das Programm braucht zum Abarbeiten eines Tasks noch Eingaben von mir (und das vielleicht in einer Schleife - ich sage nicht, dass der TE das braucht, aber es geht um den allgemeinen Fall), und kann mit DIESEM Task ohne die Eingaben nicht weitermachen.

Mir erscheint es extrem unschön, alles, was nach dieser einen Abfrage passiert, in eine anonyme Routine zu pressen, die man der Abfrage übergibt.

Oder nimm einen anderes Beispiel:

Delphi-Quellcode:
berechne1;
if JetztBrauchIchNochDaten then
   Form1.ShowSemiModal;
berechne2;
if JetztBraucheIchNochAndereDaten then
   Form2.ShowSemiModal;
berechne3;
Das wird mit so einer anonymen Methode ein völlig undurchsichtiger Wust.
Und es kann schon vorkommen, dass der User zum Beantworten der Frage, die ihm das Programm stellt, gerne in irgend einem anderen Menüpunkt des Programms etwas nachschauen würde - geht bei modalen Fenstern nicht.

@Sir Rufo
Danke für den Link, das ist eine sehr interessante Komponente, die ich bei einigen Gelegenheiten schon gerne gehabt hätte.
In der aktuellen Fragestellung geht es aber nicht um komplizierte Verschachtelungen, sondern um eine ganz einfache Schleife. Kompliziert wird es nur, wenn man sich darauf versteift, unter keinen Umständen mehr so programmieren zu dürfen, "wie zu DOS Zeiten".

Sir Rufo 27. Aug 2015 13:32

AW: Eine Art Semi-ShowModal?
 
Es geht hier eher um "nicht mehr wollen", denn das führte immer zu Spaghetti-Code.

Hier mal eine schnelle Implementierung, die alle anderen Show-Methoden (
Delphi-Quellcode:
Show
,
Delphi-Quellcode:
ShowModal
) nicht aushebelt.
Delphi-Quellcode:
unit Unit1;

interface

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

type
  TForm1 = class( TForm )
  private
    FApplicationEvents: TApplicationEvents;
    FSavedFormStyle  : TFormStyle;
    FIsSemiModal     : Boolean;
    FResultProc      : TProc<TModalResult>;
    procedure ApplicationEventsIdle( Sender: TObject; var Done: Boolean );
  protected
    procedure DoClose( var Action: TCloseAction ); override;
  public
    procedure ShowSemiModal( const ResultProc: TProc<TModalResult> );
    procedure AfterConstruction; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Vcl.Consts;

{ TForm1 }

procedure TForm1.AfterConstruction;
begin
  inherited;
  FApplicationEvents       := TApplicationEvents.Create( Self );
  FApplicationEvents.OnIdle := ApplicationEventsIdle;
end;

procedure TForm1.ApplicationEventsIdle( Sender: TObject; var Done: Boolean );
begin
  if FIsSemiModal and ( ModalResult <> mrNone )
  then
    begin
      Exclude( FFormState, fsModal );
      FormStyle := FSavedFormStyle;
      Close;
    end;
end;

procedure TForm1.DoClose( var Action: TCloseAction );
begin
  inherited;
  if FIsSemiModal
  then
    begin
      FIsSemiModal := False;
      Hide( );
      FResultProc( ModalResult );
    end;
end;

procedure TForm1.ShowSemiModal( const ResultProc: TProc<TModalResult> );
begin
  if not Enabled or Visible or ( fsModal in FormState )
  then
    raise EInvalidOperation.Create( SCannotShowModal );

  if not Assigned( ResultProc )
  then
    raise EArgumentNilException.Create( 'ResultProc' );

  FResultProc    := ResultProc;
  FSavedFormStyle := FormStyle;
  FormStyle      := fsStayOnTop;
  FIsSemiModal   := True;
  Include( FFormState, fsModal );
  Show( );
end;

end.

Mavarik 27. Aug 2015 13:41

AW: Eine Art Semi-ShowModal?
 
Obwohl

Zitat:

Zitat von sh17 (Beitrag 1313725)
Danke erst mal für die Diskussion

eigentlich den Thread schon beendet hat.

Da Du ja keine Ruhe gibst... 8-)

Zitat:

Zitat von idefix2 (Beitrag 1313728)
Das wird mit so einer anonymen Methode ein völlig undurchsichtiger Wust.
Und es kann schon vorkommen, dass der User zum Beantworten der Frage, die ihm das Programm stellt, gerne in irgend einem anderen Menüpunkt des Programms etwas nachschauen würde - geht bei modalen Fenstern nicht.

Na eben... Daher ist Modal sowieso schlecht...

Allgemein: anonymen Methode bringen den Code lesbar da hin wo er gebraucht wird... Daher auch viel übersichtlicher als immer hin und her zu springen wo die eigentliche Procedure ist. Aber darum geht es nicht...

Bleiben wir mal bei Deinem Beispiel...

Zitat:

Zitat von idefix2 (Beitrag 1313714)
Delphi-Quellcode:
repeat
   Berechne;
   Form1.ShowSemiModal;
   berechneweiter;
until BefriedigendesErgebnis;


Ich fange den User doch nicht in so einer Schleife... Abgesehen von der UI-Task.

Daher die 5 einfachen Regeln für ein reaktives Userinterface:

Rule #1 / Führe nie Code in einem ONXYEvent aus der länger als wenige Millisekunden dauert. (Außer #2)
Rule #2 / Ich will das Userinterface Updaten? Mach es im OnIdle
Rule #3 / Du brauchst Application.Processmessages - Überprüfe Dein Design
Rule #4 / Eine Aktion dauert 2xlänger als die Zeit einen Thread zu erzeugen? Erzeuge einen Thread!
Rule #5 / Die Zeit eine Thread zu erzeugen ist zu lang. - Erzeuge den Thread vorher und starte ihn nur noch in wenigen (ns)

{Auszug aus meinem Vortrag - Firemonkey im realen Crossplattform-Einsatz}

Designe den Programm-Ablauf so wie der User es auch bedienen würde.

Mavarik

PS.: Rule #6 - Schau wie es der SIR macht...

Sir Rufo 27. Aug 2015 13:43

AW: Eine Art Semi-ShowModal?
 
Zitat:

Zitat von Mavarik (Beitrag 1313733)
PS.: Rule #6 - Schau wie es der SIR macht...

Das impliziert aber noch die Rule #7

idefix2 27. Aug 2015 13:58

AW: Eine Art Semi-ShowModal?
 
Zitat:

Ich fange den User doch nicht in so einer Schleife... Abgesehen von der UI-Task.
Das ganze soll als einfaches Beispiel für den prinzipiellen Ablauf dienen. Dass der User so einen Ablauf immer abbrechen können muss, ist ohnehin klar, aber darum geht es hier doch nicht.

Zitat:

Na eben... Daher ist Modal sowieso schlecht...
Und genau darum geht es doch in diesem Thread. Wie setze ich ohne showmodal um, dass mein Programm den Ablauf erst fortsetzt, wenn es die Daten bekommen hat, die es zur Fortsetzung braucht.

Zitat:

Rule #1 / Führe nie Code in einem ONXYEvent aus der länger als wenige Millisekunden dauert. (Außer #2)
Und wer sagt dir, dass der ganze Programmablauf, der nach dem Close der Form ansteht - das können komplizierte Berechnungen sein, die sich die Daten erst irgendwo zusammensammeln müssen - nicht länger dauert? Ein Grund mehr, das nicht über die anonyme Methode ins OnClose der Form zu verlagern.

Zitat:

Bleiben wir mal bei Deinem Beispiel...
Sehr gut. Bleib also bei dem Beispiel und zeig, wie du das konkret umsetzt (den Code, um den Ablauf gegebenfalls abzubrechen, kannst du dir sparen, der kompliziert das ganze hier nur unnötig).

mkinzler 27. Aug 2015 14:02

AW: Eine Art Semi-ShowModal?
 
Ich glaube ich mach hier dicht, den das Problem ist gelöst und die unterschiedlichen Ansichten werden auch durch weitere Diskzussion nicht abgebaut werden können.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:08 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz