我如何把我的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,它完全按照预期工作,所以我不确定为什么它在我原来的情况下不工作。

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


当前回答

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

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中,如果窗口离开屏幕,如何将窗口移到屏幕上?

其他回答

只是想给这个问题加上一个解。这个实现适用于我的场景,其中CaliBurn负责显示主窗口。

protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor<IMainWindowViewModel>();

    Application.MainWindow.Topmost = true;
    Application.MainWindow.Activate();
    Application.MainWindow.Activated += OnMainWindowActivated;
}

private static void OnMainWindowActivated(object sender, EventArgs e)
{
    var window = sender as Window;
    if (window != null)
    {
        window.Activated -= OnMainWindowActivated;
        window.Topmost = false;
        window.Focus();
    }
}

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

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

我已经找到了一个解决方案,将窗口带到顶部,但它表现为一个正常的窗口:

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

既然这是个热门话题…以下是对我有效的方法。如果我不这样做,我就会得到错误,因为如果你看不到窗口,Activate()就会出错。

Xaml:

<Window .... 
        Topmost="True" 
        .... 
        ContentRendered="mainWindow_ContentRendered"> .... </Window>

后台代码:

private void mainWindow_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
    this.Activate();
    _UsernameTextBox.Focus();
}

这是唯一能让窗口在顶部显示的方法。然后激活它,这样你就可以在框中输入,而不必用鼠标设置焦点。control.Focus()不会工作,除非窗口是Active();

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

正如本页的评论中提到的,提出的几个解决方案不能在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
    }
}

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