2016-03-17 7 views
5

In einem Delphi (2007) -Programm, das unter Windows 8.1 läuft, möchte ich benachrichtigt werden, wenn der Benutzer auf den Taskleisten-Button klickt, der zu meinem Programm gehört. Also fange ich das WM_SYSCOMMAND ein, das in diesem Fall normalerweise gesendet wird.Warum empfangen modale Delphi-Formulare WM_SYSCOMMAND nicht, wenn der Benutzer auf die Schaltfläche der Taskleiste klickt?

Dies funktioniert gut für das Hauptfenster des Programms.

Wenn ein modales Fenster aktiv ist (mit Form2.ShowModal geöffnet), kann derselbe Code das WM_SYSCOMMAND weder in der Haupt- noch in der modalen Form abfangen. Was ist anders? Und gibt es eine Möglichkeit, dies zu ändern?

Dies ist der Code, den ich zu beiden Formen hinzugefügt haben:

unit unit1; 

interface 

type 
    TForm1 = class(TForm) 
    // [...] 
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND; 
    end; 

// [...] 

implementation 

// [...] 

procedure Tf_dzProgressTest.WMSysCommand(var Msg: TWMSysCommand); 
begin 
    inherited; // place breakpoint here 
end; 

// [...] 

end. 

Ich habe auch versucht Application.OnMessage oder eine TApplicationEvents Komponente und sogar zwingende des Formulars WndProc Methode zu verwenden. Weder konnte WM_SYSCOMMAND abfangen, während ein modales Formular aktiv war.

Antwort

10

Wenn Sie auf die Taskleistenschaltfläche klicken, versucht das System, die Minimierungsaktion für das Fenster auszuführen, das der Taskleistenschaltfläche zugeordnet ist. Normalerweise ist das das Fenster für das Hauptformular. Das ist, wo die WM_SYSCOMMAND stammt.

Wenn nun ein modales Formular angezeigt wird, ist das Hauptformular deaktiviert. Es wurde mit einem Aufruf der Win32 EnableWindow Funktion deaktiviert. Das ist ein wesentlicher Bestandteil der Modalität. Das modale Fenster ist das einzige aktivierte Fenster der obersten Ebene, da Sie nicht mit anderen Fenstern der obersten Ebene interagieren sollen.

Wenn ein Fenster deaktiviert ist, wird auch das Systemmenü deaktiviert. Aus diesem Grund kann das System die Minimierungsaktion nicht ausführen und Sie erhalten keine WM_SYSCOMMAND.

Es gibt nicht viel, was Sie dagegen tun können. Sobald Sie ein modales Formular anzeigen, muss das Hauptfenster deaktiviert werden. Und zu diesem Zeitpunkt wird es nicht WM_SYSCOMMAND erhalten und wird nicht herausfinden, dass der Benutzer auf die Taskleiste geklickt hat.

+0

OK, so dass im Grunde das einzige, was ich tun kann, ist, rufen EnableWindow (Mainwindowhandle, true) und mit den Konsequenzen zu leben, die hat? – dummzeuch

+2

Die Folgen sind etwas schlimm. Ihre modalen Formulare sind nicht modal. Andere Apps verhalten sich genauso. –

0

David erklärte das Problem sehr gut, also werde ich nicht wiederholen, was er gesagt hat.

Was ich Ihnen zu geben, ist eine Arbeit um blockierungs mit code.`

Sie benötigen ein Ereignis zu erklären, die uns sagen, wann das Formular geschlossen hat.

TModalResultEvent = procedure(aSender: TObject; var aModal: TModalResult) of object; 

Dies ermöglicht es uns, auf Nachrichten abzuhören in durch die Anwendung

const 
    WM_SYSCOMMAND1 = WM_USER + 1; 

type 
    TApplicationHelper = class(TWinControl) 
    private 
    FListener: TWinControl; 
    public 
    constructor Create(AOwner: TComponent); override; 
    procedure WMSysCommand1(var Msg: TWMSysCommand); message WM_SYSCOMMAND1; 
    procedure FirstChance(var Msg: TMsg; var Handled: Boolean); virtual; 

    property Listener: TWinControl read FListener write FListener; 
    end; 

constructor TApplicationHelper.Create(AOwner: TComponent); 
begin 
    inherited; 
    Application.OnMessage := FirstChance; 
    if aOwner is TWinControl then 
    FListener := TWinControl(aOwner) 
    else 
    FListener := Self; 
end; 

procedure TApplicationHelper.FirstChance(var Msg: TMsg; 
    var Handled: Boolean); 
begin 
{get in and out...this gets called alot...I would recommend only using 
PostMessage since it is non blocking} 
    if Assigned(FListener) then 
    begin 
    if Msg.Message = WM_SYSCOMMAND then 
    begin 
     PostMessage(FListener.Handle, WM_SYSCOMMAND1, Msg.wParam, Msg.lParam); 
    end; 
    end; 
end; 

procedure TApplicationHelper.WMSysCommand1(var Msg: TWMSysCommand); 
begin 
    ShowMessage('WMSYSCOMMAND1 AppHelper'); 
end; 

end.  

Beispiel dafür, wie die Non Blocking vorbei Form zu nennen.

unit IForms; 

interface 

uses 
    Forms, Controls; 

type 
    TModalResultEvent = procedure(aSender: TObject; var aModal: TModalResult) of object; 

IForm = interface 
    function getEnableForm: boolean; 
    procedure setEnableForm(const Value: boolean); 
    Property EnableForm: boolean read getEnableForm write setEnableForm; 
end; 

implementation 

end. 

TForm1 = class(TForm, IForm) 
    Button1: TButton; 
    Button2: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure CheckBox1Click(Sender: TObject); 
    procedure FormDestroy(Sender: TObject);  
private 
    { Private declarations } 
    FEnable: boolean; 
    FAppHelper: TApplicationHelper;  
    procedure FormModal(aSender: TObject; var aModal: TModalResult); 
    function getEnableForm: boolean; 
    procedure setEnableForm(const Value: boolean); 
//don't need it 
//procedure EnableChildren(aParent: TWinControl; aEnable: boolean); 
    procedure WMSysCommand1(var Msg: TWMSysCommand); message WM_SYSCOMMAND1; 
public 
{ Public declarations } 
    Property EnableForm: boolean read getEnableForm write setEnableForm; 
end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 
uses 
    Unit2, Unit3; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    a_Form: TForm2; 
begin 
//Normal blocking code 
    a_Form := TForm2.Create(nil); 
    try 
    a_Form.ShowModal; 
    finally 
    a_Form.Free; 
    end; 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    a_Form: TForm3; 
begin 
//Non blocking code 
    a_Form := TForm3.Create(nil); 
    a_Form.ShowModal(Self, FormModal); 
end; 
{ 
    mrNone  = 0; 
    mrOk  = idOk; 
    mrCancel = idCancel; 
    mrAbort = idAbort; 
    mrRetry = idRetry; 
    mrIgnore = idIgnore; 
    mrYes  = idYes; 
    mrNo  = idNo; 
    mrAll  = mrNo + 1; 
    mrNoToAll = mrAll + 1; 
    mrYesToAll = mrNoToAll + 1; 

} 
procedure TForm1.FormModal(aSender: TObject; var aModal: TModalResult); 
var 
    a_Message: string; 
begin 
    if aSender is TForm then 
    a_Message := 'Form: ' + TForm(aSender).Name; 

    Case aModal of 
    mrNone: a_Message := a_Message + ' None'; 
    mrOk: a_Message := a_Message + ' Ok'; 
    mrCancel: a_Message := a_Message + ' Cancel'; 
    mrAbort: a_Message := a_Message + ' Abort'; 
    mrRetry: a_Message := a_Message + ' Retry'; 
    mrYes: a_Message := a_Message + ' Yes'; 
    mrNo: a_Message := a_Message + ' No'; 
    mrAll: a_Message := a_Message + ' All'; 
    mrNoToAll: a_Message := a_Message + ' No To All'; 
    mrYesToAll: a_Message := a_Message + ' Yes To All'; 
    else 
    a_Message := a_Message + ' Unknown'; 
    end; 
    ShowMessage(a_Message); 
end; 
{ 
procedure TForm1.EnableChildren(aParent: TWinControl; aEnable: boolean); 
var 
    a_Index: integer; 
begin 
    for a_Index := 0 to aParent.ControlCount - 1 do 
    begin 
    if aParent.Controls[a_Index] is TWinControl then 
     EnableChildren(TWinControl(aParent.Controls[a_Index]), aEnable); 
    aParent.Controls[a_Index].Enabled := aEnable; 
    end; 
end;} 

function TForm1.GetEnableForm: boolean; 
begin 
    //Result := FEnable; 
    Result := Enabled; 
end; 

procedure TForm1.SetEnableForm(const Value: boolean); 
begin 
    //FEnable := Value; 
    Enabled := Value; 
    //EnableChildren(Self, FEnable); 
end. 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    FAppHelper:= TApplicationHelper.Create(Self); 
    FAppHelper.Parent := Self; 
end; 

procedure TForm1.CheckBox1Click(Sender: TObject); 
begin 
    if CheckBox1.Checked then 
    FAppHelper.Listener := Self 
    else 
    FAppHelper.Listener := FAppHelper; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    FAppHelper.Free; 
end; 

procedure TForm1.WMSysCommand1(var Msg: TWMSysCommand); 
begin 
    ShowMessage('WMSYSCOMMAND1 Form1'); 
end; 
{ 
object Form1: TForm1 
    Left = 84 
    Top = 126 
    Width = 514 
    Height = 259 
    Caption = 'Form1' 
    Color = clBtnFace 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Name = 'MS Sans Serif' 
    Font.Style = [] 
    OldCreateOrder = False 
    OnCreate = FormCreate 
    OnDestroy = FormDestroy 
    PixelsPerInch = 96 
    TextHeight = 13 
    object Button1: TButton 
    Left = 56 
    Top = 56 
    Width = 75 
    Height = 25 
    Caption = 'Button1' 
    TabOrder = 0 
    OnClick = Button1Click 
    end 
    object Button2: TButton 
    Left = 256 
    Top = 56 
    Width = 75 
    Height = 25 
    Caption = 'Button2' 
    TabOrder = 1 
    OnClick = Button2Click 
    end 
    object CheckBox1: TCheckBox 
    Left = 256 
    Top = 112 
    Width = 97 
    Height = 17 
    Caption = 'Send to Form' 
    Checked = True 
    State = cbChecked 
    TabOrder = 2 
    OnClick = CheckBox1Click 
    end 
end 
}  

Dies ist das Non Blocking Formular

unit Unit3; 

interface 

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

type 
    TForm3 = class(TForm) 
    Button1: TButton; 
    Button2: TButton; 
    Button3: TButton; 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    FForm: IForm; 
    FModalResultEvent: TModalResultEvent; 
    protected 
    procedure DoClose; virtual; 
    public 
    { Public declarations } 
    procedure ShowModal(aForm: IForm; aModalResultEvent: TModalResultEvent) overload; 
    end; 

var 
    Form3: TForm3; 

implementation 

{$R *.dfm} 

{ 
    object Button1: TButton 
    Left = 32 
    Top = 128 
    Width = 73 
    Height = 25 
    Caption = 'Yes' 
    ModalResult = 6 
    TabOrder = 0 
    OnClick = Button1Click 
    end 
    object Button2: TButton 
    Left = 128 
    Top = 128 
    Width = 57 
    Height = 25 
    Caption = 'No' 
    ModalResult = 7 
    TabOrder = 1 
    OnClick = Button1Click 
    end 
    object Button3: TButton 
    Left = 216 
    Top = 128 
    Width = 57 
    Height = 25 
    Caption = 'Cancel' 
    ModalResult = 2 
    TabOrder = 2 
    OnClick = Button1Click 
    end 
} 

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
    try 
    DoClose; 
    finally 
    Action := caFree; 
    end; 
end; 

procedure TForm3.ShowModal(aForm: TForm; aModalResultEvent: TModalResultEvent); 
begin 
    FForm := aForm; 
    FModalResultEvent := aModalResultEvent; 
    if Assigned(FForm) then 
    FForm.EnableForm:= False; 
    Self.Show; 
end; 

procedure TForm3.Button1Click(Sender: TObject); 
begin 
    if Sender is TButton then 
    begin 
    Self.ModalResult := TButton(Sender).ModalResult; 
    Close; 
    end; 
end; 

procedure TForm3.DoClose; 
var 
    a_MR: TModalResult; 
begin 
    a_MR := Self.ModalResult; 
    if Assigned(FForm) then 
    FForm.EnableForm := True; 

    if Assigned(FModalResultEvent) then 
    FModalResultEvent(Self, a_MR); 
end; 
+0

Sie deaktivieren weiterhin das aufrufende Formular, das Formular mit der Taskleistenschaltfläche. Es wird keine Tastatur-/Mauseingabe einschließlich Klicks auf die Schaltfläche erhalten. –

+0

ja ...eine Umgehung für diesen –

+0

Code jetzt nur deaktiviert Kinder des Formulars und nicht das Formular selbst. –

Verwandte Themen