子窗口
在上面的学习中,我们知道了窗口的创建,但在windows中,为了方便使用,他预先定义了一些常见的窗口,我们称这些窗口为子窗口或者子窗口控件或者直接叫控件
比如说按钮、复选框、文本框等等
子窗口控件:
- Windows提供了几个预定义的窗口类似方便我们使用,我们一般就他们叫做子窗口控件,简称控件
- 控件会自己处理消息,并在自己状态发生改变时通知父窗口
- 预定义的控件有:按钮、复选框、编辑框、静态字符串标签和滚动条等
通知父窗口比如说,我在子窗口画了个按钮,点击这个按钮他的状态就会发生变化,发生变化的时候他就会告诉父窗口,所以从消息处理上看,我们就能在父窗口过程里获取到子窗口发生的这种变化
但是直接用win32来画界面的很少,因为很难看且效率不高,所以就出现了MFC,但MFC也只是在win32的基础上封装出来的
本次就演示按钮与文本框的代码编写
还是上次的代码,运行出来会看到只有这么一个窗口,首先在左边大部分弄上文本框,然后右边弄上按钮
目的知道了,想想怎么实现,首先要在这个窗口上弄,那就要先知道这个窗口何时被建立了起来
文本框
在代码中,我们可以在winmain函数里刚创建主窗口时就可以创建子窗口,那我也可以在窗口回调函数(也叫窗口过程)里创建子窗口,在窗口过程里有各种各样的消息,也包括创建窗口,还记得WM_CREATE吗,这就是所以
HINSTANCE g_hInstance; //在全局
g_hInstance = hInstance;//在Winmain函数
case WM_CREATE:
{
//创建文本框
CreateWindow(
"EDIT",
"",
WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE,
0,
0,
500,
300,
hwnd,
NULL,
g_hInstance,
NULL
);
//创建2个按钮
CreateWindow(
"BUTTON",
"设置",
WS_CHILD|WS_VISIBLE,
520,
180,
60,
30,
hwnd,
NULL,
g_hInstance,
NULL
);
CreateWindow(
"BUTTON",
"获取",
WS_CHILD|WS_VISIBLE,
520,
220,
60,
30,
hwnd,
NULL,
g_hInstance,
NULL
);
}
首先看创建文本框
第一参数就是写windows预定义好的,有那些预定义好的,参考下面的图,也可以在文档里查看
第二个参数不需要标题,那就直接给个空串
第三个参数是指你希望文本框以什么形式显示,这里需要查下文档,就不贴图了代码中的属性依次是,子窗口(由于你是子窗口,这是必须要的)、创建后立马就能显示、带有滚动条、可换行(这是子窗口独有的特性在MSDN中可以看到
),中间用与符号连接
第四、五个参数是指,相对于父窗口的左上角的那个点在什么位置展现
第六、七参数是指子窗口的宽和高
第八个参数是父窗口的句柄
第九个参数是指子窗口的编号,目前用不上,写上NULL
第十个参数是指当前这个窗口属于那个进程,由于这个参数是在winmain函数里定义的,是个局部变量,所以在外面定义一个全局变量
所以按钮同理,就不多赘述了
然后写完后运行,成功,但是点击那些按钮,有点击效果,却没点击反馈,接下来就是写点击反馈的代码了
首先,记得这句话
- 控件会自己处理消息,并在自己状态发生改变时通知父窗口
所以当我们点击按钮就会发送消息,而这个消息是可以被捕获的
char OutBuff[0x80];
sprintf(OutBuff,"消息类型:%x\n",uMsg);
OutputDebugString(OutBuff);
我们不是写过捕获消息的代码吗,直接拿过来用,这里要注意,点击的时候别有其他动作,首先长按,然后松开
多试几次,就会知道111,就是这个按钮的类型,然后再去查文档
就找到这个类型了,说明了,只要按扭的状态发生变化,就会通过WM_COMMAND的方式向父窗口发送消息,所以我只要,捕获这个消息,在写上自己的代码不就行了吗
case WM_COMMAND:
{
MessageBox(0,0,0,0);
break;
}
只要点下按钮就会弹个窗口,运行后成功,但是,两个按钮都会弹,那我要区分这两个,怎么办呢?我们就会想到wParam, lParam这两个参数,这两个参数不就是详细记录了消息的细节吗,那我们就看看这个消息的细节
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_COMMAND
WPARAM wParam, // notification code and identifier
LPARAM lParam // handle to control (HWND)
);
The low-order word specifies the identifier of the menu item, control, or accelerator.
低阶字指定了菜单项、控件或加速键的标识符
可以看到第三个参数写的是身份标识,什么是身份标识,还记得子窗口编号吗,这个就是用来区分的,这个编号可以自己写
#define IDC_EDIT_1 0x100
#define IDC_BUTTON_1 0x101
#define IDC_BUTTON_2 0x102
然后别忘了添上编号,如果有问题,把编号的类型强转成(HMENU)类型,因为参数是这个类型的
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_1:
{
MessageBox(0,"1","按钮",0);
break;
}
case IDC_BUTTON_2:
{
MessageBox(0,"2","按钮",0);
break;
}
}
break;
}
switch(LOWORD(wParam))这里由于是低16位来指定标识符,所以只需要低16位的即可
运行,可以看到没有问题
然后接下来就是点击按钮可以在文本框内显示文字
这里就需要介绍新的函数了
SetDlgItemText
设置对话框中控件的标题或文本。
BOOL SetDlgItemText(
HWND hDlg, // handle to dialog box
int nIDDlgItem, // control identifier
LPCTSTR lpString // text to set
);
参数一次是,句柄、子窗口编号、要输入的文本,字符串
GetDlgItemText
检索与对话框中的控件关联的标题或文本。可以理解成获取
UINT GetDlgItemTextA(
[in] HWND hDlg,
[in] int nIDDlgItem,
[out] LPSTR lpString,
[in] int cchMax
);
返回值
类型: UINT
如果函数成功,则返回值将指定复制到缓冲区的字符数,不包括终止 null 字符。
如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
参数不多说,都能看懂
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_1:
{
SetDlgItemText(hwnd,IDC_EDIT_1,"按钮测试");
//MessageBox(0,"1","按钮",0);
break;
}
case IDC_BUTTON_2:
{
GetDlgItemText(hwnd,IDC_EDIT_1,OutBuff,100);
OutputDebugString(OutBuff);//在输出窗口显示
MessageBox(hwnd,OutBuff,OutBuff,MB_OK);
break;
}
}
break;
}
运行结果
成功
这里附上完整的代码
// 第一个窗口程序.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#define IDC_EDIT_1 0x100
#define IDC_BUTTON_1 0x101
#define IDC_BUTTON_2 0x102
HINSTANCE g_hInstance;
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
/*
char OutBuff[0x80];
DWORD dwAddr = (DWORD)hInstance;
sprintf(OutBuff,"模块地址:%x\n",dwAddr);
OutputDebugString(OutBuff);
*/
//第一步:定义那你的窗口是什么样的?
char OutBuff[0x80];
g_hInstance = hInstance;
TCHAR className[] = TEXT("My First Window");
WNDCLASS wndclass = {0};
wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wndclass.lpfnWndProc = WindowProc;
wndclass.lpszClassName = className;
wndclass.hInstance = hInstance;
RegisterClass(&wndclass);
//第二步:创建并显示窗口
HWND hwnd = CreateWindow(
className,
TEXT("我的第一个窗口"),
WS_OVERLAPPEDWINDOW,
10,
10,
600,
300,
NULL,
NULL,
hInstance,
NULL
);
if(hwnd == NULL)
{
sprintf(OutBuff,"Error:%d\n",GetLastError());
OutputDebugString(OutBuff);
return 0;
}
ShowWindow(hwnd,SW_SHOW);
//第三步:接收消息并处理
MSG msg;
BOOL bRet;
while((bRet = GetMessage(&msg,NULL,0,0)) !=0)
{
if(bRet == -1)
{
//handle the error and possibly exit
sprintf(OutBuff,"Error:%d\n",GetLastError());
OutputDebugString(OutBuff);
}
else
{
//转换消息
TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
char OutBuff[0x80];
//sprintf(OutBuff,"消息类型:%x\n",uMsg);
//OutputDebugString(OutBuff);
switch(uMsg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_1:
{
SetDlgItemText(hwnd,IDC_EDIT_1,"按钮测试");
//MessageBox(0,"1","按钮",0);
break;
}
case IDC_BUTTON_2:
{
GetDlgItemText(hwnd,IDC_EDIT_1,OutBuff,100);
OutputDebugString(OutBuff);
MessageBox(hwnd,OutBuff,OutBuff,MB_OK);
break;
}
}
break;
}
case WM_CREATE:
{
//创建文本框
CreateWindow(
"EDIT",
"",
WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE,
0,
0,
500,
300,
hwnd,
(HMENU)IDC_EDIT_1,
g_hInstance,
NULL
);
//创建2个按钮
CreateWindow(
"BUTTON",
"设置",
WS_CHILD|WS_VISIBLE,
520,
180,
60,
30,
hwnd,
(HMENU)IDC_BUTTON_1,
g_hInstance,
NULL
);
CreateWindow(
"BUTTON",
"获取",
WS_CHILD|WS_VISIBLE,
520,
220,
60,
30,
hwnd,
(HMENU)IDC_BUTTON_2,
g_hInstance,
NULL
);
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}