當我們按下一個菜單選項,或者一個控件需要通知父窗口一個事件發生(如鼠標單擊、雙擊等),或者快捷鍵被按下時,Windows將會發送一個 WM_COMMAND 消息給父窗口。那么 WM_COMMAND 消息參數是什么呢?
WM_COMMAND 消息來源 | WPARAM 高位 | WPARAM 低位 | LPARAM |
---|---|---|---|
菜單 | 0 | 菜單 ID | 0 |
快捷鍵 | 1 | 快捷鍵對應菜單 ID | 0 |
控件 | 響應 Code(如BN_CLICKED) | 控件 ID | 控件句柄 |
OK,一切運行的很好,通過 WPARAM 高位置1或0區分菜單、快捷鍵、或者控件事件Code,通過 WPARAM 低位可以知道發出WM_COMMAND消息的菜單項或控件ID,通過LPARAM知道控件句柄。
然而,有一天,當選中一個 ListControl 控件中的某一行時,人們忽然發現父窗口需要知道被選中該行的索引,這下為難了,對於控件來看,整個WM_COMMAND消息的WPARAM、LPARAM 都被塞的滿滿的。怎么辦呢?這兒有一種解決辦法:新增一個消息,就叫WM_LIST_CONTROL_CLICKED吧,如下:
消息類型 | WPARAM 高位 | WPARAM 低位 | LPARAM |
---|---|---|---|
WM_LIST_CONTROL_CLICKED | 被選中行的索引 | ListControl 控件的 ID | ListControl 控件的句柄 |
呃,看起來的確解決了問題,我們把事件 Code 通過消息 ID 體現了出來,然后把被選中行的索引塞進了WPARAM的高位,看起來非常完美!然而又有一天,人們發現對ListView,父窗口需要知道單擊該控件時選中的行號和列號,以便處理,照貓畫虎,我們又加了一個WM_LIST_VIEW_CLICKED。接着人們發現其他一些控件都需要這樣的改進,如果這樣增加消息的話,豈不是沒完沒了了?!!
於是,WM_NOTIFY消息橫空出世:
消息類型 | WPARAM | LPARAM |
---|---|---|
WM_NOTIFY | 發生 WM_NOTIFY 消息的控件 ID | NMHDR 指針 |
現在,我們將所有附加信息都存放在 NMHDR(Notify Message Handler)的一個結構體中,該結構體指針通過 LPARAM 通知到父窗口。NMHDR如下:
- typedef struct tagNMHDR
- {
- HWND hwndFrom; // 控件句柄.
- UINT_PTR idFrom; // 控件 ID.
- UINT code; // NM_ code.
- } NMHDR;
這只是一個一般的結構,如果我們需要知道 ListView選中的行和列,那么需要:
- typedef struct tagNMLISTVIEW
- {
- NMHDR hdr; // NMHDR.
- int iItem; // 行號.
- int iSubItem; // 列號.
- UINT uNewState;
- UINT uOldState;
- UINT uChanged;
- POINT ptAction;
- LPARAM lParam;
- } NMLISTVIEW, *LPNMLISTVIEW;
像其他的控件,都會對應這樣一個結構體,它們的第一個字段一定是 NMHDR。但一些微軟標准控件並不會發送WM_NOTIFY 消息,這些控件有:Edit、ComboBox、ListBox、Button、ScrollBar、Static等。所以在使用過程中請注意用法,最好的做法是參考MSDN。