解读WM_NOTIFY消息中的NMHDR结构

来源:互联网 发布:php模拟post请求接口 编辑:程序博客网 时间:2024/05/16 09:26

 VC中的消息的分类有3种:窗口消息、命令消息和控件通知消息,我们这里要谈的是最后一种:控件通知消息。   

  控件通知消息,是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控件通知消息;单击鼠标所产生的消息可以由主窗口直接处理,然后交给控件窗口处理。  

  控件通知消息主要由窗口类即直接或间接由CWND类派生类处理。   

  控件通知格式  

  控件通知经历了一个演变过程,因而SendMessage( )的变量Message、wParam和lParam有三种格式。  

  第一控件通知格式  

  第一控件通知格式只是窗口消息的子集。它的特征格式如下:WM_XXXX。它主要来自下面的3种消息类型:     

  (1)表示一个控件窗口要么已经被创建或销毁,要么已经被鼠标单击的消息:WM_PARENTNOTIFY;  

  (2)发送到父窗口,用来绘制自身窗口的消息,例如: WM_CTLCOLOR、WM_DRAWITEM、WM_MEASUREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKTOITEM、WM_COMMAND和WM_COMPAREITEM  

  (3)有滚动调控件发送,通知父窗口滚动窗口的消息:WM_VSCROLL和WM_HSCROLL  

  第二控件通知格式  

  第二控件通知格式与命令消息共享,它的特征格式如下:WM_COMMAND。  

  在WM_COMMAND中,lParam用来区分是命令消息还是控件通知消息:如果lParam为NULL,则这是个命令消息,否则lParam里面放的必然就是控件的句柄,是一个控件通知消息。对于wParam则是低位放的是控件ID,高位放的是相应的消息事件。  

  第三控件通知格式 


  这个才真正涉及到我们要讲的内容,同时他也是最为灵活的一种格式。它的特征格式如下:WM_NOTIFY。 

在WM_NOTIFY中,lParam中放的是一个称为NMHDR结构的指针。在wParam中放的则是控件的ID。  

  NMHDR结构的由来  

  NMHDR结构是很值得一提的,该结构包括有关制作该通知的控件的任何内容,而不受空间和类型的限制,他的来历也是很有意思的。  

  在最初的windows3.x中,根本就不存在什么WM_NOTIFY,控件通知它们父窗口,如鼠标点击,控件背景绘制事件,通过发送一个消息到父窗口。简单的通知仅发送一个WM_COMMAND消息,包含一个通知码和一个在wParam中的控件ID及一个在lPraram中的控件句柄。这样一来,wParam和lParam就都被填充了,没有额外的空间来传递一些其它的消息,例如鼠标按下的位置和时间。  

  为了克服这个困难,windows3.x就提出了一个比较低级的解决策略,那就是给一些消息添加一些附加消息,最为明显的就是控件自画用到的DRAWITEMSTRUCT。不知道大家对这个结构熟悉不,不过,如果你是老手,你应该非常清楚这个结构,这个结构包含了9个内容,几乎你需要控制的信息都给你提供了。为什么说它比较低级呢?因为不同的消息附加的内容不同,结果就是一盘散沙,非常混乱。  

  在win32中,MS又提出了一个更好的解决方案:引进NMHDR结构。这个结构的引进就是消息统一起来,利用它可以传递复杂的信息。这个结构的布局如下:  

NMHDR { 

HWnd hWndFrom  相当于原WM_COMMAND传递方式的lParam 

UINT idFrom  相当于原WM_COMMAND传递方式的wParam(low-order) 

UINT code  相当于原WM_COMMAND传递方式的Notify Code(wParam"s high-order) };  

  对于这个结构的应用于WM_NOTIFY信息结构,结果WM_NOTIFY就变成了:  

  A、无附加信息。结构变得很简单,就是一个NMHDR结构。    

  B、有附加信息。定义一个大的结构,它的第一个元素就是NMHDR结构,它的后面放置附加信息。  

WM_NOTIFY结构的好处  

  除了上面我们所说的好处外,WN_NOTIFY还有自己的独特的好处:  

 

  由于在大结构中,第一个成员为NMHDR,这样一来,我们就可以利用指向NMHDR的指针来传递结构地址,根据指针的特性,无论消息有没有附加信息,这个指针都适用,也能够很方便的进行强制转换。  

  分析ON_NOTIFY  

  类向导可以创建ON_NOTIFY消息映射入口并提供一个处理函数的框架,来处理 WM_NOTIFY类型的消息。ON_NOTIFY消息映射宏有如下语法.  

   ON_NOTIFY(wNotifyCode,id,memberFxn)  

  其中:wNotifyCode:要处理的通知消息通知码。比如上面我们提到的LVN_KEYDOWN;Id:控件标识ID;MemberFxn:处理此消息的成员函数。  

  此成员函数有如下的原型声明:  

   afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result);   

  比如:假设你想成员函数OnKeydownList1处理ClistCtrl(标识ID=IDC_LIST1)的 LVN_KEYDOWN消息,你可以使用类向导添加如下的消息映射:  

   ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )  

  在上面的例子中,类向导提供如下函数:  

   void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)     { 

     LV_KEYDOWN* pLVKey= (LV_KEYDOWN*)pNMHDR;      *pResult = 0;      }  

  这时类向导提供了一个适当类型的指针,你既可以通过pNMHDR,也可以通过 pLVKey来访问这个通知结构。  

  ON_NOTIFY_RANGE  

  有时我们可能需要为一组控件处理相同的WM_NOTIFY消息。这时需要使用ON_NOTIFY_RANGE而不是ON_NOTIFY。不过,很不幸的是,VC6的ClassWizard并不支持这个消息,所以我们必须手工添加。方法和一般的手工添加的消息一样,不过需要注意的是:  

  (1)当你使用 ON_NOTIFY_RANGE时,你需要指定控件的ID范围.其消息映射入口及函数原型如下: 

    ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )  

  其中:wNotifyCode:消息通知码.比如:LVN_KEYDOWN。id: 第一控件的标识ID。    

   idLast:最后一个控件的标识ID。(标识值一定要连续)memberFxn: 消息处理函数。  

  (2)成员函数必须有如下原型申明:afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );


下面附带讲一下NMHDRLPNMTREEVIEWTVITEM结构体  

树控制的数据结构 

在使用树控制时需要了解两个个非常重要的数据结构TV_ITEMTV_INSERTSTRUCT,前一个数据结构是用来表示树控制的树项信息,后一个数据结构是用来定义将树项增加到数据控制中所需要的数据内容。另外,还需要NM_TREEVIEW TV_DISPINFOTV_HITTESTINFO三个数据结构,这几个数据结构的定义方法如下:

 ①基本数据项结构

 typedef struct _TV_ITEM

 { 

UINT mask; //结构成员有效性屏蔽位 

HTREEITEM hItem; //数据项控制句柄 

UINT state; //数据项状态 

UINT stateMask; //状态有效性屏蔽位

 LPSTR pszText; //数据项名称字符串

 int cchTextMax; //数据项名称的最大长度

 int iImage; //数据项图标索引号 

int iSelectedImage;//选中数据项图标索引号 

int cChildren; //子项标识 

LPARAM lParam; //程序定义的32位数据

 } TV_ITEM, FAR *LPTV_ITEM;  

 

②插入树项结构 

typedef struct _TV_INSER TSTRUCT

 { 

HTREEITEM hParent; //父项控制句柄 

HTREEITEM hInsertAfter; //插入树项的位置 

TV_ITEM item; //数据项的结构 

} TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;  

其中插入的位置如果是TVI_FIRST TVI_LAST ,则分别插入到树控制的最前面或最后面,如果是TVI_SORT ,则插入的树项自动插入到合适的位置。 

③树控制通知消息结构 

typedef struct _NM_TREEVIEW

 { 

NMHDR hdr; //通知消息句柄 

UINT action; //通知消息标志

 TV_ITEM itemOld; //原来的数据结构

 TV_ITEM itemNew; //新的数据结构 

POINT ptDrag; //拖动指针

} NM_TREEVIEW;  

 

④取得或设置数据结构 

typedef struct _TV_DISPINFO

 { 

tvdi  NMHDR hdr; //通知消息控制句柄 

TV_ITEM item; //数据项结构 

} TV_DISPINFO; 

 

⑤指针测试数据结构 

typedef struct _TVHITTESTINFO 

{

 tvhtst  POINT pt; //客户区域屏幕坐标指针 

UINT flags; //存放测试结果的变量 

HTREEITEM hItem; //测试的数据项结构 

} TV_HITTESTINFO, FAR *LPTV_HITTESTINFO;  

其中flags测试结果可以是如下值: 

TVHT_ABOVE 在客户区域上面 TVHT_BELOW 在客户区域下面 

 TVHT_NOWHERE 在客户区域中并在最后一项下面 

TVHT_ONITEM 在与树项关联的位图或标签内 

TVHT_ONITEMBUTTON 在与树项关联的按钮上 

TVHT_ONITEMICON 在与树项关联的位图上

 TVHT_ONITEMINDENT 在与树项关联的联线上

 TVHT_ONITEMLABEL 在与树项关联的标签上 

TVHT_ONITEMRIGHT 在树项的右侧区域中 

TVHT_ONITEMSTATEICON 在用户定义的状态图标上

 TVHT_TOLEFT 在客户区域的左侧 

TVHT_TORIGHT 在客户区域的右侧  

 

NMHDR为一个结构体。其形式如下: 

typedef struct tagNMHDR 

{      

HWND hwndFrom;      

UINT idFrom;      

UINT code;  

} NMHDR; 

hwndFrom为正在发送消息的控件的句柄。 

idFrom为正在发送消息的控件的ID。

 code为消息通知码。 

 #define NM_TREEVIEW             NMTREEVIEW NMTREEVIEW也为一结构体。其形式如下: 

typedef struct tagNMTREEVIEW

 {     

NMHDR hdr;      

UINT action;      

TVITEM itemOld;     

TVITEM itemNew;      

POINT ptDrag;  

} NMTREEVIEW, FAR *LPNMTREEVIEW; 

 hdr  包含有关通知消息的NMHDR变量。

 action 通知消息方式标志。 

 itemOld 包含旧节点状态的TVITEM结构 

 itemNew 包含新节点状态的TVITEM结构,在我看来是使用过与被使用过,oldpen,newpen与其同理。 

 ptDrag 事件发生时客户区鼠标坐标   

 

这里不有一个结构体TVITEM,形式如下:

 typedef struct tagTVITEM

{    

 UINT      mask;    

 HTREEITEM hItem;    

 UINT      state;     

UINT      stateMask;    

 LPTSTR    pszText;    

 int       cchTextMax;    

 int       iImage;     

int       iSelectedImage;    

 int       cChildren;     

LPARAM    lParam;

 } TVITEM, FAR *LPTVITEM; 

这里包含的都是节点的一些属性


1 0
原创粉丝点击