目录
控件
控件创建
控件的消息处理
总代码
本篇文章对控件的学习,只是对基础的部分,简单的使用,包括消息的处理上,并不涉及深入的内容。
控件
区分控件,资源:
SDK通常提供了一系列常用的用户界面控件,如按钮、文本框、列表框、组合框等。开发人员可以使用这些控件来构建应用程序的用户界面。SDK通常提供了各种图像、图标和光标资源,开发人员可以使用这些资源来美化应用程序的用户界面。
控件的使用很方便,直接拖过去即可
程序员只需要处理好这些控件的消息也就是(ID)即可
所有控件消息都是WM_COMMAND,各种不同消息的区分就要靠 wParam,lParam这两个参数的信息
一些常用控件的学习可以参考:SDK05- 控件_黑桃鱼的博客-CSDN博客
他人珠玉在前,不再献丑
控件创建
创建控件其实也就是创建一个窗口,指定一个父窗口即可,同时安排好控件的大小位置
按钮一
// 创建子窗口(控件)控件的操作都会产生Command消息
HWND hButton1 = CreateWindowEx(0,
_T("BUTTON"),
_T("是否打勾"),
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | LBS_NOTIFY, //组合属性,可拉伸窗口
0,
0,
100,
50,
hWnd,
(HMENU)IDB_BUTTON1,
g_hInstance,
NULL);
按钮二
HWND hButton2 = CreateWindowEx(0,
_T("BUTTON"),
_T("粘贴"),
WS_CHILD | WS_VISIBLE, // 还可以给按加别的样式BS_开头
100, //按钮可打勾
0,
100,
50,
hWnd,
(HMENU)IDB_BUTTON2,
g_hInstance,
NULL);
编辑框
HWND hEdit = CreateWindowEx(0,
_T("Edit"),
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, //组合属性,可拉伸窗口
0, // ws通用 es Edit控件单独有的
60,
200,
100,
hWnd,
(HMENU)IDE_Edit1,
g_hInstance,
NULL);
列表
HWND hListBox = CreateWindowEx(0,
_T("ListBox"),
_T("编程语言列表"),
WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_HASSTRINGS, //组合属性,可拉伸窗口
0, // ws通用 es Edit控件单独有的
200,
200,
100,
hWnd,
(HMENU)IDL_LISTBOX,
g_hInstance,
NULL);
控件的消息处理
控件的消息都会被转换成WM_COMMAND消息
代码如下:
lParam是控件的句柄,在逻辑中就是先判断是控件的消息,控件句柄是否为空,是控件的消息的话,再更具wParam的低字(控件标识符)来进一步区分是哪一个控件,wParam的高字则是消息,再去进一步处理
LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnCommand\n"));
WORD wID = LOWORD(wParam);
WORD wNotifyCode = HIWORD(wParam);
HWND hColtrol = (HWND)lParam;
if (hColtrol != NULL) { //控件
if (wID == IDB_BUTTON1) {
if (wNotifyCode == BN_CLICKED) {
LRESULT Result = SendMessage(hColtrol, BM_GETCHECK, 0, 0);
if (BST_CHECKED == Result) {
SendMessage(hColtrol, BM_SETCHECK, BST_UNCHECKED, NULL);
MessageBox(NULL, _T("未婚"), _T("婚姻状况"), MB_OK);
}
else if (BST_UNCHECKED == Result) {
SendMessage(hColtrol, BM_SETCHECK, BST_CHECKED, NULL);
MessageBox(NULL, _T("已婚"), _T("婚姻状况"), MB_OK);
}
}
}
else if (wID == IDB_BUTTON2) {
if (wNotifyCode == BN_CLICKED) {
HWND hEdit = GetDlgItem(hwnd, IDE_Edit1);
char szText[] = { "" };
SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)szText); // 直接调用窗口过程函数,知道过程函数返回,否则就会一直卡在这里
SendMessage(hEdit, WM_PASTE, 0, 0);
}
}
if (wID == IDL_LISTBOX && wNotifyCode == LBN_SELCHANGE) {
int nIndex = SendMessage(hColtrol, LB_GETCURSEL, 0, 0);
if (nIndex != -1) {
int nLen = SendMessage(hColtrol, LB_GETTEXTLEN, nIndex, 0);
LPVOID lpBuff = malloc(nLen+1);
SendMessage(hColtrol, LB_GETTEXT, nIndex, (LPARAM)lpBuff);
MessageBoxA(hwnd, (char*)lpBuff, "51asm", MB_OK);
free(lpBuff);
}
}
return TRUE;
}
switch (wID) {
case IDM_OPEN:
MessageBox(NULL, _T("打开"), _T("51asm"), MB_OK);
break;
case IDM_SAVE:
MessageBox(NULL, _T("保存"), _T("51asm"), MB_OK);
case IDM_EXIT:
SendMessage(hwnd, WM_USER + 1, NULL, NULL);// 向窗口发送一个消息
PostQuitMessage(0); // 给自己投递QUIT消息
// 操作窗口,想窗口发消息 WM_USER 0x0400开始
//SendMessage(hwnd,WM_USER+1,NULL,NULL);// 向窗口发送一个消息
break;
}
return TRUE;
}
总代码
#include <windows.h>
#include <tchar.h>
#include <fstream>
#include <string>
#ifdef _DEBUG
#define MYOUTPUT OutputDebugString
#else
#define MYOUTPUT
#endif // DEBUG
#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104
#define IDB_BUTTON1 105
#define IDB_BUTTON2 106
#define IDE_Edit1 107
#define IDL_LISTBOX 108
#define MY_MSG WM_USER+1 // 用户自定义消息
using namespace std;
string g_Text;
TEXTMETRIC g_tm; // 字体信息
HINSTANCE g_hInstance = NULL;
void ShowErrorMsg() {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
}
LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Create\n"));
// 创建一个定时器,有两种调用方式,100毫秒画一次
// SetTimer(hwnd, 1, 1000, nullptr);
// SetTimer(hwnd, 2, 1000, nullptr);
return TRUE;
}
// 当你关闭窗口点击确定后就会向窗口发送WM_Destroy消息,就意味要销毁窗口,
LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Close\n"));
return FALSE;
}
// 这个工作可以交给系统处理,也可以自己处理
LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Destory\n"));
PostQuitMessage(0);
// 销毁定时器,倘若有多个窗口,销毁一个窗口而不是全部
KillTimer(hwnd, 1);
return TRUE;
}
LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnCommand\n"));
WORD wID = LOWORD(wParam);
WORD wNotifyCode = HIWORD(wParam);
HWND hColtrol = (HWND)lParam;
if (hColtrol != NULL) { //控件
if (wID == IDB_BUTTON1) {
if (wNotifyCode == BN_CLICKED) {
LRESULT Result = SendMessage(hColtrol, BM_GETCHECK, 0, 0);
if (BST_CHECKED == Result) {
SendMessage(hColtrol, BM_SETCHECK, BST_UNCHECKED, NULL);
MessageBox(NULL, _T("未婚"), _T("婚姻状况"), MB_OK);
}
else if (BST_UNCHECKED == Result) {
SendMessage(hColtrol, BM_SETCHECK, BST_CHECKED, NULL);
MessageBox(NULL, _T("已婚"), _T("婚姻状况"), MB_OK);
}
}
}
else if (wID == IDB_BUTTON2) {
if (wNotifyCode == BN_CLICKED) {
HWND hEdit = GetDlgItem(hwnd, IDE_Edit1);
char szText[] = { "" };
SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)szText); // 直接调用窗口过程函数,知道过程函数返回,否则就会一直卡在这里
SendMessage(hEdit, WM_PASTE, 0, 0);
}
}
if (wID == IDL_LISTBOX && wNotifyCode == LBN_SELCHANGE) {
int nIndex = SendMessage(hColtrol, LB_GETCURSEL, 0, 0);
if (nIndex != -1) {
int nLen = SendMessage(hColtrol, LB_GETTEXTLEN, nIndex, 0);
LPVOID lpBuff = malloc(nLen+1);
SendMessage(hColtrol, LB_GETTEXT, nIndex, (LPARAM)lpBuff);
MessageBoxA(hwnd, (char*)lpBuff, "51asm", MB_OK);
free(lpBuff);
}
}
return TRUE;
}
switch (wID) {
case IDM_OPEN:
MessageBox(NULL, _T("打开"), _T("51asm"), MB_OK);
break;
case IDM_SAVE:
MessageBox(NULL, _T("保存"), _T("51asm"), MB_OK);
case IDM_EXIT:
SendMessage(hwnd, WM_USER + 1, NULL, NULL);// 向窗口发送一个消息
PostQuitMessage(0); // 给自己投递QUIT消息
// 操作窗口,想窗口发消息 WM_USER 0x0400开始
//SendMessage(hwnd,WM_USER+1,NULL,NULL);// 向窗口发送一个消息
break;
}
return TRUE;
}
BOOL InitControl(HWND hWnd) {
// 创建子窗口(控件)控件的操作都会产生Command消息
HWND hButton1 = CreateWindowEx(0,
_T("BUTTON"),
_T("是否打勾"),
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | LBS_NOTIFY, //组合属性,可拉伸窗口
0,
0,
100,
50,
hWnd,
(HMENU)IDB_BUTTON1,
g_hInstance,
NULL);
HWND hButton2 = CreateWindowEx(0,
_T("BUTTON"),
_T("粘贴"),
WS_CHILD | WS_VISIBLE, // 还可以给按加别的样式BS_开头
100, //按钮可打勾
0,
100,
50,
hWnd,
(HMENU)IDB_BUTTON2,
g_hInstance,
NULL);
HWND hEdit = CreateWindowEx(0,
_T("Edit"),
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, //组合属性,可拉伸窗口
0, // ws通用 es Edit控件单独有的
60,
200,
100,
hWnd,
(HMENU)IDE_Edit1,
g_hInstance,
NULL);
HWND hListBox = CreateWindowEx(0,
_T("ListBox"),
_T("编程语言列表"),
WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_HASSTRINGS, //组合属性,可拉伸窗口
0, // ws通用 es Edit控件单独有的
200,
200,
100,
hWnd,
(HMENU)IDL_LISTBOX,
g_hInstance,
NULL);
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"C语言");
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"C++");
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"C数据结构");
// 操作控件发消息
SendMessage(hButton1, BM_SETCHECK, BST_UNCHECKED, NULL);
return TRUE;
}
LRESULT OnKeyDown(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: OnKeyDown\n"));
return TRUE;
}
LRESULT OnKeyUp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: OnKeyUp\n"));
return TRUE;
}
// 定时器消息
LRESULT OnTimer(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (wParam == 1) {
MYOUTPUT(_T("[51asm]: OnTimer\n"));
HWND hDesktop = GetDesktopWindow();
HDC hdc = GetDC(hDesktop);
TextOut(hdc, 0, 0, _T("51asm"), 5);
ReleaseDC(hDesktop,hdc);
//CloseHandle(hDesktop); GetDesktopWinodw()函数的句柄不需要用户释放
MYOUTPUT(_T("[51asm]: OnTimer ID=1 \n"));
}
else if (wParam == 2){
MYOUTPUT(_T("[51asm]: OnTimer ID=2 \n"));
}
return TRUE;
}
LRESULT OnNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: OnNotify\n"));
NMHDR* pnmh = (LPNMHDR)lParam;
if (wParam == IDL_LISTBOX) {
int nIndex = SendMessage(pnmh->hwndFrom, LB_GETCURSEL, 0, 0);
if (nIndex != -1) {
int nLen = SendMessage(pnmh->hwndFrom, LB_GETTEXTLEN, nIndex, 0);
LPVOID lpBuff = malloc(nLen);
SendMessage(pnmh->hwndFrom, LB_GETTEXT, nIndex, (LPARAM)lpBuff);
MessageBoxA(hwnd, (char*)lpBuff, "51asm", MB_OK);
free(lpBuff);
}
}
return TRUE;
}
// 消息处理
// 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
// 先创建非客户区,再创建客户区,还有创建窗口等很多消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LRESULT lResult = FALSE;
switch (uMsg) {
case WM_NOTIFY:
lResult = OnNotify(hwnd, uMsg, wParam, lParam);
break;
case WM_CREATE:
lResult = OnCreate(hwnd, uMsg, wParam, lParam);
break;
case WM_CLOSE:
lResult = OnClose(hwnd, uMsg, wParam, lParam);
break;
case WM_DESTROY:
lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
break;
case WM_COMMAND:
lResult = OnCommand(hwnd, uMsg, wParam, lParam);
break;
case WM_KEYDOWN:
lResult = OnKeyDown(hwnd, uMsg, wParam, lParam);
break;
case WM_KEYUP:
lResult = OnKeyUp(hwnd, uMsg, wParam, lParam);
break;
case WM_TIMER:
lResult = OnTimer(hwnd, uMsg, wParam, lParam);
break;
case MY_MSG:
MessageBox(NULL, _T("我的消息"), NULL, NULL);
}
if (!lResult) {
return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
}
return lResult;
}
/*
客户区(用户区)与非客户区(系统的) NC
*/
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
) {
g_hInstance = hInstance;
// 申请堆控件,因为快捷键需要全局访问
// 申请100字节的局部堆地址空间
// 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
// 申请局部堆地址
HLOCAL hMenu = LocalAlloc(LHND, 100);
LPVOID lpMemory = LocalLock(hMenu);
LocalFree(hMenu);
// 申请全局堆地址
// 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
GlobalAlloc(LHND, 100);
// 申请堆空间
// 新的API,返回值是一个地址,前两个函数的底层是调用这个
//HeapAlloc();
// 申请堆地址空间,可以指定内存属性
//VirtualAlloc();
ACCEL* pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL) * 2);
if (pAccelNews == nullptr) {
ShowErrorMsg();
return 0;
}
// 快捷键,可以用下面的写法,把快捷键和菜单消息一起处理
// pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
// pAccelNews[0].key = 'A';
// pAccelNews[0].cmd = WM_COMMAND;
//
pAccelNews[1].fVirt = FCONTROL | FVIRTKEY;
pAccelNews[1].key = 'S';
pAccelNews[1].cmd = IDM_SAVE;
// 创建快捷键表
HACCEL hAccel = CreateAcceleratorTable(pAccelNews, 2);
if (hAccel == NULL) {
ShowErrorMsg();
return 0;
}
// 创建窗口实例
TCHAR szWndClassName[] = { _T("CR41WndClassName") };
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_HAND);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wc.lpszMenuName = NULL;
wc.lpszClassName = szWndClassName;
// 注册窗口
if (RegisterClassEx(&wc) == 0) {
ShowErrorMsg();
return 0;
}
// 创建窗口
TCHAR szWndName[] = { _T("51asm") };
HWND hWnd = CreateWindowEx(0,
szWndClassName,
szWndName,
WS_OVERLAPPEDWINDOW, //组合属性,可拉伸窗口
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
if (hWnd == NULL) {
ShowErrorMsg();
return 0;
}
// 菜单
HMENU HMenu = CreateMenu();
// 弹出菜单
BOOL ret;
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
SetMenu(hWnd, HMenu);
// 添加子菜单
HMENU hSubMenu = GetSubMenu(HMenu, 0);
ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
SetMenu(hWnd, HMenu);
RECT rc;
GetClientRect(hWnd, &rc);
// 创建子窗口(控件)
InitControl(hWnd);
// 显示,更新窗口
ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
//ShowWindow(hChild, SW_SHOWNORMAL); 非子窗口需要单独show
UpdateWindow(hWnd); // 产生WM_PAINT
SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
// 消息循环
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1) {
break;
}
else {
// 转换快捷键消息 WM_COMMAND
if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
TranslateMessage(&msg);// 转换键盘消息
DispatchMessage(&msg); // 派发消息
}
}
}
// 删除快捷键表
DestroyAcceleratorTable(hAccel);
HeapFree(GetProcessHeap(), 0, pAccelNews);
return msg.wParam;
}