如何使用 Win32 API 在自定义弹出窗口中放置 EDIT 控件? [英] How to put an EDIT control in a custom popup window using Win32 API?

查看:39
本文介绍了如何使用 Win32 API 在自定义弹出窗口中放置 EDIT 控件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to add an EDIT control to a window used as a dropdown for a custom combobox-like control. Initially this dropdown window was implemented as a child (WS_CHILD) window of the desktop, which is similar to the "ComboLbox" window used by the real combobox. This worked just fine, however an EDIT window seems to just refuse to accept focus when it is put into such dropdown window. I.e. it is enabled and reacts to right mouse clicks, for example, but clicking on it or calling SetFocus() fails (the latter sets last error to ERROR_INVALID_PARAMETER).

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

Because of this, and also because of the way custom popup windows are implemented in many examples including Raymond Chen's fakemenu sample, I've changed the dropdown implementation to use WS_POPUP, with the main application window as owner. This has a known problem with stealing activation from the owner window when the popup is shown, however this can be addressed by returning MA_NOACTIVATE from WM_MOUSEACTIVATE handler for the popup window and it indeed works well initially, i.e. the owner window keeps activation when the popup shows up. But as soon as I click the EDIT control inside the popup, it calls, from its default window proc, SetFocus() to set the focus to itself, which deactivates the parent window.

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

My question is how can I prevent this from happening? I know that it can be done because WinForms ToolStripManager manages to allow editing text in a dropdown without deactivating the parent window and it also uses WS_POPUP style for the popup window. But how does it do it?

推荐答案

在评论中提出了一个解决方案通过处理WM_NCACTIVATE来防止主机窗口明显地显示为非活动状态"这应该如下例所示.

A solution was suggested in comments "prevent the host window from visibly appearing inactive by handling WM_NCACTIVATE" This should work as shown in the example below.

当菜单窗口打开时,宿主窗口(HostProc)会收到WM_NCACTIVATE消息.主机会寻找"menuclass",如果找到菜单类,主机将返回DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam); 以防止主机窗口的标题栏被涂成不活跃.

When menu window is opened, the host window (HostProc) will receive WM_NCACTIVATE message. Host will look for "menuclass", if menu class is found then host will return DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam); to prevent the title bar for host window get painted inactive.

您还需要在假菜单窗口中处理WM_NCACTIVATE.当菜单窗口失去焦点时,MenuProc接收到WM_NCACTIVATE,此时菜单可以自行关闭.

You also need to handle WM_NCACTIVATE in fake menu window. When menu window goes out of focus, WM_NCACTIVATE is received by MenuProc, at this point the menu can close itself.

#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;
}

这篇关于如何使用 Win32 API 在自定义弹出窗口中放置 EDIT 控件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆