标签:
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