欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF 让窗口激活作为前台最上层窗口的方法

程序员文章站 2022-06-30 17:36:23
在 WPF 中,如果想要使用代码控制,让某个窗口作为当前用户的输入的逻辑焦点的窗口,也就是在当前用户活动的窗口的最上层窗口,默认使用 Activate 方法,通过这个方法在大部分设备都可以做到激活窗口 ......

在 wpf 中,如果想要使用代码控制,让某个窗口作为当前用户的输入的逻辑焦点的窗口,也就是在当前用户活动的窗口的最上层窗口,默认使用 activate 方法,通过这个方法在大部分设备都可以做到激活窗口

但是在一些特殊的设备上,使用下面代码调起窗口只是在任务栏闪烁图标,而没有让窗口放在最上层

window.show();
window.activate();

在大部分设备上,通过 show 和 activate 组合可以让窗口作为当前用户活动的,即使窗口之前是最小化或隐藏,都可以通过 show 的方法显示

但是某些设备窗口被盖在其他的窗口的下面,此时的窗口的 window.isactive 还是 true 但是调用 activate 不会让窗口放在上层

我在网上看到好多小伙伴调用了 setforegroundwindow 方法,其实现在 wpf 是开源的,可以看到 window 的 activate 方法是这样写

        public bool activate()
        {
            // this call ends up throwing an exception if activate
            // is not allowed
            verifyapisupported();
            verifycontextandobjectstate();
            verifyhwndcreateshowstate();

            // adding check for iscompositiontargetinvalid
            if (issourcewindownull || iscompositiontargetinvalid)
            {
                return false;
            }

            return unsafenativemethods.setforegroundwindow(new handleref(null, criticalhandle));
        }

源代码请看 github

也就是调用 setforegroundwindow 和调用 activate 方法是差不多的,如果调用 activate 应该调用 setforegroundwindow 也差不多

通过大佬的 setforegroundwindow的正确用法 - 子坞 - 博客园 可以了解到,需要按照以下步骤

    1.得到窗口句柄findwindow 
    2.切换键盘输入焦点attachthreadinput 
    3.显示窗口showwindow(有些窗口被最小化/隐藏了) 
    4.更改窗口的zorder,setwindowpos使之最上,为了不影响后续窗口的zorder,改完之后,再还原 
    5.最后setforegroundwindow 

在 wpf 中对应的更改窗口的顺序使用的是 topmost 属性,同时设置顺序需要做一定小的更改

在 wpf 中通过 c# - bring a window to the front in wpf - stack overflow 可以了解到如何用 attachthreadinput 方法

整个代码请看下面,具体的 win32 方法我就没有写出来了,请小伙伴自己添加

        private static void setwindowtoforegroundwithattachthreadinput(window window)
        {
            var interophelper = new windowinterophelper(window);
            var thiswindowthreadid = win32.user32.getwindowthreadprocessid(interophelper.handle, intptr.zero);
            var currentforegroundwindow = win32.user32.getforegroundwindow();
            var currentforegroundwindowthreadid = win32.user32.getwindowthreadprocessid(currentforegroundwindow, intptr.zero);

            // [c# - bring a window to the front in wpf - stack overflow](https://*.com/questions/257587/bring-a-window-to-the-front-in-wpf )
            // [setforegroundwindow的正确用法 - 子坞 - 博客园](https://www.cnblogs.com/ziwuge/archive/2012/01/06/2315342.html )
            /*
                 1.得到窗口句柄findwindow 
                2.切换键盘输入焦点attachthreadinput 
                3.显示窗口showwindow(有些窗口被最小化/隐藏了) 
                4.更改窗口的zorder,setwindowpos使之最上,为了不影响后续窗口的zorder,改完之后,再还原 
                5.最后setforegroundwindow 
             */
            win32.user32.attachthreadinput(currentforegroundwindowthreadid, thiswindowthreadid, true);

            window.show();
            window.activate();
            // 去掉和其他线程的输入链接
            win32.user32.attachthreadinput(currentforegroundwindowthreadid, thiswindowthreadid, false);

            // 用于踢掉其他的在上层的窗口
            window.topmost = true;
            window.topmost = false;

我测试了几个原本没有让窗口放在上层的设备,使用上面的代码可以设置,但是我不了解设置上面代码可能的坑是什么

附带 walterlv 的测试工具,可以用来拿到当前的 getforegroundwindow 是哪个

walterlv 的工具

另外少君小伙伴写了一个有趣的库,里面封装了很多 win32 的方法,请看 kkwpsv lsjutil