子窗口和父窗口间的消息传递
子窗口和父窗口之间如何进行通信呢,答案是肯定,仍然是通过消息来传递,以按钮为例,当我们用鼠标单击按钮的时候,此时子窗口(也就是按钮)会向其父窗口发送一个WM_COMMAND的消息。其中消息的各个参数如下所示:
wParam:低字节表示子窗口的ID,这个值使我们使用CreateWindow时传递的参数,即其中的HMENU项。高字节表示通知码,根据通知码我们可以知道该消息的含义,比如,如果按钮被点击,则对应的通知码是BN_CLICKED。
lParam:子窗口句柄,即我们调用CreateWindow时得到的返回值。
上面讲到的只是子窗口向父窗口传递消息,同样,父窗口也可以向子窗口发送消息,对于按钮来说,定义了如下的按钮消息
BM_GETCHECK BM_SETCHECK BM_GETSTATE BM_SETSTATE BM_CLICK BM_GETIMAGE BM_SETIMAGE
BM_GETCHECK和BM_SETCECK消息由父窗口发送给子窗口空间,以取得或者设定复选框和单选按钮的选中标记。BM_GETSTATE和BM_SETSTATE消息表示按钮处于正常状态还是按下状态。BM_SETSTYLE消息允许在按钮建立之后改变按钮样式。(发送消息使用SendMessage消息)比如我们在来自控件的WM_COMMAND消息时,我们可以用如下的代码来反转复选框的状态。
SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM)!SendMessage((HWND)lParam, BM_GETCHECK, 0, 0), 0);
编辑类
同样,我们可以再CreateWindow中以edit作为窗口类来建立一个编辑控件,和静态子窗口控件一样,我们可以通过设置窗口样式ES_LEFT、ES_RIGHT和ES_CENTER来指定这些编辑控件的文字是左对齐、右对齐还是居中。
默认状态下,编辑控件是单行的,我们可以设置ES_MULTILINE来建立多行编辑控件。如果我们想要使用自动水平或者垂直滚动的编辑控件,可以采用样式ES_AUTOHSCROLL或ES_AUTOVSCROLL。
同样,编辑控件给父窗口消息处理程序发送WM_COMMAND消息,并且,各参数的含义和按钮控件的含义一样。其中,常见编辑控件的通知码如下:
EN_SETFOCUS:编辑控件已经获得输入焦点
EN_KILLFOCUS:编辑控件已经失去输入焦点
EN_CHANGE:编辑控件的内容将改变
EN_UPDATE:编辑控件的内容已经改变
EN_HSCROLL:编辑控件的水平滚动条已经被按下
EN_VSCROLL:编辑控件的垂直滚动条已经被按下
父窗口可以向编辑控件发送剪切、复制、或者清除目前被选择文字等消息。如:
SendMessage(hwndEdit, WM_CUT, 0, 0);
具体有哪些消息,可以查看MSDN。
列表框
下面介绍另外一个常用的控件,列表框控件,列表框(List Box)是一个允许用户从已有的项目中进行选择的控件。它可以是单选的也可是是多选的。
同样,如果我们需要建立列表框控件,仍然需要使用CreateWindow函数,并把“listbox”作为窗口类,但需要注意的是,默认的列表框是不向父窗口发送WM_COMMAND消息的,如果我们需要这样做,那么就必须让列表框控件的样式包含LBS_NOTIFY。同样,如果想要支持多选,那么需要使用样式LBS_MULTIPLESEL。其中我们最常用的样式的LBS_STANDARD,这是以下几种样式的组合。
LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER
我们可以通过SendMessage为列表框发送加入字符串消息来把一个字符串放入到列表框中,字符串通常是从0开始计数的,其中0对应于列表框中最顶上的项。如下所示:
SendMessage( hwndList, LB_ADDSTRING, 0, (LPARAM)szString );
也可是使用LBS_INSERTSTRING消息来插入字符串,但这时,我们需要传入一个iIndex,即插入的位置索引。
SendMessage( hwndList, LB_INSERTSTRING, iIndex, (LPARAM)szString );
同样,如果需要删除指定字符串,我们需要使用LB_DELETESTRING消息。
清除列表框内容使用LB_RESETCONTENT消息
还有一些常用的消息如下所示:
LB_GETCOUNT:获取列表框中有多少项
LB_SELECTSTRING:查找字符串
LB_GETCURSEL:获得当前选项的索引
LB_GETTEXTLEN:获得列表框中字符串的长度
LB_GETTEXT:把列表框中的选择字符串复制到一个文字缓冲区中,
当然还有很多其他的消息和控件,以及参数的含义在这里不再赘述,只是简单的介绍一个概念,具体使用查看MSDN。
总之,对于控件来说,
1、因为控件也是一种窗口,所以我们仍然使用CreateWindow函数来进行创建,只是使用的窗口类不需要我们自己去定义,而是使用系统预定义好的一些窗口类。
2、我们在CreateWindow中使用控件,一般需要把控件的类型设置为WS_CHILD | WS_VISIBLE,如果不设置WS_VISIBLE,那么我们需要使用ShowWindow函数来显示窗口。
3、控件一般给父窗口发送WM_COMMAND消息,其中wParam低字节表示控件ID,高字节表示通知码,父窗口通过通知码来获取控件的状态,从而做出相应的反应。lParam表示控件的句柄。
4、我们可以使用控件ID或者控件的句柄来识别是哪一个控件,但不同的控件ID可能是相同的,但句柄是唯一的。
5、父窗口可以通过SendMessage发送对应的控件消息来传递给控件。控件结束消息,使用预定义好的窗口类中的窗口处理函数来进行处理。
实例:
下面用Windows程序设计一书中的一个示例来简单的运用一下编辑控件,该示例是一个简单的文本编辑器,我们可以在这个基础上运用上面所讲的知识,来继续扩充功能,添加菜单等等。代码如下:
#include
#define ID_EDIT 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
TCHAR szAppName[] = TEXT ("PopPad1") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static HWND hwndEdit ;
switch (message)
{
case WM_CREATE :
hwndEdit = CreateWindow (TEXT ("edit"), NULL,
WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
WS_BORDER | ES_LEFT | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL,
0, 0, 0, 0, hwnd, (HMENU) ID_EDIT,
((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
return 0 ;
case WM_SETFOCUS :
SetFocus (hwndEdit) ;
return 0 ;
case WM_SIZE :
MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;
return 0 ;
case WM_COMMAND :
if (LOWORD (wParam) == ID_EDIT)
if (HIWORD (wParam) == EN_ERRSPACE ||
HIWORD (wParam) == EN_MAXTEXT)
MessageBox (hwnd, TEXT ("Edit control out of space."),
szAppName, MB_OK | MB_ICONSTOP) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}