【问题标题】:How can I open and close a popup menu when I click on a window in win32 api?在win32 api中单击窗口时如何打开和关闭弹出菜单?
【发布时间】:2020-04-04 20:20:46
【问题描述】:

我用win32 api做了一个小窗口,当我点击窗口时想打开一个弹出菜单。当我再次点击它时,如果它仍然打开,我想关闭它。

我在 WM_LBUTTONUP 上打开菜单,因为我想在 WM_LBUTTONDOWN 上拖动窗口。

我通过WM_ENTERMENULOOPWM_EXITMENULOOP知道菜单何时出现或消失。

我知道如何以编程方式关闭菜单,但不幸的是我不知道如何确定菜单是在 WM_LBUTTONUP 上打开还是关闭。问题是菜单在 WM_LBUTTONDOWN 上自动关闭,这就是为什么我无法保存菜单的当前状态。

如果有人提示如何解决这个问题,那就太好了。

其他信息:

  • 整个窗口是一个无边界的客户区,上面绘有位图

    hWnd = CreateWindowExW(WS_EX_TOPMOST, L"MyWindow", 0, WS_POPUP, wndPosX, wndPosY, m_WndWidth, m_WndHeight, 0, 0, m_hInst, 0);
    
  • 位图在窗口的 WindowProcedure 中的 WM_CREATE 上加载,并在 WM_PAINT 上绘制

    m_hBitmap = (HBITMAP)LoadImageA(NULL, "MyBitmap.bmp", IMAGE_BITMAP, m_WndWidth, m_WndHeight, LR_LOADFROMFILE);
    
  • 窗口默认不可拖动,因为我没有非客户区,所以我根据当前光标位置在WM_MOUSEMOVE上手动移动窗口

  • WM_LBUTTONUP 我在窗口顶部创建弹出菜单

    HMENU hPopupMenu = CreatePopupMenu();
    InsertMenuW(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_ITEM_A, L"ItemA");
    SetForegroundWindow(hWnd);
    RECT wndRect;
    GetWindowRect(hWnd, &wndRect);
    TrackPopupMenu(hPopupMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, wndRect.left, wndRect.top, 0, hWnd, NULL);
    

【问题讨论】:

  • 如果你想改变菜单的工作方式,最好的办法是在显示之前安装一个WH_MSGFILTER 钩子。然后您可以监视和修改它收到的消息(例如,如果您希望它忽略鼠标单击,请将 WM_LBUTTONDOWN 更改为 WM_NULL)。
  • 拖动窗口会影响菜单吗?您可以按照乔纳森的方式避免鼠标点击(WM_LBUTTONDOWN)。一般WM_LBUTTONDOWNWM_LBUTTONUP出现在鼠标的动作中。
  • 感谢您的帮助。我对win api很陌生。如何安装挂钩?以及如何修改它收到的消息?
  • 也许有另一种方法可以做到这一点。我可以通过单击某处来防止弹出菜单自行关闭吗?然后我可以自己处理。
  • 小窗口属于主窗口?或者它是一个单独的窗口?也许您可以添加一些代码来说明您的问题

标签: c++ windows winapi popupmenu


【解决方案1】:

这是使用 WindowsHooks 处理鼠标事件的解决方案 您必须在窗口客户区域内捕获鼠标按下事件并跳过下一个鼠标按下事件

在全球范围内

static HHOOK hMouseHook = 0;
static HWND hMainWindow = 0;
static int nSkipClick = 0;

LRESULT CALLBACK MouseProc(_In_ int    nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
    if (nCode < 0)
        return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
    if (wParam == WM_LBUTTONDOWN) {
        MOUSEHOOKSTRUCT* p = (MOUSEHOOKSTRUCT*)lParam;
        if (WindowFromPoint(p->pt) == hMainWindow) {
            POINT pt = p->pt;
            ScreenToClient(hMainWindow, &pt);
            RECT rct;
            GetClientRect(hMainWindow, &rct);
            if (PtInRect(&rct, pt))nSkipClick = 1;
        }
    }
    return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}

在 WndProc 中

case WM_LBUTTONUP: 
    if (nSkipClick == 0) { // menu not shown before
        hMouseHook = SetWindowsHookEx(WH_MOUSE, &MouseProc, NULL, GetCurrentThreadId());
        hMainWindow = hWnd;
        HMENU hPopupMenu = CreatePopupMenu();
        InsertMenuW(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_ITEM_A, L"ItemA");
        SetForegroundWindow(hWnd);
        RECT wndRect;
        GetWindowRect(hWnd, &wndRect);
        TrackPopupMenu(hPopupMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, wndRect.left, wndRect.top, 0, hWnd, NULL);
        UnhookWindowsHookEx(hMouseHook);
        hMouseHook = 0;
    }
    else {
        nSkipClick = 0;
    }
break;

我希望这是你想做的。

【讨论】:

  • 感谢您的回答。这里的问题是条件 in_menu == 0 始终为真,因为在我单击窗口时收到 WM_LBUTTONDOWN 之前菜单已关闭。在这种情况下,它会一直打开。一般来说,当我点击某个地方时菜单会关闭,我点击我的窗口或其他地方都没关系。
  • 我检查了您的编辑。现在,如果我单击该窗口,该窗口将打开菜单,并在我再次单击该窗口时将其关闭。没关系。如果我单击窗口并打开菜单,然后单击其他位置,菜单将关闭,我无法通过单击窗口再次打开它。如果我再次单击,它将再次打开。如果通过单击窗口或其他方式关闭菜单,也许有一种方法可以注册。我真的被这个问题困住了,但我认为有一个解决方案。我想避免安装挂钩或类似的东西。
  • @MarBot 可以分享不使用 hooks 的原因吗?
  • 我对使用钩子不太熟悉,但我认为没有其他可能性。我需要为此包含 DLL 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
  • 2023-02-09
  • 2016-01-26
  • 2019-04-04
  • 2015-08-02
  • 1970-01-01
  • 2012-12-21
相关资源
最近更新 更多