我如何把我的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);

其中没有一个在做这项工作(Marshal.GetLastWin32Error()表示这些操作成功完成,并且每个定义的P/Invoke属性都具有SetLastError=true)。

如果我创建一个新的空白WPF应用程序,并使用计时器调用SwitchToThisWindow,它完全按照预期工作,所以我不确定为什么它在我原来的情况下不工作。

编辑:我这样做与一个全局热键。


当前回答

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

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. 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.

其他回答

让这个快速复制粘贴- 使用这个类的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

我想到了一个变通办法。我从一个用于实现热键的键盘钩子进行调用。如果我把它放入一个暂停的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();
}

为什么这一页上的一些答案是错误的!

Any answer that uses window.Focus() is wrong. Why? If a notification message pops up, window.Focus() will grab the focus away from whatever the user is typing at the time. This is insanely frustrating for end users, especially if the popups occur quite frequently. Any answer that uses window.Activate() is wrong. Why? It will make any parent windows visible as well. Any answer that omits window.ShowActivated = false is wrong. Why? It will grab the focus away from another window when the message pops up which is very annoying! Any answer that does not use Visibility.Visible to hide/show the window is wrong. Why? If we are using Citrix, if the window is not collapsed when it is closed, it will leave a weird black rectangular hold on the screen. Thus, we cannot use window.Show() and window.Hide().

从本质上讲:

当窗口激活时,它不应该从其他窗口抢夺焦点; 该窗口在显示时不应激活其父窗口; 该窗口应与Citrix兼容。

MVVM答案

此代码与Citrix 100%兼容(屏幕上没有空白区域)。它在普通WPF和DevExpress上都进行了测试。

这个答案适用于任何用例,其中我们想要一个小的通知窗口,它总是在其他窗口的前面(如果用户在首选项中选择了这个)。

如果这个答案看起来比其他答案更复杂,那是因为它是健壮的企业级代码。本页上的其他一些答案很简单,但实际上并不管用。

XAML -附加属性

将此附加属性添加到窗口中的任何UserControl中。所附财产将:

等待Loaded事件被触发(否则它无法查找可视树以找到父窗口)。 添加一个事件处理程序,以确保窗口是否可见。

在任何时候,您都可以通过翻转附加属性的值来设置窗口在前面或不在前面。

<UserControl x:Class="..."
         ...
         attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
             "{Binding EnsureWindowInForeground, Mode=OneWay}">

辅助方法

public static class HideAndShowWindowHelper
{
    /// <summary>
    ///     Intent: Ensure that small notification window is on top of other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoForeground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Visible)
            {
                window.Visibility = Visibility.Visible;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = true;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }

    /// <summary>
    ///     Intent: Ensure that small notification window can be hidden by other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoBackground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Collapsed)
            {
                window.Visibility = Visibility.Collapsed;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = false;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }
}

使用

为了使用这个,你需要在ViewModel中创建一个窗口:

private ToastView _toastViewWindow;
private void ShowWindow()
{
    if (_toastViewWindow == null)
    {
        _toastViewWindow = new ToastView();
        _dialogService.Show<ToastView>(this, this, _toastViewWindow, true);
    }
    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
    HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}

private void HideWindow()
{
    if (_toastViewWindow != null)
    {
        HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
    }
}

额外的链接

有关如何确保通知窗口总是移回可见屏幕的提示,请参阅我的回答:在WPF中,如果窗口离开屏幕,如何将窗口移到屏幕上?

我知道这个问题相当古老,但我刚刚遇到了这个精确的场景,并想分享我实现的解决方案。

正如本页的评论中提到的,提出的几个解决方案不能在XP上工作,而我需要在我的场景中支持XP。虽然我同意@Matthew Xavier的观点,他认为这是一种糟糕的用户体验实践,但有时这完全是一种合理的用户体验。

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

我已经清理并修改了一些代码,并将其实现为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
    }
}

我希望这段代码能帮助其他遇到这个问题的人。

myWindow.Activate();

试图将窗口带到前台并激活它。

这应该做的把戏,除非我误解了,你想要永远在顶端的行为。在这种情况下,你需要:

myWindow.TopMost = true;