显示窗口而不激活 [英] Show Window without activating

查看:187
本文介绍了显示窗口而不激活的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨!



我现在几天都在努力展示一个简单的窗口而不会让我的主窗口失去焦点而变得不活跃。



基本上我试图模仿一个上下文菜单(由于各种原因我不能使用所有者绘制的菜单)但是当它被显示时它将继续并成为活动窗口。



到目前为止我尝试了什么:

- 使用WS_EX_NOACTIVATE样式创建窗口

- 使用SW_SHOWNA或SW_SHOWNOACTIVATE显示窗口

- 在'菜单'窗口中处理WM_ACTIVATE和WM_NCACTIVATE消息,以阻止它们向DefWindowProc方向移动并返回0

- 使用带有SWP_SHOWWINDOW的SetWindowPos显示'菜单'窗口SWP_NOACTIVATE

- 手动将主窗口设置为活动窗口(但它不是很优雅,但是涉及到一些闪烁)。

-在Google,MSDN和CP上无休止地搜索



在这里展示一些见解。



这就是我创建窗口的方式:

Hi!

I am struggling for a few days now to show a simple window without causing my main window to lose focus and become inactive.

Basically I am trying to emulate a context menu (I cannot use owner drawn menus for various reasons) but when being shown it will go ahead and become the active window.

What I have tried so far:
-Create window with WS_EX_NOACTIVATE style
-Showing the window with SW_SHOWNA or SW_SHOWNOACTIVATE
-Handle WM_ACTIVATE and WM_NCACTIVATE messages in the 'menu' window to stop them going towards DefWindowProc and returning 0
-Display the 'menu' window using SetWindowPos with SWP_SHOWWINDOW | SWP_NOACTIVATE
-Setting back manually the main window as the active window (but it's not very elegant there's some flicker involved).
-Searching endlessly on Google, MSDN and CP

To show an insight here's some code.

This is how I am creating the window:

HWND hMenuWnd = CreateWindowEx(WS_EX_NOACTIVATE, L"MyCustomMenu", L"", WS_POPUP | WS_BORDER, 0, 0, 1, 1, hMainWindow, 0, hInstance, 0);





然后在主窗口的窗口过程中,当处理WM_COMMAND以显示菜单时



Then in the window procedure for the main window, when handling WM_COMMAND to display the 'menu'

case IDC_MAINMENUBUTTON:
    ShowWindow(hMenuWnd, SW_SHOWNOACTIVATE);
    break;





执行上述声明后,您可以清楚地看到主窗口标题在非活动状态下重绘。除了我刚才提到的,两个windows程序都没有什么特别的。



任何建议或解决方案都会受到高度赞赏,不要让我失望CP !



谢谢,

Alex



After the statement above executes you can clearly see the main window caption is redrawn in the inactive state. Besides what I've mentioned there is absolutely nothing special going on in both windows procedures.

Any suggestion or solution will be highly appreciated, don't let me down CP!

Thanks,
Alex

推荐答案

问题是:如果你显示一个无法激活的窗口,是在另一个窗口后面;在你的情况下,它可能是你的主窗口。



解决方案是将无法激活与永远在线扩展风格相结合: WS_EX_TOPMOST | WS_EX_NOACTIVATE

http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543%28v=vs.85%29.aspx [ ^ ],

http://msdn.microsoft .com / zh-CN / library / windows / desktop / ms632680%28v = vs.85%29.aspx [ ^ ],

http://msdn.microsoft.com/en-us/library/windows/desktop /ms633591%28v=vs.85%29.aspx [ ^ ]。



-SA
The problem is: if you show a window which cannot be activated, is is goes behind another window; in your case, it could be your main window.

The solution is to combine "cannot-activate" with "always-on-top" extended style: WS_EX_TOPMOST | WS_EX_NOACTIVATE:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543%28v=vs.85%29.aspx[^],
http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680%28v=vs.85%29.aspx[^],
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591%28v=vs.85%29.aspx[^].

—SA


首先,我要感谢所有拥有的人对这个问题的想法做出了贡献,这里是我发现最符合我需求的解决方案。



对于直接跳到解决方案的人,我正在显示一个自定义上下文菜单,由弹出窗口托管。关键是让菜单窗口不干扰所有者窗口的激活和焦点(简而言之,使其表现得像上下文菜单)。



创建窗口像这样:

First I would like to thank everybody that has contributed with ideas to this question, here's the solution I found to fit best my needs.

For those jumping straight to the solution, I am displaying a custom context menu that is hosted by a popup window. The point is to have the menu window not interfere with activation and focus of the owner window (short, make it behave like a context menu).

The window is created like so:
HWND hMenuWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOACTIVATE, "MyCustomMenu", NULL, WS_POPUP | WS_BORDER, 0, 0, 1, 1, hMainWindow, 0, hInstance, 0);





注意 WS_EX_NOACTIVATE 指示Windows跳过窗口激活的样式,通常在显示新的顶级窗口时发生。



稍后菜单窗口将被定位并由其所有者显示为单击按钮的结果。当调用 SetWindowPos 将菜单设置在正确的位置时,请确保添加 SWP_NOACTIVATE 标志,否则此函数将从窗口中删除 WS_EX_NOACTIVATE 。 br />


这是一个简单的用法示例:



Notice the WS_EX_NOACTIVATE style which instructs Windows to skip window activation that normally happens when a new top level window is shown.

Later on the menu window will be positioned and shown by it's owner as a result from clicking a button. When calling SetWindowPos to set the menu in the correct location make sure you are adding SWP_NOACTIVATE flag otherwise this function will remove WS_EX_NOACTIVATE from the window.

This is a simple usage example:

case IDC_MYMENUBUTTON:
   SetWindowPos(hMenuWnd, 0, x, y, wdith, height, SWP_NOZORDER | SWP_NOACTIVATE);
   ShowWindow(hMenuWnd, SW_SHOWNOACTIVATE);

   break;





再来一次,到保持菜单窗口不被激活你需要在菜单窗口过程中处理 WM_MOUSEACTIVATE 消息并返回 MA_NOACTIVATE



Again, to keep the menu window from being activated you need to handle WM_MOUSEACTIVATE message in the menu window procedure and return MA_NOACTIVATE.

case WM_MOUSEACTIVATE:
    return MA_NOACTIVATE;





现在窗口已准备好可见,您需要监控鼠标点击,特别是那些超出它的区域以使菜单隐藏的点击。这将通过设置一个本地鼠标钩子(只会观察当前线程消息队列)来实现,该钩子将捕获鼠标点击并决定是否该时间隐藏菜单。



此外,只有当菜单可见时才需要此挂钩处于活动状态(无需在不需要操作时过滤消息),因此您将处理 WM_WINDOWPOSCHANGED 消息,如下所示:





Now that the window is ready to become visible, you will need to monitor mouse clicks especially those that go outside it's area to cause the menu to hide. This will be achieved by setting a local mouse hook (will only watch the current thread message queue) that will catch mouse clicks and decide if it's time to hide the menu.

Also, you will want this hook to be active only when the menu is visible (no need to filter messages when no action is required) so for this you will handle WM_WINDOWPOSCHANGED message like so:

case WM_WINDOWPOSCHANGED:
    WINDOWPOS* wp = (WINDOWPOS*) lParam;
    
    // if the window is shown
    if (wp->flags & SWP_SHOWWINDOW)
    {
        // set our hook and store the handle in the global variable
        g_hMouseHook = SetWindowsHookEx(WH_MOUSE, MyMenuMouseHook, 0, GetCurrentThreadId());
        
        if (!g_hMouseHook)
        {
            // handle your error!
        }
     } else if (wp->flags & SWP_HIDEWINDOW) // the menu has been hidden
     {
        UnhookWindowsHookEx(g_hMouseHook); // unhook
     }

     // pass on the message to DefWindowProc
     return DefWindowProc(hWnd, Msg, wParam, lParam); 





现在钩子程序拦截所有鼠标按钮操作(向下,向上等)并决定何时应该隐藏菜单。





Now the hook procedure that intercepts all mouse button actions (down, up, etc) and decides when the menu should be hidden.

LRESULT CALLBACK MyMenuMouseHook(int Code, WPARAM wParam, LPARAM lParam)
{
    // messages are defined in a linear way the first being WM_LBUTTONUP up to WM_MBUTTONDBLCLK
    // this subset does not include WM_MOUSEMOVE, WM_MOUSEWHEEL and a few others
    if (wParam >= WM_LBUTTONUP && wParam <= WM_MBUTTONDBLCLK)
    {
        // it's a fair assumption to say that you should have only one menu displayed at a certain point in the owner window
        HWND hMenuWnd = FindWindow(L"MyCustomMenu", 0);


        if (hMenuWnd)
        {
            POINT pt; RECT rcWindow;
        
            GetCursorPos(&pt);
            GetWindowRect(hMenuWnd, &rcWindow);
            
            // if the mouse action is outside the menu, hide it. the window procedure will also unset this hook 
            if (!PtInRect(&rcWindow, pt))
                ShowWindow(hMenuWnd, SW_HIDE);
        }   
    }

    return CallNextHookEx(NULL, Code, wParam, lParam);
}





稍后编辑:钩子不会拦截鼠标动作,例如所有者窗口外的点击没有发布在钩子线程队列上。在所有者程序中处理 WM_KILLFOCUS 消息并检查菜单是否可见然后隐藏它。





Later There are mouse action that the hook will not intercept such as clicks outside the owner window as they are not posted on the hook thread queue. Handle WM_KILLFOCUS message in the owner procedure and check if the menu is visible then hide it.

case WM_KILLFOCUS:
    if (IsWindowVisible(hMenuWnd))
        ShowWindow(hMenuWnd, SW_HIDE);
    
    break;



你还需要自己隐藏菜单单击它的内部控件。



-Alex


You will also have to hide the menu yourself when one of it's internal controls are clicked.

-Alex


当我这样做时,我必须做三件事。



1.所有者窗口调用ShowWindow(SW_SHOWNA)来显示弹出窗口。

2.如果偶然弹出窗口确实收到WM_ACTIVATE ,它会立即激活它的所有者窗口。

3.从所有者窗口向弹出窗口转发按键事件。



我没有使用WS_EX_NOACTIVATE或WS_EX_TOPMOST。
When I've done this, I had to do three things.

1. Owner window calls ShowWindow(SW_SHOWNA) to display the popup window.
2. If by chance the popup did receive a WM_ACTIVATE, it would promptly activate it's owner window.
3. Forward keypress events from the owner window to the popup.

I did not use WS_EX_NOACTIVATE or WS_EX_TOPMOST.


这篇关于显示窗口而不激活的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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