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
不会阻止它发生