如何使用Win32 API在自定义弹出窗口中放置EDIT控件



我正试图将一个EDIT控件添加到一个窗口中,该窗口用作类似自定义组合框控件的下拉列表。最初,这个下拉窗口被实现为桌面的子窗口(WS_CHILD),类似于真正的组合框使用的"ComboLbox"窗口。这运行得很好,但是当EDIT窗口被放入这样的下拉窗口时,它似乎只是拒绝接受焦点。例如,它被启用并对鼠标右键单击做出反应,但单击它或调用SetFocus()失败(后者将最后一个错误设置为ERROR_INVALID_PARAMETER)。

正因为如此,也因为在包括Raymond Chen的fakemenu示例在内的许多示例中实现自定义弹出窗口的方式,我将下拉实现更改为使用WS_POPUP,并将主应用程序窗口作为所有者。当弹出窗口显示时,这有一个已知的从所有者窗口窃取激活的问题,但这可以通过从弹出窗口的WM_MOUSEACTIVATE处理程序返回MA_NOACTIVATE来解决,并且它最初确实运行良好,即当弹出窗口出现时,所有者窗口保持激活。但是,只要我单击弹出窗口中的EDIT控件,它就会从默认窗口进程调用SetFocus(),将焦点设置为自己,从而停用父窗口。

我的问题是如何防止这种情况发生?我知道这是可以做到的,因为WinForms ToolStripManager能够在不停用父窗口的情况下允许编辑下拉列表中的文本,并且它还使用WS_POPUP样式作为弹出窗口。但是它是怎么做到的呢?

注释中建议了一个解决方案"通过处理WM_NCACTIVATE防止主机窗口明显显示为不活动">这应该如下面的示例所示。

当菜单窗口打开时,主机窗口(HostProc)将收到WM_NCACTIVATE消息。主机将查找"menuclass",如果找到菜单类,则主机将返回DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam);,以防止主机窗口的标题栏变为非活动状态。

您还需要在伪菜单窗口中处理WM_NCACTIVATE。当菜单窗口失焦时,MenuProc接收到WM_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;
}