TPaintBox 响应自定义消息(100分)

  • 主题发起人 主题发起人 MengZhaoXing
  • 开始时间 开始时间
M

MengZhaoXing

Unregistered / Unconfirmed
GUEST, unregistred user!
从tpaintbox 继承,其中定义了 procedure ... 响应自定义消息。

但 tpaintbox 没有 hwnd ,我怎么给她法消息

tapintbox 可以响应 paint, mouse消息,它是怎么做的????
 
怎么没人愿意讨论这个问题,呜呜。

?????
 
override WndProc 或许可以
你去试试看
 
你可以使用TControl的Perform方法给他发消息,就象这样:
PaintBox.Perform(WM_Paint,0,0);
 
问题不在于 override wndproc
而是 我在应用程序中如何 给tpaintbox 发消息,
postmessage 需要hwnd, 而 tpaintbox 不是从twincontrol继承的,没有handle,
但tpaintbox 可以响应paint,mouse等消息,也就是说我自定义的消息,也应该可以
响应,但她没句柄(或则它的句柄藏在什么地方?怪),我怎么发给它,
之所以用消息儿不是事件,是因为要异步处理。

但一种变通解决办法,是将消息发给parent,然后重载panrent及self的wndproc,
在panrent.wndproc 中监测消息,调用self.wndproc,但假如有多个paintbox,
这里存在识别和引用的问题,总之很不自然。

这里我是想和大家讨论一下此类问题,我们可以更引申一步,较低层次的类如
tcomponent 如何响应消息,或者如何给她发消息??
 
看了Delphi的源代码,如下:

//以鼠标消息为例

procedure TWinControl.WndProc(var Message: TMessage);
var
....
begin
....
case Message.Msg of
//是鼠标消息
WM_MOUSEFIRST..WM_MOUSELAST:
if IsControlMouseMsg(TWMMouse(Message)) then Exit;
....
end;

function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
Control: TControl;
P: TPoint;
begin
//获取当前捕获鼠标的窗口
//如果是自己
if GetCapture = Handle then
begin
Control := nil;
if (CaptureControl <> nil) and (CaptureControl.Parent = Self) then
//获取当前捕获鼠标的TControl
Control := CaptureControl;
end else
//不是自己,说明是自己的子窗口
//根据鼠标位置来获取TControl
Control := ControlAtPos(SmallPointToPoint(Message.Pos), False);
Result := False;
//由TControl来处理消息
if Control <> nil then
begin
P.X := Message.XPos - Control.Left;
P.Y := Message.YPos - Control.Top;
Control.Perform(Message.Msg, Message.Keys, Longint(PointToSmallPoint(P)));
Result := True;
end;
end;

//其中CaptureControl:TControl是Controls.Pas中的
//全局变量,用于管理当前捕获鼠标的TControl,
//详细情况见Control.Pas中
//{ Mouse capture management }以下的部分
//var
// CaptureControl: TControl = nil;
//....
//function GetCaptureControl: TControl;
//....
//procedure SetCaptureControl(Control: TControl);
//....

看到了吗?由TWinControl来接收Windows消息,
它是Window,有Window Hanlde,一切都没有问题,
然后它分析当前是哪个子Control捕获鼠标,
调用这个TControl的Perform()方法,
最终在TControl.WndProc中将消息处理掉。

可见Delphi将Windows中消息的概念扩展开了,
Windows中消息全部是相对Window而言的,
但Delphi显然不这么认为,它认为没有Window
Hanlde的TControl也应该响应消息。如何实现
呢?TControl都有Parent:TWinControl,就在
Parent中接收Windows系统的消息,然后解释为
Delphi自己的处理体系。

是不是很严密的思想?我觉得真是很酷的想法,
由于Delphi的工作,我们现在可以使用没有Hanlde
的TControl,只要能响应消息,能Draw到屏幕
上,谁知道是什么东西呢?这样做的明显好处就
是节省大量的Windows资源,那些功能简单的东西
我们只要响应几个消息,画几个图形就够了,而不必
创建一个Window
 
>由于Delphi的工作,我们现在可以使用没有Hanlde
>的TControl,只要能响应消息,能Draw到屏幕
-----问题就在这里,对于自定义消息,通过什么异步进入wndproc,
除了我前面提到的给parent发消息外,可有什么好办法
附带说一句,我对windows的消息机制深恶痛绝,消息响应不是围绕进程
而是愚蠢的窗口,也不知 M$那帮脑袋是怎么想的
>上,谁知道是什么东西呢?这样做的明显好处就
>是节省大量的Windows资源,那些功能简单的东西
>我们只要响应几个消息,画几个图形就够了,而不必
>创建一个Window
 
问题是parent并不把所有消息都转发给自己的GraphicControl,所以把消息发给
parent不总有效。所以在本程序内部,还是用Perform方法为好。注意他不是异步
的,实际就是调用相应的消息处理函数。另外,可以用parent的BroadCast方法广播
消息给自己的control.
 
给parent发消息是可以的,你需要 override parent和自己的 wndproc,
在parent.wndproc中

if message = wm_usermessage then
paintbox.wndproc;
~~~~~~~~ 问题在于需要知道 调用那一个实例的wndproc?
inhirented wndproc;

 
> ~~~~~~~~ 问题在于需要知道 调用那一个实例的wndproc?

办法倒有一个:
在你自定义的消息中将一个
参数设置为paintbox地址(或Name属性值),

然后
1、如果用地址
TPaintBox(Point(Message.LParam)^).WndProc(Message)
2、如果用Name
with FindComponent(PChar(Message.LParam)^) as TPaintBox do
WndProc(Message)
 
to 李颖,这个办法还可以,其实我是想讨论一下,非control类的消息响应的解决
办法,如 直接从tcomponent继承的类如何响应消息。
我们可以从消息的发送开始,
在系统中
1。postmessage -> 2。windows消息队列 ->
3. 由系统将消息分发给个窗口的消息队列 -> 4. 窗口wndproc ->
5. 子窗口消息队列 -> 6. 子窗口wndproc -> .....

在 delphi 中 1.1application.run -> 1.2application.handlemessage

-> 1.3application.processmessage -> 1.4peekmessage -> ???

?? -> 1-5.TWinControl.MainProc-> 1-6.TControl.WindowPoc
1.4->1.5 如何衔接的? ~~~~~~? 怎么进入的
-> 1-7.TControl.WndPoc -> 1-8.TObject.Dispath -> 1-9.消息响应回调
~~~~~~~~~? 怎么做的
我看 application 也有一个 wndproc 是什么时候调用的?


 
to MengZhaoXing: 你否确切地说明你要做什么? 准备发哪一类的消息?
 
1.5->1.6在上面的代码里可以看到基本思路

1.7->1.8如下:
procedure TControl.WndProc(var Message: TMessage);
var
...
begin
...
Dispatch(Message);
end;
毕竟TControl是从TObject继承来的,应该想得到啊!

TApplication源代码如下:

procedure TApplication.Run;
begin
...
repeat
HandleMessage
until Terminated;
...
end;
---------------------------------------
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
---------------------------------------
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
//看到了吗PeekMessage在这里调用
//
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
....
if Msg.Message <> WM_QUIT then
begin
....
TranslateMessage(Msg);
DispatchMessage(Msg);
....
end
....
end
end;
---------------------------------------

其中TranslateMessage和DispatchMessage都是Windows API
1.4->1.5似乎就是这里完成的
 
同志们哪!多看源代码啊!
 
to huizhang: 我是想发 非窗口类消息,如将自定义消息,wm_user + 1等,发给
一个非窗口类对象(也就是说不是从twincontrol继承的,而是
从tcomponent 继承),我想修改delphi 源码,这样我就可以
给此类响应此类消息的组件,向普通的窗口消息类一样使用,
既定义消息,在组件中定义消息的响应句柄。然后over.

to 李颖: 1.我是问dispath 那段 汇编代码是什么意思
2. application.wndproc 干吗的,什么地方用它?

我想前提 1.message 格式不变

要解决的问题 1. 非窗口类的标识问题 (如同窗口类的hwnd)
2. 消息发送问题,如何进入系统队列-> winmain?
3. 消息的调度问题,如何遍历找到消息的响应
 
1、TApplication直接从TComponent继承来,
没有WndProc
2、消息接收者的标识
上面已经说过几种方法,我觉得用
TControl的地址来标识比较简单。
如果觉得不标准,可以用Tag属性来
区分,但要保证Tag不重复。可以在
TControl的创建方法中检查Parent
中其他所有Control.Tag,然后给自
己生成一个不同的。
3、消息的发送
发送到接受者的Parent
4、Parent收到后按照(2)中确定的
标识方案,在Controls中遍历查找
匹配的子Control,找到后调用
子Control的WndProc
 
关于TObject.Dispatch
1、记得对象方法中定义消息响应过程的格式吗?
procedure XXXX(var Message:XXX);message XXXX
Delphi编译时会将消息响应方法生成列表(在VMT中),
2、TObject.Dispatch
其中的代码就是根据Message参数,
在消息响应方法列表中查找匹配的方法,
调用它。

Delphi帮助说明如下
Dispatch determines whether a message is in the list of message
handlers declared for the object. If the object does not handle the
message, Dispatch then examines the message-handler list of the
ancestor type, and continues checking ancestors until it either finds
a specific handler or runs out of ancestors, in which case it calls
DefaultHandler.
Dispatch确定消息是否在消息处理里表列表中,如果对象不处理这个消息,
Dispatch检查祖先类的消息响应列表,并继续这个过程,直到找到一个响应
这个消息的方法,或者在继承树中查找完毕,这时调用DefaultHandler。
 
多人接受答案了。
 
后退
顶部