注意,这些函数只有Private一种形式(也就是不允许覆盖,但仍在动态表格中):

其中TWinControl对TControl有10个消息进行了覆盖(红色标记),其中有2个是WM_消息,8个是CM_消息。

  TWinControl = class(TControl)
private
// 41个windows消息,几乎全部消息都是私有函数(因为不需要别人来调用)。很多都是覆盖消息,也有少部分是首次出现。
// 总结规律:直接接受消息的函数都起一个中转站的作用,其函数内容都十分简单。
// WM_PAINT第一次出现,由某些直接继承Win控件的类使用。而图形控件和自绘控件会自己响应这个消息。
// TControl 类控件的鼠标和重绘消息是从 Parent TWinControl 中产生的。但我们只发现了鼠标消息的产生,那么重绘消息是从哪里产生出来的呢?答案是TWinControl.WMPaint
// http://hi.baidu.com/bakyman/item/2a426ba5c6251d37020a4d42
procedure WMPaint(var Message: TWMPaint); message WM_PAINT; // 它调用PaintHandler函数,自己除了双缓冲以外不做处理(只有Win控件才能第一次接受这个消息)。
procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT; // 各种特殊效果就靠它了
procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND; // important7 是唯一的消息对应函数,且只有Win控件才能响应这个消息
procedure WMPrintClient(var Message: TWMPrintClient); message WM_PRINTCLIENT; //
procedure WMSysColorChange(var Message: TWMSysColorChange); message WM_SYSCOLORCHANGE;
// 调色板
procedure WMPaletteChanged(var Message: TMessage); message WM_PALETTECHANGED;
procedure WMQueryNewPalette(var Message: TMessage); message WM_QUERYNEWPALETTE;
// 重要消息
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND; // fixme 值得研究,搞清楚与Application的关系
procedure WMCommand(var Message: TWMCommand); message WM_COMMAND; // 命令消息,把WM_消息转换成CN_消息
procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY; // 通知消息
// 关键消息 http://bbs.2ccc.com/topic.asp?topicid=455945
procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY; // important 子控件创建、销毁、被点击时,这个消息被发到父控件。可以参考 TComponent.Notification
procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY; // fixme 取消内核注册,为什么?
procedure WMNCDestroy(var Message: TWMNCDestroy); message WM_NCDESTROY;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
// 系统消息
procedure WMTimeChange(var Message: TMessage); message WM_TIMECHANGE;
procedure WMWinIniChange(var Message: TMessage); message WM_WININICHANGE;
// 这6个消息都转发到子控件里去,执行子类的DefaultHandler,除非子类覆盖消息索引函数。如果子类都不处理,那么执行TWinControl的DefaultHandler
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL; // 给子控件发消息
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure WMCompareItem(var Message: TWMCompareItem); message WM_COMPAREITEM;
procedure WMDeleteItem(var Message: TWMDeleteItem); message WM_DELETEITEM;
procedure WMDrawItem(var Message: TWMDrawItem); message WM_DRAWITEM;
procedure WMMeasureItem(var Message: TWMMeasureItem); message WM_MEASUREITEM;
// 位置
procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED; // 覆盖
procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMMove(var Message: TWMMove); message WM_MOVE;
// 按键
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMSysKeyDown(var Message: TWMKeyDown); message WM_SYSKEYDOWN;
procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
procedure WMSysKeyUp(var Message: TWMKeyUp); message WM_SYSKEYUP;
procedure WMChar(var Message: TWMChar); message WM_CHAR;
procedure WMCharToItem(var Message: TWMCharToItem); message WM_CHARTOITEM;
procedure WMVKeyToItem(var Message: TWMVKeyToItem); message WM_VKEYTOITEM;
// 焦点
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
// 输入法
procedure WMIMEStartComp(var Message: TMessage); message WM_IME_STARTCOMPOSITION;
procedure WMIMEEndComp(var Message: TMessage); message WM_IME_ENDCOMPOSITION;
// 右键菜单,字体
procedure WMContextMenu(var Message: TWMContextMenu); message WM_CONTEXTMENU; // 覆盖,发送给Windows控件的子控件。important 很有意思很明了
procedure WMFontChange(var Message: TMessage); message WM_FONTCHANGE;
// 31个组件消息
// important 心得:许多消息函数起中转站的作用。对方爱处理不处理,反正目的达到了就行了
// 重要
procedure CMRecreateWnd(var Message: TMessage); message CM_RECREATEWND; // important 毁掉后,重新创建,并加上焦点
procedure CMInvalidate(var Message: TMessage); message CM_INVALIDATE; // important5 调用InvalidateRect后,对每一个子控件都调用此函数
procedure CMChanged(var Message: TMessage); message CM_CHANGED; // 如果有Parent,就调用Parent.WindowProc
procedure CMVisibleChanged(var Message: TMessage); message CM_VISIBLECHANGED; // 覆盖消息,更新状态后显示
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; // 与TControl的实现不同,调用API
procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED; // 简单调用API,真正显示窗口
procedure CMEnter(var Message: TCMEnter); message CM_ENTER; // 读取键盘布局后调用DoEnter
procedure CMExit(var Message: TCMExit); message CM_EXIT; // 简单调用DoExit
procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST; // TControl也有这个函数 什么都不做
// 一般
procedure CMShowHintChanged(var Message: TMessage); message CM_SHOWHINTCHANGED; // 广播消息
procedure CMChildKey(var Message: TMessage); message CM_CHILDKEY; // 转给父组件执行
// 广播消息
procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;
procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
procedure CMSysFontChanged(var Message: TMessage); message CM_SYSFONTCHANGED; // 有点特殊,先执行父类同名函数,再广播
procedure CMWinIniChange(var Message: TWMWinIniChange); message CM_WININICHANGE;
procedure CMFontChange(var Message: TMessage); message CM_FONTCHANGE;
procedure CMTimeChange(var Message: TMessage); message CM_TIMECHANGE;
procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
// 通知父控件,自己被改变了
procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED; // NotifyControls,会改变消息后转发消息
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; // NotifyControls
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED; // NotifyControls
procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED; //
//
procedure CMBorderChanged(var Message: TMessage); message CM_BORDERCHANGED; // 调用API
procedure CMCursorChanged(var Message: TMessage); message CM_CURSORCHANGED; // 发消息,调用API
procedure CMParentCtl3DChanged(var Message: TMessage); message CM_PARENTCTL3DCHANGED;
procedure CMDrag(var Message: TCMDrag); message CM_DRAG;
procedure CMControlListChange(var Message: TMessage); message CM_CONTROLLISTCHANGE; // 简单通知父控件,并调用它的WndProc
procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT;
procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT;
procedure CMFloat(var Message: TCMFloat); message CM_FLOAT;// 5个控件CN按键消息,还没有真懂
procedure CNKeyDown(var Message: TWMKeyDown); message CN_KEYDOWN;
procedure CNKeyUp(var Message: TWMKeyUp); message CN_KEYUP;
procedure CNChar(var Message: TWMChar); message CN_CHAR;
procedure CNSysKeyDown(var Message: TWMKeyDown); message CN_SYSKEYDOWN; // important
procedure CNSysChar(var Message: TWMChar); message CN_SYSCHAR;
end;

再看它的WndProc函数:

procedure TWinControl.WndProc(var Message: TMessage);
var
Form: TCustomForm;
begin
with Message do
// 只处理少部分Windows控件很明显的通用消息,简单记忆:也就6类消息(不是6个)
// 只要是拦截的消息,都要带上Exit,不必继续传递了
case Message.Msg of
WM_SETFOCUS: //设置控件的焦点
begin
Form := GetParentForm(Self);
if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit; // 退出
end;
WM_KILLFOCUS:
if csFocusing in ControlState then Exit; // 退出
//当鼠标有活动的时候发出该消息,如果鼠标没有被捕捉到,则消息发往鼠标下面的那个窗口,否则消息将发往捕捉到鼠标的那个窗口。
WM_NCHITTEST: // important7 fixme 为什么这里也有,消息索引函数里也有呢
begin
inherited WndProc(Message);
//如果窗体被挡住并且在指定的点没有控件,则返回结果为在client区。
if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
Message.Result := HTCLIENT;
Exit; // 退出
end;
//鼠标消息是否直接发往组件的窗体子组件
WM_MOUSEFIRST..WM_MOUSELAST: // 13个鼠标消息,包括了WM_MOUSEMOVE WM_LBUTTONDOWN消息等等。
begin
// 我认为TLabel的OnClick第一原动力来自这里,但是没法调试,因为还没点击,鼠标移动也会停到这里来。必须加代码:
if Message.Msg = WM_LBUTTONDOWN then
begin
tag:=;
end;
// 不是简单的判断,此函数做了无数的事情。即使返回False,也仍然执行过了此函数
// 图形控件执行鼠标消息就靠这个函数,这个函数内部如果测试是图形控件的位置接受的鼠标消息,就会Perform执行
if IsControlMouseMsg(TWMMouse(Message)) then // 测试是否是图形子控件的消息,而且消息已经被执行过了
begin
{ Check HandleAllocated because IsControlMouseMsg might have freed the
window if user code executed something like Parent := nil. }
// 如果图形子控件处理消息有问题(没有处理),那么放到父窗口的默认窗口函数里去执行
// FIXME 如果Win控件本身要处理这个消息怎么办?
if (Message.Result = ) and HandleAllocated then
// 我猜是因为,如果控件自身覆盖了鼠标消息,那么就不会执行到这里来了。到了这里就是让系统自动处理鼠标消息
// 屏蔽执行似乎没问题
// 出问题就扔给Windows默认窗口函数,为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。
DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam); // API,默认窗口函数,真正起作用的一个函数,另一处不起作用
Exit; // 退出
end;
end;
WM_KEYFIRST..WM_KEYLAST: // 10个键盘消息
if Dragging then Exit; // 退出
WM_CANCELMODE: // 启动模式窗口, 当前窗口会收到一条 WM_CancelMode 消息; 改消息无参数。http://www.cnblogs.com/del/archive/2008/10/29/1322205.html
if (GetCapture = Handle) // API fixme
and (CaptureControl <> nil) // 全局变量 fixme 要研究
and (CaptureControl.Parent = Self) then // 如果自己是自己的父窗口
CaptureControl.Perform(WM_CANCELMODE, , ); // 那么转发消息 fixme 问题:会转发到哪里呢?还是WndProc吗?回答:不是,图形子控件有相应的消息函数。但它就是Win控件,所以还是会执行到WndProc来。做实验
end;
// 上面的消息找不到才进一步向上传递。看前面大多数消息都有Exit
inherited WndProc(Message); // important7 最后一定向上传递消息,不管被处理过没有
end;

当然还有DefaultHandler:

procedure TWinControl.DefaultHandler(var Message);
begin
if FHandle <> then
begin
with TMessage(Message) do
begin
if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then
begin
Result := Parent.Perform(Msg, WParam, LParam);
if Result <> then Exit;
end;
case Msg of
WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
begin
SetTextColor(WParam, ColorToRGB(FFont.Color));
SetBkColor(WParam, ColorToRGB(FBrush.Color));
Result := FBrush.Handle;
end;
else
if Msg = RM_GetObjectInstance then
Result := Integer(Self)
else
begin
if Msg <> WM_PAINT then
Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);
end;
end;
if Msg = WM_SETTEXT then
SendDockNotification(Msg, WParam, LParam);
end;
end
else
inherited DefaultHandler(Message);
end;

这样算下来,TWinControl总共处理了41+15-2=54个Windows消息,31+17-8=40个CM_消息,总计94个消息函数(不算WinProc和DefaultHandler),这个数字十分惊人,这些功能是Delphi适应Windows消息机制的功能保证,但做出来的程序却又不占用很多内存,实在是高明之级。

感悟:TWinControl拥有如此之多的消息函数,其子类对象却不占用很大内存,原因就是Delphi透过特殊的Dispatch函数在祖先类中对消息进行检索,而无需占用子类自身VMT表,我觉得Delphi能想出这个法子并做到这一点,实在是太牛了。对Delphi的研究越深入,就越能感觉到它的绝美。

-------------------------------------------------------------------------

特意查了一下,XE5增加了6个WM_消息的处理,分别是:

WM_INPUTLANGCHANGE
WM_MOUSEACTIVATE
WM_GESTURE
WM_GESTURENOTIFY
WM_IME_CHAR
WM_TABLET_QUERYSYSTEMGESTURESTATUS

但是没有统计CM_的新情况。

TWinControl的消息覆盖函数大全(41个WM_函数和31个CM_函数,它的WndProc就处理鼠标(转发)、键盘(取消拖动)、焦点、和WM_NCHITTEST一共4类消息)的更多相关文章

  1. TControl的消息覆盖函数大全(15个WM_函数和17个CM_函数,它的WndProc就处理鼠标与键盘消息)

    注意,这些函数只有Private一种形式(也就是不允许覆盖,但仍在动态表格中)(特别注意,这里居然没有WM_PAINT函数): TControl = class(TComponent) private ...

  2. orale 函数大全[转]

    oracle函数大全 http://wenku.baidu.com/link?url=bXaGsnn8iN264GB8ec48IUPg5eRGDKAyAiSw0OBKL1I0mBVG549-2u9HT ...

  3. delphi字符串函数大全

    转帖:delphi字符串函数大全 2009-11-17 16:43:55 分类: delphi字符串函数大全 ━━━━━━━━━━━━━━━━━━━━━首部 function StringToGUID ...

  4. TWinControl与TControl的覆盖函数(TWinControl对TControl的10个消息覆盖函数,17个覆盖函数,私有虚函数仍可多态)

    手工找出来,对比一下,有助于VCL框架的理解.----------------------------------------------------------------------------- ...

  5. SendMessage函数的常用消息及其应用大全

    来源:http://www.360doc.com/content/09/0814/10/19147_4907488.shtml,非常全面的解释. 文本框控件通常用于输入和编辑文字.它属于标准 Wind ...

  6. PB函数大全

    PB函数大全 Abs()功能计算绝对值.语法Abs ( n )参数n:要得到绝对值的数值型变量或表达式返回值返回值的数据类型与n的数据类型相同,函数执行成功时返回n的绝对值.如果参数n的值为NULL, ...

  7. 【转】NI语法 JNI参考 JNI函数大全

    原文网址:http://blog.sina.com.cn/s/blog_5de73d0b0101chk1.html 一.对照表 Java类型    本地类型         描述boolean     ...

  8. LoadRunner 函数大全之中文解释

    LoadRunner 函数大全之中文解释 // sapgui_table_set_column_selected 模拟用户 // 单击表中的列标题. int sapgui_table_set_colu ...

  9. 转:Delphi 函数大全

    Delphi 函数大全 - xiucaiyao的专栏 - 博客频道 - CSDN.NEThttp://blog.csdn.net/xiucaiyao/article/details/4544039 名 ...

随机推荐

  1. Tomcat与web程序结构与Http协议

    telnet 一:打开telnet服务: 控制面板------> 程序和功能---> 打开或关闭windows功能---> 选中 Telnet客户端--->确定 二:测试tel ...

  2. 数学之路-python计算实战(14)-机器视觉-图像增强(直方图均衡化)

    我们来看一个灰度图像,让表示灰度出现的次数,这样图像中灰度为 的像素的出现概率是  是图像中全部的灰度数, 是图像中全部的像素数,  实际上是图像的直方图,归一化到 . 把  作为相应于  的累计概率 ...

  3. Activity的绘制流程简单分析(基于android 4.0源码进行分析)

    要明白这个流程,我们还得从第一部开始,大家都知道 在activity里面 setcontentview 调用结束以后 就可以看到程序加载好我们的布局文件了,从而让我们在手机上看到这个画面. 那么我们来 ...

  4. C++多继承的好处是增加了弹性和灵活性,Delphi类强迫单继承TObject是为了提供许多强大功能

    要说灵活性,是C++更强.我自己开发已经好几次碰到需要多继承的情况了. 但是Delphi强迫继承TObject,虽然是单继承,但是提供了相当多的强力功能.要说强大,那还是Delphi当仁不让. 摘自& ...

  5. C++智能指针的实现

    说起智能指针,不少人都不陌生.比方auto_ptr.shared_ptr.unique_ptr.weak_ptr. 依据shared_ptr的功能,自己仿造也实现了个. 对于shared_ptr这样的 ...

  6. 解决java mail发送TXT附件被直接显示在正文中的问题

    这两天遇到一个问题,关于使用java mail发送邮件的问题. 详细是这样子的:我使用java mail发送异常报告邮件,邮件中有一个包含异常日志的附件,和关于设备信息的邮件正文.假设日志为log后缀 ...

  7. FindChildControl与FindComponent

    前两天编码遇到了要使用FindChildControl方法获取指定名称的TSpeedButton按钮,结果折腾了半天就是没得结果(基础不扎实,呵呵),于是赶紧搜索了下,补习关于这两个方法的用法. TW ...

  8. 在纯C工程的main函数之前跑代码(手工找到程序入口点, 替换为我们自己的函数)

    在main函数之前跑代码的方法 方法: 手工找到程序入口点, 替换为我们自己的函数 写测试程序 // test.cpp : Defines the entry point for the consol ...

  9. kettle 数据迁移 (转)

    最近在公司搞一个项目重构迁移问题,旧项目一直在线上跑,重构的项目则还没上线.重构之后数据库表结构,字段,类型等都有变化,而且重构的数据库由oracl改为mysql.这样就设计到数据迁移问题,别人推荐下 ...

  10. Android中View绘制优化之三---- 优化View

    本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 译三: 优化视图 关于如何设计自定义View以及响应触摸时间等,请看Android developer : 地 ...