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/)
-   -   Delphi Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto (https://www.delphipraxis.net/214856-verwendung-von-ttitlebarpanel-mit-modalpopupmode-%3D-pmauto.html)

VCLVirtuoso 20. Mär 2024 13:26

Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto
 
Liste der Anhänge anzeigen (Anzahl: 1)
Bei der Verwendung von TTitleBarPanel in Forms, die mit ShowModal aufgerufen werden, ist mir ein merkwürdiges Verhalten aufgefallen. Wenn Application.ModalPopupMode := pmAuto gesetzt ist (was ja bei neueren Delphi-Anwendungen standardmäßig der Fall ist), wird die Custom Titlebar beim zweiten Aufruf des Fensters nicht über die normale Titlebar gezeichnet, sondern darunter, als wäre sie nicht richtig zugewiesen (siehe Anhang). Beim ersten Aufruf des Fensters sieht alles korrekt aus.

Reproduzieren lässt sich der Fehler mit zwei Forms und einer Titlebar in Form2. Die Titlebar wird über
Delphi-Quellcode:
 CustomTitlebar.Control := TitleBarPanel1
und
Delphi-Quellcode:
CustomTitlebar.Enabled := True
in Form2 zugewiesen. Öffnet man über den Button in Form1 nun Form2, schließt Form2 wieder und öffnet es dann nochmal, tritt der Fehler wie auf dem Bild auf.

Wenn unter TForm2.FormActivate die API-Methode SetWindowPos mit dem Parameter SWP_FRAMECHANGED aufgerufen wird, tritt das Problem nicht mehr auf. In komplexeren Fenstern führt das aber zu einem unschönen Flackern. Gibt es einen eleganteren Weg, den Fehler zu umgehen oder handelt es sich dabei um einen Bug in Delphi? pmAuto führt generell an verschiedenen Stellen zu Problemen (siehe https://stackoverflow.com/questions/...bug-workaround), was auch so in der Dokumentation steht, wird aber in unserer Anwendung benötigt, damit TCommonDialoge, sowie die dynamische Vorschau in der Windows-Taskleiste korrekt funktionieren.
Delphi-Quellcode:
program Project1;

uses
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.ModalPopupMode := pmAuto;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.
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, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.ShowModal;
end

end.
Delphi-Quellcode:
unit Unit2;

interface

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

type
  TForm2 = class(TForm)
    TitleBarPanel1: TTitleBarPanel;
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  protected
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses
Unit1;

procedure TForm2.FormActivate(Sender: TObject);
begin
  SetWindowPos(Handle, 0, Left, Top, Width, Height, SWP_FRAMECHANGED); // Diese Zeile behebt das Problem
end;

end.

peterbelow 21. Mär 2024 12:06

AW: Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto
 
Zitat:

Zitat von VCLVirtuoso (Beitrag 1534887)
Bei der Verwendung von TTitleBarPanel in Forms, die mit ShowModal aufgerufen werden, ist mir ein merkwürdiges Verhalten aufgefallen. Wenn Application.ModalPopupMode := pmAuto gesetzt ist (was ja bei neueren Delphi-Anwendungen standardmäßig der Fall ist), wird die Custom Titlebar beim zweiten Aufruf des Fensters nicht über die normale Titlebar gezeichnet, sondern darunter, als wäre sie nicht richtig zugewiesen (siehe Anhang). Beim ersten Aufruf des Fensters sieht alles korrekt aus.

Reproduzieren lässt sich der Fehler mit zwei Forms und einer Titlebar in Form2. Die Titlebar wird über
Delphi-Quellcode:
 CustomTitlebar.Control := TitleBarPanel1
und
Delphi-Quellcode:
CustomTitlebar.Enabled := True
in Form2 zugewiesen. Öffnet man über den Button in Form1 nun Form2, schließt Form2 wieder und öffnet es dann nochmal, tritt der Fehler wie auf dem Bild auf.

Wenn unter TForm2.FormActivate die API-Methode SetWindowPos mit dem Parameter SWP_FRAMECHANGED aufgerufen wird, tritt das Problem nicht mehr auf. In komplexeren Fenstern führt das aber zu einem unschönen Flackern. Gibt es einen eleganteren Weg, den Fehler zu umgehen oder handelt es sich dabei um einen Bug in Delphi? pmAuto führt generell an verschiedenen Stellen zu Problemen (siehe https://stackoverflow.com/questions/...bug-workaround), was auch so in der Dokumentation steht, wird aber in unserer Anwendung benötigt, damit TCommonDialoge, sowie die dynamische Vorschau in der Windows-Taskleiste korrekt funktionieren.

Es ist wohl ein Bug aber der beste workaround wäre meiner Meinung nach, modal genutzte Forms nicht in die autocreate-Liste aufzunehmen sondern nach Bedarf zu erzeugen und danach auch sofort wieder zu zerstören. Man braucht dann auch keine Form-Variable, die läßt sich sozusagen "internalisieren" wenn man der Formklasse eine public class function nach dem folgenden Muster spendiert:

Delphi-Quellcode:
class function TFormXYZ.Execute(aFormdata: TFormXYZData): boolean;
var
  LInstance: TFormXYZ;
begin
  LInstance := TFormXYZ.Create(nil);
  try
    LInstance.Initialize(aFormData);
    Result := LInstance.ShowModal := mrOK;
    if Result then
      LInstance.GetUserInput(aFormData);
  finally
    LInstance.Free;
  end;
end;
TFormXYZData ist dabei ein Objekt das die Daten enthält, die das Form anzeigen bzw. entgegennehmen soll. Man kann auch einen record verwenden, dann muß der Parameter für Execute aber ein Var-Parameter sein.

Auf diese Weise kommt man auch nicht in Versuchung, eine Form als Datenspeicher zu mißbrauchen. :wink:

VCLVirtuoso 22. Mär 2024 09:10

AW: Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto
 
Zitat:

Zitat von peterbelow (Beitrag 1534903)
Es ist wohl ein Bug aber der beste workaround wäre meiner Meinung nach, modal genutzte Forms nicht in die autocreate-Liste aufzunehmen sondern nach Bedarf zu erzeugen und danach auch sofort wieder zu zerstören. Man braucht dann auch keine Form-Variable, die läßt sich sozusagen "internalisieren" wenn man der Formklasse eine public class function nach dem folgenden Muster spendiert

Auf diese Weise kommt man auch nicht in Versuchung, eine Form als Datenspeicher zu mißbrauchen. :wink:

Das wäre wahrscheinlich der richtige Weg und würde auch viele andere Probleme ersparen. Unsere Legacy-Software macht allerdings genau den Fehler an so vielen Stellen, dass sich der Code leider nicht ohne Weiteres refaktorieren lässt.

Mit
Delphi-Quellcode:
SetWindowPos
im
Delphi-Quellcode:
TForm2.FormShow
funktioniert es schon besser, weil das Fenster ja noch nicht gezeigt wird. Das ist aber natürlich immer noch ein Hack, den ich eigentlich gerne anders lösen würde. Lässt sich das pmAuto vielleicht umgehen? Ich habe die Dokumentation allerdings schon so verstanden, dass das die "empfohlene" Variante ist. Aber wenn es tatsächlich ein Bug ist, muss es wahrscheinlich erstmal bei dem SetWindowPos-Workaround bleiben.

peterbelow 22. Mär 2024 11:20

AW: Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto
 
Zitat:

Zitat von VCLVirtuoso (Beitrag 1534925)
Zitat:

Zitat von peterbelow (Beitrag 1534903)
Es ist wohl ein Bug aber der beste workaround wäre meiner Meinung nach, modal genutzte Forms nicht in die autocreate-Liste aufzunehmen sondern nach Bedarf zu erzeugen und danach auch sofort wieder zu zerstören. Man braucht dann auch keine Form-Variable, die läßt sich sozusagen "internalisieren" wenn man der Formklasse eine public class function nach dem folgenden Muster spendiert

Auf diese Weise kommt man auch nicht in Versuchung, eine Form als Datenspeicher zu mißbrauchen. :wink:

Das wäre wahrscheinlich der richtige Weg und würde auch viele andere Probleme ersparen. Unsere Legacy-Software macht allerdings genau den Fehler an so vielen Stellen, dass sich der Code leider nicht ohne Weiteres refaktorieren lässt.

Mit
Delphi-Quellcode:
SetWindowPos
im
Delphi-Quellcode:
TForm2.FormShow
funktioniert es schon besser, weil das Fenster ja noch nicht gezeigt wird. Das ist aber natürlich immer noch ein Hack, den ich eigentlich gerne anders lösen würde. Lässt sich das pmAuto vielleicht umgehen? Ich habe die Dokumentation allerdings schon so verstanden, dass das die "empfohlene" Variante ist. Aber wenn es tatsächlich ein Bug ist, muss es wahrscheinlich erstmal bei dem SetWindowPos-Workaround bleiben.

Du kannst für jedes Form individuell den PopupMode ändern oder z. B. explizit den PopupParent auf das aktive Form setzen, bevor Du ShowModal aufrufst, aber selbst wenn das funktioniert erfordert es natürlich auch eine entsprechende Änderung an vielen Stellen der existierenden Anwendung.

Ansonsten bleibt nur, die verantwortliche Stelle im VCL-Kode zu suchen und zu fixen. Tritt das Problem auch in Delphi 11.x auf? Falls nicht würde ich die Änderungen am VCL-Kode für "moderneres" Erscheinungsbild von MDI-Anwendungen im Verdacht haben; da wurde einiges verschlimmbessert, was auch nicht-MDI Anwendungen torpedieren kann.

VCLVirtuoso 5. Apr 2024 08:48

AW: Verwendung von TTitleBarPanel mit ModalPopupMode := pmAuto
 
Zitat:

Zitat von peterbelow (Beitrag 1534929)
Du kannst für jedes Form individuell den PopupMode ändern oder z. B. explizit den PopupParent auf das aktive Form setzen, bevor Du ShowModal aufrufst, aber selbst wenn das funktioniert erfordert es natürlich auch eine entsprechende Änderung an vielen Stellen der existierenden Anwendung.

Ansonsten bleibt nur, die verantwortliche Stelle im VCL-Kode zu suchen und zu fixen. Tritt das Problem auch in Delphi 11.x auf? Falls nicht würde ich die Änderungen am VCL-Kode für "moderneres" Erscheinungsbild von MDI-Anwendungen im Verdacht haben; da wurde einiges verschlimmbessert, was auch nicht-MDI Anwendungen torpedieren kann.

Tatsächlich funktioniert es, sobald man
Code:
pmAuto
nicht nur global setzt sondern nochmal für jedes Fenster einzeln. Das ist etwas Aufwand, aber deutlich besser als der vorherige Hack. In der Zwischenzeit sind durch das globale
Code:
pmAuto
noch weitere Fehler aufgetreten, weil die Window Handles jedes Mal neu erzeugt werden (zB leere ComboBoxes, wenn die nicht bei jedem Form-Aufruf neu beschrieben werden).
In der Dokumentation zu PopupMode steht auch etwas dazu, das ich überlesen habe:
Zitat:

Die Eigenschaft PopupMode wird beim Aufruf der Methode ShowModal automatisch auf pmAuto gesetzt. Dies verursacht aber eine Neuerstellung des Fenster-Handle, was in der Regel nicht wünschenswert ist. Um die Neuerstellung von Fenster-Handles zu vermeiden, muss die Eigenschaft PopupMode vor dem Aufruf der Methode ShowModal explizit auf pmAuto gesetzt werden (z.B. zur Entwurfszeit).
Damit ist das Thema nun gelöst. Danke für die Hilfe!


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