目录
控件
控件创建
控件的消息处理
总代码
本篇文章对控件的学习,只是对基础的部分,简单的使用,包括消息的处理上,并不涉及深入的内容。
控件
区分控件,资源:
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); 非子窗口需要单独showUpdateWindow(hWnd); // 产生WM_PAINTSetClassLong(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_COMMANDif (!TranslateAccelerator(hWnd, hAccel, &msg)) {TranslateMessage(&msg);// 转换键盘消息DispatchMessage(&msg); // 派发消息}}}// 删除快捷键表DestroyAcceleratorTable(hAccel);HeapFree(GetProcessHeap(), 0, pAccelNews);return msg.wParam;
}