Win32应用程序中,子控件的消息都是分发到其父窗口的消息处理函数中去了,这对于我们需要自定义子控件的某些特性时时十分不方便的,还好,Windows为我们提供了控件子类化的相关接口API。核心的思想是:通过获取子控件的消息处理函数地址,设置子控件的消息处理函数到自己定义的函数里,也就是Get/SetWindowLong API的使用。
这里是一个简单的测试程序,在控制台程序中创建一个对话框,然后对话框上有一个EDIT控件(资源编辑器里拖入的,不多说),子类化EDIT控件,右键弹出自己的菜单屏蔽系统默认的菜单。
#include <crtdbg.h> #include <Windows.h> #include <WindowsX.h> #include <CommCtrl.h> #include "resource.h" HWND g_hEditWnd; WNDPROC g_pOldProc; HINSTANCE g_hInst; INT_PTR CALLBACK DlgMessageProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK EditMessageProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int _tmain(int argc, _TCHAR* argv[]) { g_hInst = (HINSTANCE)::GetModuleHandle(NULL); ::LoadLibrary(L"riched20.dll"); DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgMessageProc); _CrtDumpMemoryLeaks(); //system("ipconfig"); system("pause"); return 0; } INT_PTR CALLBACK DlgMessageProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_INITDIALOG: { ::SetWindowText(hDlg, L"控制台测试程序:"); g_hEditWnd = ::GetDlgItem(hDlg, IDC_EDIT1); g_pOldProc = (WNDPROC)::GetWindowLong(g_hEditWnd, GWL_WNDPROC); ::SetWindowLong(g_hEditWnd, GWL_WNDPROC, (LONG)EditMessageProc); //::SetWindowSubclass(g_hEditWnd, ) return TRUE; } } return ::DefWindowProc(hDlg, uMsg, wParam, lParam); } LRESULT CALLBACK EditMessageProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_RBUTTONUP: { HMENU hMenu = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENU1)); HMENU hPopMenu = ::GetSubMenu(hMenu, 0); POINT pt; ::GetCursorPos(&pt); ::SetForegroundWindow(hWnd); DWORD dwBegin, dwEnd; ::SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwBegin, (LPARAM)&dwEnd); BOOL bCanUndo = ::SendMessage(hWnd, EM_CANUNDO, 0, 0); if ( dwEnd == dwBegin ) { ::ModifyMenu(hPopMenu, ID_EDITMENU_CUT, MF_BYCOMMAND|MF_GRAYED, ID_EDITMENU_CUT, L"剪切 Ctrl + X"); ::ModifyMenu(hPopMenu, ID_EDITMENU_COPY, MF_BYCOMMAND|MF_GRAYED, ID_EDITMENU_COPY, L"复制 Ctrl + C"); ::ModifyMenu(hPopMenu, ID_EDITMENU_DEL, MF_BYCOMMAND|MF_GRAYED, ID_EDITMENU_DEL, L"删除 Ctrl + D"); } if ( !bCanUndo ) { ::ModifyMenu(hPopMenu, ID_EDITMENU_UNDO, MF_BYCOMMAND|MF_GRAYED, ID_EDITMENU_UNDO, L"撤销 Ctrl + U"); } ::TrackPopupMenu(hPopMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL); ::DestroyMenu(hMenu); return TRUE; } case WM_COMMAND: { WORD wHigh = HIWORD(wParam); WORD wLow = LOWORD(wParam); if ( wHigh == 0 ) {//菜单消息 switch( wLow ) { case ID_EDITMENU_CUT: {//剪切 ::SendMessage(hWnd, WM_CUT, 0, 0); break; } case ID_EDITMENU_COPY: {//复制 ::SendMessage(hWnd, WM_COPY, 0, 0); break; } case ID_EDITMENU_PASTE: {//粘贴 ::SendMessage(hWnd, WM_PASTE, 0, 0); break; } case ID_EDITMENU_SELALL: {//全选 ::SendMessage(hWnd, EM_SETSEL, 0, -1); break; } case ID_EDITMENU_UNDO: {//撤销 ::SendMessage(hWnd, WM_UNDO, 0, 0); break; } case ID_EDITMENU_DEL: {//全选 ::SendMessage(hWnd, WM_CLEAR, 0, 0); break; } } } break; } default: break; } //把消息处理交还给默认消息处理函数 return g_pOldProc(hWnd, uMsg, wParam, lParam); }
菜单的点击消息都是用WM_COMMAND消息发送给菜单的父窗口的(在创建菜单时传入的那个窗口句柄),HIWORD(wParam)为0的时候就是菜单的消息了,详细的看MSDN就知道了。
另外,除了这些消息,其他的消息都要调用默认消息函数处理,不然的话EDIT控件是画不出来的。
关键地方:
<span style="white-space:pre"> </span>g_hEditWnd = ::GetDlgItem(hDlg, IDC_EDIT1); g_pOldProc = (WNDPROC)::GetWindowLong(g_hEditWnd, GWL_WNDPROC); ::SetWindowLong(g_hEditWnd, GWL_WNDPROC, (LONG)EditMessageProc);WM_INITDIALOG在对话框的初始化消息中获取EDIT的句柄,然后GetWindowLong保存原来的消息处理函数指针,SetWindowLong来设置消息到我们的处理函数中。
至于标准EDIT控件中的复制、粘贴、剪切……等处理在Windows中都已经提供了消息处理,我们只需要发送对应的消息到EDIT控件就好了,不用去计算文字选中等复杂过程。
原文:http://blog.csdn.net/mfcing/article/details/44706227