Reputation: 4827
In a new project, I created a MainForm with 2 panels, and a Form with a button.
I added this code on the MainForm:
interface
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
private
procedure OnMyMessage(var Msg: TMessage); message WM_FILEREADY;
public
{ Public declarations }
end;
implementation
uses
PannelForm;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
with TForm2.Create(self) do
try
parent := panel2;
borderstyle := bsNone;
InnerHandle := self.Handle;
Show;
finally
end;
end;
procedure TForm1.OnMyMessage(var Msg: TMessage);
begin
showmessage('got event');
end;
And this code on the Form with a button:
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
InnerHandle:HWND;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
// PostMessage(Application.Mainform.Handle, WM_FILEREADY, 0, 0); // works
// PostMessage(Application.Handle, WM_FILEREADY, 0, 0); // not working
// PostMessage(parent.Handle, WM_FILEREADY, 0, 0); // not working
PostMessage(InnerHandle, WM_FILEREADY, 0, 0); // works
end;
My question is: when calling the first and forth version, everything is fine.
What is missing in the third version that is not working?
Why doesn't Parent contain the right handle? Isn't it the (part of) point of passing a Parent?
Upvotes: 0
Views: 761
Reputation: 595672
You set the Parent
to be Form1.Panel2
, not Form1
itself. Your message handler will only receive messages that are posted to Form1
directly. Your other calls are posting to Form1.Handle
, which is why they work.
If you want to post the message to Parent.Handle
when Parent
is not Form1
, you will have to subclass the Panel you are assigning as Parent
:
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
private
DefPanelWndProc: TWndMethod;
procedure PanelWndProc(var Msg: TMessage);
public
{ Public declarations }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DefPanelWndProc := Panel2.WindowProc;
Panel2.WindowProc := PanelWndProc;
Form2 := TForm2.Create(Self);
Form2.Parent := Panel2;
Form2.BorderStyle := bsNone;
Form2.Show;
end;
procedure TForm1.PanelWndProc(var Msg: TMessage);
begin
if Msg.Msg = WM_FILEREADY then
ShowMessage('got event')
else
DefPanelWndProc(Msg);
end;
Otherwise, post the message to Form1
instead.
If you post using the Form1.Handle
property each time, everything will be fine (I am not counting multi-threaded code, as TWinControl.Handle
is not thread-safe). However, if you cache the value of Form1.Handle
to a variable and then post using that variable, your code will stop working if Form1.Handle
is recreated (which can and does happen). In that case, you need to detect the recreation and update the variable accordingly:
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
protected
procedure CreateWnd; override;
procedure DestroyWnd; override;
private
procedure OnMyMessage(var Msg: TMessage); message WM_FILEREADY;
public
{ Public declarations }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form2 := TForm2.Create(Self);
Form2.Parent := Panel2;
Form2.BorderStyle := bsNone;
Form2.InnerHandle := Self.Handle;
Form2.Show;
end;
procedure TForm1.CreateWnd;
begin
inherited;
if Form2 <> nil then
Form2.InnerHandle := Self.Handle;
end;
procedure TForm1.DestroyWnd;
begin
if Form2 <> nil then
Form2.InnerHandle := 0;
inherited;
end;
procedure TForm1.OnMyMessage(var Msg: TMessage);
begin
ShowMessage('got event');
end;
Otherwise, don't use Form1.Handle
at all. Use a different window that will never be recreated.
You can use AllocateHWnd()
to create a dedicated window:
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
MsgWnd: HWND;
procedure MsgWndProc(var Msg: TMessage);
public
{ Public declarations }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MsgWnd := AllocateHWnd(MsgWndProc);
Form2 := TForm2.Create(Self);
Form2.Parent := Panel2;
Form2.Borderstyle := bsNone;
Form2.InnerHandle := MsgWnd;
Form2.Show;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if MsgWnd <> 0 then
DeallocateHWnd(MsgWnd);
end;
procedure TForm1.MsgWndProc(var Msg: TMessage);
begin
if Msg.Msg = WM_FILEREADY then
ShowMessage('got event')
else
Message.Result := DefWindowProc(MsgWnd, Msg.Msg, Msg.WParam, Msg.LParam);
end;
Or you can use the Application.Handle
window:
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
function AppWndProc(var Msg: TMessage): Boolean;
public
{ Public declarations }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.HookMainWindow(AppWndProc);
Form2 := TForm2.Create(Self);
Form2.Parent := Panel2;
Form2.Borderstyle := bsNone;
Form2.InnerHandle := Application.Handle;
Form2.Show;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Application.UnhookMainWindow(AppWndProc); end;
function TForm1.AppWndProc(var Msg: TMessage): Boolean:
begin
if Msg.Msg = WM_FILEREADY then
begin
ShowMessage('got event');
Result := True;
end else
Result := False;
end;
Upvotes: 1
Reputation: 28517
You have implemented message handling in TForm1
, but Form2.Parent.Handle
is not Form1.Handle
instead you have assigned Panel2.Handle
to it.
Each windowed control has its own handle. So your panels have different handles than your form and they cannot process messages that are implemented in Form class.
Everything works as it should, even though it is not what you expect.
Upvotes: 2