C# 在 WPF 中将窗口置于最前面

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/257587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:14:37  来源:igfitidea点击:

Bring a window to the front in WPF

c#.netwpfwinapipinvoke

提问by Factor Mystic

How can I bring my WPF application to the front of the desktop? So far I've tried:

如何将我的 WPF 应用程序带到桌面的前面?到目前为止,我已经尝试过:

SwitchToThisWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle, true);

SetWindowPos(new WindowInteropHelper(Application.Current.MainWindow).Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

None of which are doing the job (Marshal.GetLastWin32Error()is saying these operations completed successfully, and the P/Invoke attributes for each definition do have SetLastError=true).

没有一个在做这项工作(Marshal.GetLastWin32Error()是说这些操作成功完成,每个定义的 P/Invoke 属性确实有SetLastError=true)。

If I create a new blank WPF application, and call SwitchToThisWindowwith a timer, it works exactly as expected, so I'm not sure why it's not working in my original case.

如果我创建一个新的空白 WPF 应用程序并SwitchToThisWindow使用计时器调用,它会完全按预期工作,所以我不确定为什么它在我的原始案例中不起作用。

Edit: I'm doing this in conjunction with a global hotkey.

编辑:我正在与全局热键一起执行此操作。

采纳答案by Factor Mystic

Well I figured out a work around. I'm making the call from a keyboard hook used to implement a hotkey. The call works as expected if I put it into a BackgroundWorker with a pause. It's a kludge, but I have no idea why it wasn't working originally.

好吧,我想出了一个解决方法。我正在从用于实现热键的键盘挂钩进行调用。如果我暂停将其放入 BackgroundWorker 中,该调用将按预期工作。这是一个混乱,但我不知道为什么它最初不起作用。

void hotkey_execute()
{
    IntPtr handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
    BackgroundWorker bg = new BackgroundWorker();
    bg.DoWork += new DoWorkEventHandler(delegate
        {
            Thread.Sleep(10);
            SwitchToThisWindow(handle, true);
        });
    bg.RunWorkerAsync();
}

回答by Morten Christiansen

myWindow.Activate();

Attempts to bring the window to the foreground and activates it.

尝试将窗口置于前台并激活它。

That should do the trick, unless I misunderstood and you want Always on Top behavior. In that case you want:

这应该可以解决问题,除非我误解了并且您希望始终处于最佳状态。在这种情况下,您想要:

myWindow.TopMost = true;

回答by Matthew Xavier

If the user is interacting with another application, it may not be possible to bring yours to the front. As a general rule, a process can only expect to set the foreground window if that process is already the foreground process. (Microsoft documents the restrictions in the SetForegroundWindow()MSDN entry.) This is because:

如果用户正在与另一个应用程序交互,则可能无法将您的应用程序放在最前面。作为一般规则,如果该进程已经是前台进程,则该进程只能期望设置前台窗口。(Microsoft 在SetForegroundWindow()MSDN 条目中记录了限制。)这是因为:

  1. The user "owns" the foreground. For example, it would be extremely annoying if another program stole the foreground while the user is typing, at the very least interrupting her workflow, and possibly causing unintended consequences as her keystrokes meant for one application are misinterpreted by the offender until she notices the change.
  2. Imagine that each of two programs checks to see if its window is the foreground and attempts to set it to the foreground if it is not. As soon as the second program is running, the computer is rendered useless as the foreground bounces between the two at every task switch.
  1. 用户“拥有”前景。例如,如果另一个程序在用户打字时窃取了前台,这将非常烦人,至少会中断她的工作流程,并可能导致意想不到的后果,因为她针对一个应用程序的击键被冒犯者误解,直到她注意到变化.
  2. 想象一下,两个程序中的每一个都检查其窗口是否为前景,如果不是,则尝试将其设置为前景。一旦第二个程序运行,计算机就会变得毫无用处,因为在每次任务切换时前景在两者之间跳来跳去。

回答by joshperry

The problem could be that the thread calling your code from the hook hasn't been initialized by the runtime so calling runtime methods don't work.

问题可能是从钩子调用代码的线程没有被运行时初始化,所以调用运行时方法不起作用。

Perhaps you could try doing an Invoke to marshal your code on to the UI thread to call your code that brings the window to the foreground.

也许您可以尝试执行 Invoke 将您的代码编组到 UI 线程,以调用将窗口置于前台的代码。

回答by Amir

In case you need the window to be in front the first time it loads then you should use the following:

如果您需要窗口在第一次加载时位于前面,那么您应该使用以下内容:

private void Window_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
}

private void Window_Initialized(object sender, EventArgs e)
{
    this.Topmost = true;
}

回答by Seth

I have had a similar problem with a WPF application that gets invoked from an Access application via the Shell object.

我在 WPF 应用程序中遇到了类似的问题,该应用程序通过 Shell 对象从 Access 应用程序调用。

My solution is below - works in XP and Win7 x64 with app compiled to x86 target.

我的解决方案如下 - 适用于 XP 和 Win7 x64,应用程序编译为 x86 目标。

I'd much rather do this than simulate an alt-tab.

我宁愿这样做而不是模拟 alt-tab。

void Window_Loaded(object sender, RoutedEventArgs e)
{
    // make sure the window is normal or maximised
    // this was the core of the problem for me;
    // even though the default was "Normal", starting it via shell minimised it
    this.WindowState = WindowState.Normal;

    // only required for some scenarios
    this.Activate();
}

回答by rahmud

To show ANY currently opened window import those DLL:

要显示任何当前打开的窗口,请导入这些 DLL:

public partial class Form1 : Form
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);
    [DllImportAttribute("User32.dll")]
    private static extern int SetForegroundWindow(int hWnd);

and in program We search for app with specified title (write title without first letter (index > 0))

并在程序中我们搜索具有指定标题的应用程序(写标题不带首字母(索引> 0))

  foreach (Process proc in Process.GetProcesses())
                {
                    tx = proc.MainWindowTitle.ToString();
                    if (tx.IndexOf("Title of Your app WITHOUT FIRST LETTER") > 0)
                    {
                        tx = proc.MainWindowTitle;
                        hWnd = proc.Handle.ToInt32(); break;
                    }
                }
                hWnd = FindWindow(null, tx);
                if (hWnd > 0)
                {
                    SetForegroundWindow(hWnd);
                }

回答by Jader Dias

I have found a solution that brings the window to the top, but it behaves as a normal window:

我找到了一个将窗口置于顶部的解决方案,但它的行为与普通窗口相同:

if (!Window.IsVisible)
{
    Window.Show();
}

if (Window.WindowState == WindowState.Minimized)
{
    Window.WindowState = WindowState.Normal;
}

Window.Activate();
Window.Topmost = true;  // important
Window.Topmost = false; // important
Window.Focus();         // important

回答by Hertzel Guinness

To make this a quick copy-paste one -
Use this class' DoOnProcessmethod to move process' main window to foreground (but not to steal focus from other windows)

为了使它成为一个快速复制粘贴 -
使用这个类的DoOnProcess方法将进程的主窗口移动到前台(但不要从其他窗口窃取焦点)

public class MoveToForeground
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);

    const int SWP_NOMOVE        = 0x0002;
    const int SWP_NOSIZE        = 0x0001;            
    const int SWP_SHOWWINDOW    = 0x0040;
    const int SWP_NOACTIVATE    = 0x0010;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

    public static void DoOnProcess(string processName)
    {
        var allProcs = Process.GetProcessesByName(processName);
        if (allProcs.Length > 0)
        {
            Process proc = allProcs[0];
            int hWnd = FindWindow(null, proc.MainWindowTitle.ToString());
            // Change behavior by settings the wFlags params. See http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx
            SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        }
    }
}

HTH

HTH

回答by Zodman

I know this question is rather old, but I've just come across this precise scenario and wanted to share the solution I've implemented.

我知道这个问题已经很老了,但我刚刚遇到了这个精确的场景,想分享我实施的解决方案。

As mentioned in comments on this page, several of the solutions proposed do not work on XP, which I need to support in my scenario. While I agree with the sentiment by @Matthew Xavier that generally this is a bad UX practice, there are times where it's entirely a plausable UX.

正如本页的评论中所提到的,提出的一些解决方案不适用于 XP,我需要在我的场景中支持这些解决方案。虽然我同意 @Matthew Xavier 的观点,即通常这是一种糟糕的 UX 实践,但有时它完全是一个合理的 UX。

The solution to bringing a WPF window to the top was actually provided to me by the same code I'm using to provide the global hotkey. A blog article by Joseph Cooneycontains a link to his code samplesthat contains the original code.

将 WPF 窗口置于顶部的解决方案实际上是由我用来提供全局热键的相同代码提供给我的。Joseph Cooney 的一篇博客文章包含指向他的代码示例链接,其中包含原始代码。

I've cleaned up and modified the code a little, and implemented it as an extension method to System.Windows.Window. I've tested this on XP 32 bit and Win7 64 bit, both of which work correctly.

我对代码进行了一些清理和修改,并将其实现为 System.Windows.Window 的扩展方法。我已经在 XP 32 位和 Win7 64 位上进行了测试,它们都可以正常工作。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace System.Windows
{
    public static class SystemWindows
    {
        #region Constants

        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        #endregion

        /// <summary>
        /// Activate a window from anywhere by attaching to the foreground window
        /// </summary>
        public static void GlobalActivate(this Window w)
        {
            //Get the process ID for this window's thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

            //Get the process ID for the foreground window's thread
            var currentForegroundWindow = GetForegroundWindow();
            var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            //Attach this window's thread to the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            //Set the window position
            SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

            //Detach this window's thread from the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            //Show and activate the window
            if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
            w.Show();
            w.Activate();
        }

        #region Imports

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        #endregion
    }
}

I hope this code helps others who encounter this problem.

我希望此代码可以帮助遇到此问题的其他人。