Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Modaler Doppelklick gibt Event an Ursprungsfenster weiter (https://www.delphipraxis.net/197852-modaler-doppelklick-gibt-event-ursprungsfenster-weiter.html)

michaelg 11. Sep 2018 15:46

Delphi-Version: 10.2 Tokyo

Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Hallo alle,

mir ist da was ganz komisches aufgefallen.

Ich habe ein Fenster, in dem ein DBGrid ist. Dieses Grid hat ein OnTitleClick-Event, welches eine Umsortierung ausführt und sich merkt, welche Spalte gerade sortiert ist (dazu später).

Nun öffne ich einen modalen Dialog, welcher ganz normal mit Showmodal aufgerufen wird. In diesem modalen Fenster ist ein cxGrid mit einer Liste von Datensätzen
(eine Art Suchfenster). Das Doppelclick-Event auf einer Zeile schliesst den Dialog mit modalresult:=mrok;

Wenn ich nun auf einem bestimmten Datensatz doppelklicke, der genau auf Höhe der Spaltenüberschrift des anderen im Hintergrund liegenden Fensters liegt, wird zusätzlich das
OnTitleclick des anderen Fensters aufgerufen, nachdem der modale Dialog geschlossen wurde.

Ich habe in den Fenstern keinerlei Windows-Events überschrieben oder ähnliches. Der modale Dialog wird auch für die Application erzeugt, nicht für das Form, von dem
es aus aufgerufen wurde, dass heisst eigentlich haben die beiden Fenster gar nichts miteinanders zu tun.

Es ist sehr merkwürdig und sehe eine elementare Problematik. Ich hab schon gelesen, dass ein Doppelklick eigentlich aus vier Mouseevents besteht. Aber die müssten
dann doch abgearbeitet sein, wenn ich wieder in das Ursprungsfenster zurückkomme.

Hab noch kein kleines Beispielprogramm geschrieben, was ich hier posten könnte. Bevor ich mir die Mühe mache, wollte ich fragen, ob es eine Möglichkeit gibt, dem modalen
Dialog oder der Applikation oder Windows zu sagen, dass genau dieses Event abgearbeitet ist, so dass im Ursprungsfenster kein OnTitleclick mehr ankommt. Oder eben irgendwie zu verhindern, dass das Event im Ursprungsfenster auch noch aufgerufen wird.

Weiß jemand was dazu?

API 11. Sep 2018 16:25

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Hallo

Ich verlinke mal einen anderen Beitrag auf stackoverflow.
Dort handelt es sich um eine gleiche/ähnliche Problemstelling.
https://stackoverflow.com/questions/...-oncolumnmoved

Schokohase 11. Sep 2018 16:46

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Der Grund liegt daran, dass ein DoubleClick beim MouseDown erfolgt. Der TitleClick aber beim MouseUp.

Und da erst MouseDown und dann MouseUp, erfolgt erst der Aufruf von DoubleClick, dann das Schließen des Fensters, und dann der MouseUp der dann von der Titel-Zeile behandelt wird.

Lösen kann man das durch einen Timer, der das Setzen von ModalResult entsprechend verzögert (100ms sind mehr als ausreichend und nicht spürbar).

Hier mal ein Ablauf der relevanten Events die bei einem DblClick passieren
  1. MouseDown
  2. OnIdle
  3. Click
  4. MouseUp
  5. OnIdle
  6. DblClick <= Schließen der Form
  7. MouseDown
  8. OnIdle
  9. MouseUp
  10. OnIdle

Hobbycoder 11. Sep 2018 17:22

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Wäre das dann nicht schlauer man hätte den DblClick über MouseUp gesteuert?

Hat das einen tieferen Sinn, dass das so festgelegt ist?

Schokohase 11. Sep 2018 17:49

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Ja, es gibt einen Grund.

Du kannst mit einem Doppel-Klick in einem Text ein ganzes Wort markieren. Klickst du jetzt aber nur MouseDown/MouseUp/MouseDown dann ist das Wort markiert und du kannst noch weitere Wörter durch Bewegen der Maus selektieren bis zum MouseUp.

Nennt sich auch Double-Click-And-Drag

Hobbycoder 11. Sep 2018 17:52

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Ah, ok. An sowas hab ich gar nicht gedacht.

hoika 11. Sep 2018 18:54

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Hallo,
das hatte ich beim TAdvStringGrid (TMS) auch.

Lösung:
Vor dem Erzeugen des modalen Fensters das eigene Fensters disablen

Self.Enabled:= False;
Dialog->ShowModal
Self.Enabled:= True;

Hatte zumindestens mir geholfen.

EWeiss 12. Sep 2018 04:48

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von hoika (Beitrag 1412976)
Hallo,
das hatte ich beim TAdvStringGrid (TMS) auch.

Lösung:
Vor dem Erzeugen des modalen Fensters das eigene Fensters disablen

Self.Enabled:= False;
Dialog->ShowModal
Self.Enabled:= True;

Hatte zumindestens mir geholfen.

Gute Lösung zumal ein aktiviertes Parent Fenster bei einem Modal angezeigten Fenster quatsch ist.
In dem Fall braucht man kein Modales Fenster wenn das Parent bedient werden kann.
Denn das ist der einzige sinn und zweck eines Modalen Fensters.


PS: [OT]
Nebenbei ein Modales Fenster ist unter Delphi ein Nonsens denn es erschließt sich mir nicht welchen sinn es bezwecken soll.
Da liegt wohl ein Design technisches Problem vor.

Ein Modales Fenster unter VB6 erstellt hat folgende Eigenschaften.
ZOrder = -1 (HWND_TOPMOST)
Parent Fenster Disabled

Delphi.
ZOrder = 0 (HWND_TOP)
Parent Fenster Enabled.

Welcher sinn und zweck steckt also dahinter dann kann ich direkt ein normales Fenster erstellen mit anschließenden SetWindowPos um die Zorder festzulegen.
Irgendwie quatsch das Modale Fenster unter Delphi!

Das hier ist schon ein Widerspruch!
Zitat:

ShowModal erlaubt es dem Anwender, nur das neue Fenster zu verwenden und blockiert alle Eingaben in andere Fenster. Erst, wenn dieses Fenster geschlossen wird, werden alle anderen wieder freigegeben. Man kennt das z.B. vom Infofenster der meisten Anwendungen.
Diese Verhalten kann ich unter Delphi nicht bestätigen (Ohne das Parent selbst zu deaktivieren) unter VB6 hingegen schon!

Es ist etwas anderes wenn man sich der Rückgabe Parameter einer Modalen Form bedienen möchte um in der Hauptform auf ein Ereignis zu warten.
bsp.
Delphi-Quellcode:
if Form.ShowModal = mrCancel then

Ansonsten sehe ich da keine Sinnvolle Verwendung für.
[/OT]

War Blödsinn habe mich über ein altes Problem ausgelassen was scheinbar gefixt wurde. :duck:

Dein Problem ist also das der Wert der Eigenschaft ModalResult zurückgegeben wird.. weil dein Parent Fenster aktiv ist.
Und zwar an die Funktion aus der du das Modale Fenster erstellst.
Erstelle ein normales Fenster wenn du ModalResult nicht auswerten willst und gut ist.
Ein .Show mit angehängter SetWindowPos wäre dann die bessere alternative.

gruss

Schokohase 12. Sep 2018 08:20

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von EWeiss (Beitrag 1412983)
Dein Problem ist also das der Wert der Eigenschaft ModalResult zurückgegeben wird.. weil dein Parent Fenster aktiv ist.

Das stimmt so leider nicht.

Unter Delphi 10.2 Tokyo wird beim ShowModal für jedes Fenster der Anwendung MSDN-Library durchsuchenEnableWindow aufgerufen mit
Delphi-Quellcode:
bEnable = false
.
Delphi-Quellcode:
// Vcl.Forms.pas 2215
function DoDisableWindow(Window: HWnd; Data: LPARAM): Bool; {$IFNDEF CLR}stdcall;{$ENDIF}
var
  P: TTaskWindowType;
begin
  if (Window <> TaskActiveWindow) and IsWindowVisible(Window) and
    IsWindowEnabled(Window) then
  begin
{$IF DEFINED(CLR)}
    P := TTaskWindow.Create;
{$ELSE}
    New(P);
{$ENDIF}
    P.Next := TaskWindowList;
    P.Window := Window;
    TaskWindowList := P;
    EnableWindow(Window, False);
  end;
  Result := True;
end;
Diesen Wert kann man aber nicht über Delphi-Referenz durchsuchenTForm.Enabled auslesen. Trotz dass
Delphi-Referenz durchsuchenTForm.Enabled auf
Delphi-Quellcode:
true
steht, ist es für das Betriebssystem aber disabled.

Das Problem ist, dass das modale Fenster versteckt wird und das vorherige Fenster aktiviert wird, obwohl in der Message-Queue noch Nachrichten enthalten sind, die vom modalen Fenster verarbeitet werden müssten (MouseUp) und jetzt aber vom falschen Fenster verarbeitet werden.

Delphi-Quellcode:
// VCL.Forms.pas 7352
function TCustomForm.ShowModal: Integer;
var
  WindowList: TTaskWindowList;
  LSaveFocusState: TFocusState;
  SaveCursor: TCursor;
  SaveCount: Integer;
  ActiveWindow: HWnd;
begin
  // ... schnipp ...
  Application.ModalStarted;
  try
    { RecreateWnd could change the active window }
    ActiveWindow := GetActiveWindow;
    // ... schnipp ...
    WindowList := DisableTaskWindows(0);
    try
      Show;
      try
        SendMessage(Handle, CM_ACTIVATE, 0, 0);
        ModalResult := 0;
        repeat // Message-Loop zum Verarbeiten der Messages
          Application.HandleMessage;
          if Application.Terminated then ModalResult := mrCancel else
            if ModalResult <> 0 then CloseModal;
        until ModalResult <> 0;
        Result := ModalResult;
        SendMessage(Handle, CM_DEACTIVATE, 0, 0);
        if GetActiveWindow <> Handle then ActiveWindow := 0;
      finally
        Hide;
      end;
    finally
      if Screen.CursorCount = SaveCount then
        Screen.Cursor := SaveCursor
      else Screen.Cursor := crDefault;
      EnableTaskWindows(WindowList);
      if Screen.SaveFocusedList.Count > 0 then
      begin
        Screen.FocusedForm := TCustomForm(Screen.SaveFocusedList.First);
        Screen.SaveFocusedList.Remove(Screen.FocusedForm);
      end else Screen.FocusedForm := nil;

      // Hier wird jetzt versucht das alte Fenster wieder zu aktivieren

      { ActiveWindow might have been destroyed and using it as active window will
        force Windows to activate another application }
      if (ActiveWindow <> 0) and not IsWindow(ActiveWindow) then
        ActiveWindow := FindTopMostWindow(0);
      if ActiveWindow <> 0 then
        SetActiveWindow(ActiveWindow);
      RestoreFocusState(LSaveFocusState);
      Exclude(FFormState, fsModal);
    end;
  finally
    Application.ModalFinished;
  end;
end;

KodeZwerg 12. Sep 2018 08:25

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Vielleicht hilft es auch im Event sowas hier einzubauen, damit werden folge-Befehle für Maus-Input gelöscht.
Delphi-Quellcode:
procedure EmptyMouseQueue;
var
  Msg: TMsg;
begin
  while PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST,
    PM_REMOVE or PM_NOYIELD) do;
end;

EWeiss 12. Sep 2018 08:38

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Das stimmt so leider nicht.
Unter Delphi 10.2 Tokyo wird beim ShowModal für jedes Fenster der Anwendung MSDN-Library durchsuchen EnableWindow aufgerufen mit bEnable = false .
Ok Danke!

Aber unter D2010 ist es leider so.. zumindest bei mir.
Ich sollte also nach einem ShowModal das Parent Fenster nicht mehr verschieben dürfen aber das kann ich.
Muss da nochmal recherchieren.

OK! Recherchiert
Habe da wohl quatsch geschrieben.. Es ist wie du sagst.
Kann mich aber erinnern das ich das Problem schon mal hatte das ich die Parent Form trotzdem verschieben konnte.
Ist vielleicht gefixt worden.

gruss

Schokohase 12. Sep 2018 08:47

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Ich vergaß noch zu erwähnen, dass die Lösung von @hoika das Problem nicht beseitigt.

EWeiss 12. Sep 2018 08:53

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von Schokohase (Beitrag 1413001)
Ich vergaß noch zu erwähnen, dass die Lösung von @hoika das Problem nicht beseitigt.

Hmm.. nun wie ich schon sagte wenn er ModalResult NICHT auswertet kann er auch auf ShowModal verzichten ist wohl die beste Lösung.
EnableWindow und SetWindowPos liefern dann das gleiche Ergebnis.

Emuliertes ShowModal.. und fertig.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin

  Form2.Show;
end;

end.
Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation
uses Unit1;

{$R *.dfm}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin

  Form1.Enabled := True;
end;

procedure TForm2.FormShow(Sender: TObject);
begin

  Form1.Enabled := False;
  SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;

end.
Man kann jetzt noch das neue Fenster über das alte zentrieren das habe ich mir aber erspart.

gruss

Schokohase 12. Sep 2018 09:28

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Eine funktionierende Lösung für
Delphi-Quellcode:
ShowModal
sieht so aus:
Delphi-Quellcode:
unit Forms.SomeDialogForm;

interface

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

type
  TSomeDialogForm = class( TForm )
    ApplicationEvents1: TApplicationEvents;
    procedure ApplicationEvents1Idle(
      Sender:  TObject;
      var Done: Boolean );
    procedure FormDblClick( Sender: TObject );
  private
    FModalResult: TModalResult;
    procedure SetModalResult( const Value: TModalResult );
  public
  end;

var
  SomeDialogForm: TSomeDialogForm;

implementation

{$R *.dfm}

procedure TSomeDialogForm.ApplicationEvents1Idle(
  Sender:  TObject;
  var Done: Boolean );
begin
  if ( FModalResult <> 0 ) and ( GetAsyncKeyState( VK_LBUTTON ) = 0 )
  then
    begin
      // Wir setzen den ModalResult erst dann, wenn der linke Maus-Button nicht gedrückt ist
      ModalResult := FModalResult;
    end;
end;

procedure TSomeDialogForm.FormDblClick( Sender: TObject );
begin
  SetModalResult( mrOK );
end;

procedure TSomeDialogForm.SetModalResult( const Value: TModalResult );
begin
  if ( Value <> Self.ModalResult ) or ( Value <> FModalResult )
  then
    begin
      FModalResult := Value;
    end;
end;

end.
Schön ist dabei im Auge des Betrachters, denn nach einem MouseDown/MouseUp/MouseDown wird der DoppelKlick ausgelöst aber die Form noch nicht geschlossen. Erst wenn man MouseUp kommt (die Maus-Taste losgelassen wird) wird ModalResult gesetzt und die Form geschlossen.

EWeiss 12. Sep 2018 09:30

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Jo wenn er ModalResult auswertet ansonsten überflüssig ;)
Trotzdem guter Ansatz. :thumb:

Ok.. Bin raus irgendwie kommt vom TE keine Rückmeldung..

gruss

Schokohase 12. Sep 2018 09:37

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Ich habe gerade mal geschaut wie das Verhalten bei .NET WinForms ist.

Da wird der Doppel-Klick-Event erst ausgelöst, wenn die Maustaste wieder oben ist.

@Hobbycoder

Da war dein Vorschlag ja doch richtig.

michaelg 12. Sep 2018 10:19

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Erstmal vielen Dank an alle, die sich meinen Kopf zerbrochen haben. Ich bin begeistert über so viel Mithilfe.

Die Lösung von Schokohase funktioniert super.

Delphi-Quellcode:
unit Forms.SomeDialogForm;

interface

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

type
  TSomeDialogForm = class( TForm )
    ApplicationEvents1: TApplicationEvents;
    procedure ApplicationEvents1Idle(
      Sender: TObject;
      var Done: Boolean );
    procedure FormDblClick( Sender: TObject );
  private
    FModalResult: TModalResult;
    procedure SetModalResult( const Value: TModalResult );
  public
  end;

var
  SomeDialogForm: TSomeDialogForm;

implementation

{$R *.dfm}

procedure TSomeDialogForm.ApplicationEvents1Idle(
  Sender: TObject;
  var Done: Boolean );
begin
  if ( FModalResult <> 0 ) and ( GetAsyncKeyState( VK_LBUTTON ) = 0 )
  then
    begin
      // Wir setzen den ModalResult erst dann, wenn der linke Maus-Button nicht gedrückt ist
      ModalResult := FModalResult;
    end;
end;

procedure TSomeDialogForm.FormDblClick( Sender: TObject );
begin
  SetModalResult( mrOK );
end;

procedure TSomeDialogForm.SetModalResult( const Value: TModalResult );
begin
  if ( Value <> Self.ModalResult ) or ( Value <> FModalResult )
  then
    begin
      FModalResult := Value;
    end;
end;

end.
Ich weiss nur nicht, ob es Auswirkungen hat, dass ich im Mainform des Programms bereits ein tApplicationEvents habe. Arbeitet das dann auch noch, wenn mein modaler Dialog gerade angezeit wird?

KodeZwerg 12. Sep 2018 10:27

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Nur mal nachgehakt, warum nicht einfach vor dem Close von Fenster X einfach Mauspuffer löschen, man ist doch eh im Event drinnen was getriggert werden sollte?

Schokohase 12. Sep 2018 17:44

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von KodeZwerg (Beitrag 1413009)
Nur mal nachgehakt, warum nicht einfach vor dem Close von Fenster X einfach Mauspuffer löschen, man ist doch eh im Event drinnen was getriggert werden sollte?

Probier es doch einfach mal aus ...

KodeZwerg 12. Sep 2018 20:37

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von Schokohase (Beitrag 1413055)
Zitat:

Zitat von KodeZwerg (Beitrag 1413009)
Nur mal nachgehakt, warum nicht einfach vor dem Close von Fenster X einfach Mauspuffer löschen, man ist doch eh im Event drinnen was getriggert werden sollte?

Probier es doch einfach mal aus ...

Ich bekomme Fehler nicht reproduziert, deshalb meine Frage. Problem ist ja bereits gelöst, aber interessiert hätte es mich schon.

michaelg 12. Sep 2018 21:12

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Ich probiere es morgen noch mal aus und werde berichten.

michaelg 13. Sep 2018 07:27

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Die Sache mit dem EmptyMouseQueue hat bei mir nicht funktioniert. Ich hatte es beim Formclose des modalen Dialogs eingebaut.

hoika 13. Sep 2018 07:38

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Hallo,
das löschen aller Events hatte bei mir auch nicht geholfen.
Es sah so aus, als ob Delphi das intern schon "gespeichert" hatte
und nur auf das Ende der Erzeuge_und_Zeige_Form-Methode gewartet hatte.

Schokohase 13. Sep 2018 09:34

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Wenn ihr zum Zeitpunkt X Events löschen könnt, die zum Zeitpunkt X noch gar nicht da sind, dann wärt ihr Zauberer.

Und was noch nicht da ist, braucht auch nicht gespeichert werden, sondern es kommt einfach etwas später und dann ist es da.

Gut, wenn man ganz spitzfindig ist, dann könnte man behaupten, die Events sind in der Zukunft gespeichert.

KodeZwerg 13. Sep 2018 10:29

AW: Modaler Doppelklick gibt Event an Ursprungsfenster weiter
 
Zitat:

Zitat von michaelg (Beitrag 1413078)
Die Sache mit dem EmptyMouseQueue hat bei mir nicht funktioniert. Ich hatte es beim Formclose des modalen Dialogs eingebaut.

Vielen Dank fürs Testen, den Tipp werde ich somit streichen.:thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:16 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