Winapi 如何使用Win32 API在自定义弹出窗口中放置编辑控件?
我正在尝试向窗口添加一个编辑控件,该窗口用作自定义组合框类控件的下拉列表。最初,这个下拉窗口是作为桌面的一个子(Winapi 如何使用Win32 API在自定义弹出窗口中放置编辑控件?,winapi,Winapi,我正在尝试向窗口添加一个编辑控件,该窗口用作自定义组合框类控件的下拉列表。最初,这个下拉窗口是作为桌面的一个子(WS\u child)窗口实现的,它类似于真实combobox使用的“ComboLbox”窗口。这工作得很好,但是当编辑窗口放入这样的下拉窗口时,它似乎只是拒绝接受焦点。例如,它被启用并对鼠标右键单击作出反应,但单击它或调用SetFocus()失败(后者将最后一个错误设置为error\u INVALID\u参数) 正因为如此,也因为许多示例中自定义弹出窗口的实现方式,包括Raymond
WS\u child
)窗口实现的,它类似于真实combobox使用的“ComboLbox”窗口。这工作得很好,但是当编辑窗口放入这样的下拉窗口时,它似乎只是拒绝接受焦点。例如,它被启用并对鼠标右键单击作出反应,但单击它或调用SetFocus()
失败(后者将最后一个错误设置为error\u INVALID\u参数
)
正因为如此,也因为许多示例中自定义弹出窗口的实现方式,包括Raymond Chen的,我将下拉实现更改为使用WS_popup
,主应用程序窗口为所有者。当弹出窗口显示时,从所有者窗口窃取激活是一个已知的问题,但是这可以通过从弹出窗口的WM_MOUSEACTIVATE
处理程序返回MA_NOACTIVATE
来解决,并且它最初确实运行良好,即当弹出窗口显示时,所有者窗口保持激活。但是,只要我单击弹出窗口中的编辑控件,它就会从其默认窗口进程调用SetFocus()
,将焦点设置为自身,从而禁用父窗口
我的问题是如何防止这种情况发生?我知道这是可以做到的,因为WinForms能够在不停用父窗口的情况下允许在下拉列表中编辑文本,并且它还使用了弹出窗口的
WS\u POPUP
样式。但是它是如何做到的呢?注释中建议了一种解决方案“通过处理WM\u NCACTIVATE
,防止主机窗口明显显示为非活动状态”,这应该如下面的示例所示
当菜单窗口打开时,主机窗口(HostProc
)将收到WM\u NCACTIVATE
消息。主机将查找“menuclass”
,如果找到菜单类,则主机将返回deffwindowproc(hwnd,WM\u NCACTIVATE,TRUE,lparam)代码>以防止主机窗口的标题栏被绘制为非活动状态
您还需要在假菜单窗口中处理WM\u NCACTIVATE
。当菜单窗口失去焦点时,MenuProc
接收到WM\u NCACTIVATE
,此时菜单可以自动关闭
#include <windows.h>
const wchar_t* menuclass = L"menuclass";
LRESULT CALLBACK MenuProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 160, 30,
hwnd, NULL, NULL, NULL);
break;
case WM_NCACTIVATE:
{
if(!wparam)
{
//close the menu if its losing focus
PostMessage(hwnd, WM_CLOSE, 0, 0);
//tell parent to paint inactive, if user clicked on a different program
POINT pt;
GetCursorPos(&pt);
HWND hit = WindowFromPoint(pt);
HWND hparent = GetParent(hwnd);
if(hit != hparent && !IsChild(hparent, hit))
DefWindowProc(hparent, WM_NCACTIVATE, FALSE, 0);
}
break;
}
case WM_LBUTTONDOWN:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
//also handles other mouse/key messages associated with a menu...
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT CALLBACK HostProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_NCACTIVATE:
//paint the window as active when custom menu starts
if(!wparam && FindWindow(menuclass, NULL))
return DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam);
break;
case WM_RBUTTONUP:
{
//show the custom menu
POINT pt;
GetCursorPos(&pt);
CreateWindow(menuclass, NULL, WS_VISIBLE | WS_POPUP | WS_BORDER,
pt.x, pt.y, 200, 400, hwnd, 0, 0, 0);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpfnWndProc = HostProc;
wcex.lpszClassName = L"hostwnd";
RegisterClassEx(&wcex);
wcex.lpfnWndProc = MenuProc;
wcex.lpszClassName = menuclass;
RegisterClassEx(&wcex);
CreateWindow(L"hostwnd", L"Right click for menu ...",
WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
#包括
常量wchar_t*menuclass=L“menuclass”;
LRESULT回调菜单(HWND HWND,UINT msg,WPARAM WPARAM,LPARAM LPARAM)
{
开关(msg)
{
案例WM_创建:
CreateWindow(L“编辑”,空,WS|U子项| WS|U可见| WS|U边框,10,10,160,30,
hwnd,NULL,NULL,NULL);
打破
案例WM_n激活:
{
如果(!wparam)
{
//如果失去焦点,请关闭菜单
PostMessage(hwnd,WM_CLOSE,0,0);
//如果用户单击了其他程序,请告诉家长绘制非活动
点pt;
GetCursorPos(&pt);
HWND hit=窗口起始点(pt);
HWND hparent=GetParent(HWND);
if(hit!=hparent&!IsChild(hparent,hit))
DefWindowProc(hparent,WM_NCACTIVATE,FALSE,0);
}
打破
}
案例WM_LBUTTONDOWN:
PostMessage(hwnd,WM_CLOSE,0,0);
打破
//还处理与菜单关联的其他鼠标/按键消息。。。
}
返回DefWindowProc(hwnd、msg、wparam、lparam);
}
LRESULT回调HostProc(HWND HWND,UINT msg,WPARAM WPARAM,LPARAM LPARAM)
{
开关(msg)
{
案例WM_n激活:
//启动自定义菜单时,将窗口绘制为活动
if(!wparam&&FindWindow(菜单类,NULL))
返回DefWindowProc(hwnd、WM_NCACTIVATE、TRUE、lparam);
打破
案例WM_RBUTTONUP:
{
//显示自定义菜单
点pt;
GetCursorPos(&pt);
CreateWindow(菜单类,NULL,WS|U可见| WS|U弹出| WS|U边框,
第x部分、第y部分、200、400、hwnd、0、0、0);
返回0;
}
案例WM_销毁:
PostQuitMessage(0);
返回0;
}
返回DefWindowProc(hwnd、msg、wparam、lparam);
}
int WINAPI wWinMain(HINSTANCE HINSTANCE,HINSTANCE,LPTSTR,int)
{
WNDCLASSEX wcex={sizeof(WNDCLASSEX)};
wcex.hInstance=hInstance;
wcex.hCursor=LoadCursor(空,IDC_箭头);
wcex.lpfnWndProc=HostProc;
wcex.lpszClassName=L“hostwnd”;
注册类别(&wcex);
wcex.lpfnWndProc=MenuProc;
wcex.lpszClassName=menuclass;
注册类别(&wcex);
CreateWindow(L“hostwnd”,L“右键单击菜单…”,
WS|u可见| WS|u重叠窗口,0,0,600,400,0,0,hInstance,0);
味精;
while(GetMessage(&msg,NULL,0,0))
{
翻译信息(&msg);
发送消息(&msg);
}
返回(int)msg.wParam;
}
我相信,通过处理WM\u NCACTIVATE
,并在wParam==FALSE
时返回FALSE
,可以防止主机窗口显示为不活动状态。或者,您可以尝试使弹出窗口归父窗口所有。不幸的是,从WM\u NCACTIVATE
返回FALSE
会阻止SetFocus()
工作,显然,如果我这样做,将无法使弹出窗口内的编辑具有焦点。此外,弹出窗口已经属于父窗口,但不设置任何所有者实际上似乎不会改变任何东西。您是否也尝试过从编辑控件的WM\u MOUSEACTIVATE
处理程序返回MA\u NOACTIVATE
?@zett42否,谢谢您的想法。但不幸的是,在现在这样做之后,我发现它没有改变任何东西(我真的很惊讶,我认为这会阻止编辑获得焦点),即编辑仍然获得焦点,并且父/所有者窗口失去激活。返回MA_noactivateAT
不会阻止它发生