在WPF中将窗口移到最前面


214

如何将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使用一个计时器调用,它将完全按预期工作,因此我不确定为什么在我的原始情况下它不起作用。

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


您是否已验证MainWindow是您想要的窗口?从MSDN:MainWindow会自动设置为对要在AppDomain中实例化的第一个Window对象的引用。
Todd White

好主意,但这是应用程序中唯一的窗口。
因素神秘主义者

您能否再提供一些上下文代码?
托德·怀特

Answers:


314
myWindow.Activate();

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

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

myWindow.TopMost = true;

14
我只是在使用myWindow.Show(),有时它不在最上面。之后,我立即拨打了myWindow.Activate()的电话,它起作用了。
伯莫

4
激活有时在Windows XP上不起作用。我推荐@Matthew Xavier的答案。
Lex Li

有点奇怪,因为默认情况下ShowActivated是打开的。
greenoldman 2011年

1
第一个答案是好的,谢谢!但是,使用该Topmost属性的第二行代码是一个不好的做法,因为它会使其他弹出对话框模糊,并具有意外的行为。
乔纳森·佩里

2
实际上,可以这样做:if (myWindow.WindowState == WindowState.Minimized) myWindow.WindowState = WindowState.Normal;奇怪的是,它还将保留所有最大化的窗口,并且不会将其还原为“正常”状态。
r41n

168

我找到了一种将窗口移至顶部的解决方案,但其行为与普通窗口相同:

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

1
很好的提示!如果该窗口已经打开,但在其他窗口之下,TopMost会使魔术在Windows 7上发生。
gsb 2012年

这也对我有用。感谢gsb关于TopMost的奇怪用法的附加评论!
2013年

1
谢谢-修复很简短。
code4life 2014年

2
在我的情况下,Window.Activate()和Window.Focus()就足够了。不需要设置Window.TopMost。

6
不要使用Window.Focus()。这将使焦点从用户当前在文本框中键入的内容移开,这对于最终用户而言是非常沮丧的。上面的代码没有它就可以正常工作。
康坦戈

32

如果您需要在第一次加载窗口时将其放在最前面,则应使用以下方法:

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

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

1
如果您在C#中开发类似于Launchy(launchy.net)的内容,则应注意此答案几乎没有用。
Lex Li

21

要使它成为快速的复制粘贴-
使用此类的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);
        }
    }
}

高温超导


6
+1这是唯一对我有用的答案。我有一个具有一个主窗口和几个浮动从属窗口的应用程序。激活其中任何一个后,所有其他窗口也都应放在最前面。但是大多数答案都没有激活/获得焦点:这是一场灾难,因为它使当前单击的窗口不可单击,因为突然另一个窗口获得了焦点。
stijn 2011年

任何不使用的理由process.MainWindowHandle
Sriram Sakthivel 2014年

就我而言,我不需要主窗口,但我同意,还有其他方法可以获取hWnd。FWIW一个HwndSource对象运行良好。
tobriand 2015年

21

我知道这个问题已经很老了,但是我只是遇到了这种精确的情况,想分享我已经实现的解决方案。

如本页注释中所述,一些建议的解决方案在XP上不起作用,我需要在我的方案中予以支持。尽管我同意@Matthew Xavier的观点,即通常这是一种不良的UX做法,但有时它完全是一种合理的UX。

实际上,将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
    }
}

我希望这段代码对遇到此问题的其他人有所帮助。


嘿,表情包!我已经为此奋斗了几个月!这对我的情况都适用。太棒了!(Windows 7 x64)
mdiehl13

实际上,只有在我这样做时,它才似乎起作用:App.mainWindow.Show(); SystemWindows.GlobalActivate(App.mainwindow); //当我删除第一个.show()时,它没有显示在前面
mdiehl13

+1为SetWindowPos(),我一直在寻找一种只将Window放在前面而不中断其他应用程序或不占用焦点的方法。this.Activate()失去了焦点。
prettyvoid 2015年

这为我做到了,我的案例的重点是窃取焦点,就像用户与某些元素进行交互时发生的那样。所以非常感谢,这似乎是一致的!只是打个电话this.Activate()似乎有些时间。
彼得

13

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

  1. 用户“拥有”前台。例如,如果用户在键入时另一个程序偷走了前台,至少会打断她的工作流程,并可能导致意外的后果,这是非常烦人的,因为她对一个应用程序的击键会被犯罪者误解,直到她注意到更改为止。
  2. 想象一下,两个程序中的每一个都会检查其窗口是否为前景,如果不是,则尝试将其设置为前景。第二个程序运行时,由于前景在每个任务切换之间在两者之间反弹,因此计算机变得无用。

好点子。但是,代码的目的是与全局热键结合使用的,而其他应用程序则以某种方式实现了此目的。
神秘主义者

不得不使用的PInvoke在C#中效仿本文中描述codeproject.com/Tips/76427/...
莱克斯李

那么为什么有时切换到Visual Studio时,表达式混合错误弹出对话框仍然可见?:-/
Simon_Weaver

Simon,我怀疑您看到的错误弹出窗口是“最顶部”的窗口(我不赞成这样做的设计决定)。前台窗口(接收用户输入)和Z顺序的“最高”窗口之间有区别。任何窗口都可以使自己处于“最顶层”,将其置于所有非最顶层窗口的顶部,但不会使窗口键盘聚焦,等等。成为前景窗口的方式。
马修·泽维尔

这个技巧在一些特殊的窗口中失败了。Visual Studio和命令提示符窗口必须具有阻止其他窗口成为前台窗口的内容。
Lex Li

9

我知道这是迟来的答案,可能对研究人员有帮助

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.Activate();
 }

9

为什么此页面上的某些答案是错误的!

  • 任何使用的答案window.Focus()都是错误的。

    • 为什么?如果弹出通知消息,则window.Focus()将使焦点从当前用户键入的内容移开。对于最终用户而言,这真是令人沮丧,尤其是当弹出窗口非常频繁地出现时。
  • 任何使用的答案window.Activate()都是错误的。

    • 为什么?它将使所有父窗口也可见。
  • 任何遗漏的答案 window.ShowActivated = false都是错误的。
    • 为什么?当消息弹出时,它将焦点从另一个窗口移开,这很烦人!
  • 任何不使用的答案 Visibility.Visible隐藏/显示窗口的都是错误的。
    • 为什么?如果我们使用的是Citrix,则在关闭窗口时如果窗口没有折叠,它将在屏幕上留下一个奇怪的黑色矩形框。因此,我们不能使用window.Show()window.Hide()

本质上:

  • 窗口在激活时不应将焦点从任何其他窗口移开。
  • 显示窗口时,不应激活其父窗口。
  • 该窗口应与Citrix兼容。

MVVM解决方案

此代码与Citrix 100%兼容(屏幕无空白区域)。它已通过常规WPF和DevExpress进行了测试。

该答案适用于我们想要一个始终位于其他窗口前面的小型通知窗口的任何用例(如果用户在首选项中选择了此窗口)。

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

XAML-附加属性

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

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

通过翻转附加属性的值,您可以随时将窗口设置为在最前面或不在最前面。

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

C#-辅助方法

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


5
“企业级代码”以及随后的几行内容catch (Exception) { }。是的...它使用的代码甚至没有在答案中显示,例如_dialogServiceShiftWindowOntoScreenHelper。加上要求在viewmodel端创建窗口(这基本上破坏了整个MVVM模式)...
Kryptos '18

@Kryptos这是企业级代码。我是从内存中键入的,而这种精确的技术已在大型FTSE100公司中使用。与我们都追求的完美设计模式相比,现实生活的原始程度有所降低。
Contango '18

我不喜欢我们自己在视图模型中保留窗口实例的事实,正如Kryptos提到的那样,这打破了mvvm的全部观点,也许可以用代码隐藏来代替吗?
伊戈尔·梅萨罗斯

1
@Igor Meszaros同意。现在,我有了更多的经验,如果我不得不再次做一次,我将添加一个Behavior,并使用Func<>绑定到ViewModel的对其进行控制。
康坦戈

7

WPF应用程序有一个类似的问题,该问题是通过Shell对象从Access应用程序调用的。

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

我宁愿这样做,也不愿模拟alt选项卡。

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();
}

4

好吧,由于这是一个热门话题……这对我来说很有效。如果我不这样做,我会收到错误消息,因为如果看不到窗口,那么Activate()会出错。

Xaml:

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

代码背后:

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

这是我使窗口显示在顶部的唯一方法。然后将其激活,以便您可以在框中键入内容而不必使用鼠标来设置焦点。除非窗口为Active(),否则control.Focus()将无法工作。


2

好吧,我想出了一个解决方案。我正在从用于实现热键的键盘挂钩进行呼叫。如果我将其暂停,将其按预期方式工作。这是一个麻烦,但我不知道为什么它最初不起作用。

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();
}

只是感兴趣:您是否尝试过Window.Activate(由Morten建议)和其他建议?他们似乎没有这个公认的笨拙那样胆怯。
Simon D.

这已经有一段时间了,但是是的,在我确实尝试过的时候
Factor Mystic

这在我的Windows XP上不起作用。我推荐@Matthew Xavier的答案。
Lex Li

2

要显示任何当前打开的窗口,请导入这些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);

在程序中,我们搜索具有指定标题的应用程序(写标题时不带首字母(索引> 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);
                }

“您的应用标题,没有第一封信” Oof,hacky hacky hacky。为什么不IndexOf正确使用呢?
Lightness比赛于

1

问题可能是运行时尚未初始化从挂钩中调用代码的线程,因此调用运行时方法无效。

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


1

这些代码将一直正常工作。

首先,在XAML中设置激活的事件处理程序:

Activated="Window_Activated"

将以下行添加到您的Main Window构造器块中:

public MainWindow()
{
    InitializeComponent();
    this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
}

在激活的事件处理程序中,复制以下代码:

private void Window_Activated(object sender, EventArgs e)
{
    if (Application.Current.Windows.Count > 1)
    {
        foreach (Window win in Application.Current.Windows)
            try
            {
                if (!win.Equals(this))
                {
                    if (!win.IsVisible)
                    {
                        win.ShowDialog();
                    }

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

                    win.Activate();
                    win.Topmost = true;
                    win.Topmost = false;
                    win.Focus();
                }
            }
            catch { }
    }
    else
        this.Focus();
}

这些步骤将正常工作,并将所有其他窗口置于其父窗口的前面。


0

如果您想隐藏窗口,例如最小化窗口,我发现使用

    this.Hide();

将其正确隐藏,然后只需使用

    this.Show();

然后将再次将该窗口显示为最顶部的项目。


0

只是想为这个问题添加另一个解决方案。此实现适用于我的场景,其中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();
    }
}

0

请记住不要将显示该窗口的代码放在PreviewMouseDoubleClick处理程序中,因为活动窗口将切换回处理事件的窗口。只需将其放入MouseDoubleClick事件处理程序中,或通过将e.Handled设置为True来停止冒泡。

在我的情况下,我正在处理Listview上的PreviewMouseDoubleClick,并且未设置e.Handled = true,所以它引发了MouseDoubleClick事件,将焦点放回了原始窗口。


-1

我构建了一个扩展方法,以方便重用。

using System.Windows.Forms;
    namespace YourNamespace{
        public static class WindowsFormExtensions {
            public static void PutOnTop(this Form form) {
                form.Show();
                form.Activate();
            }// END PutOnTop()       
        }// END class
    }// END namespace

调用表单构造器

namespace YourNamespace{
       public partial class FormName : Form {
       public FormName(){
            this.PutOnTop();
            InitalizeComponents();
        }// END Constructor
    } // END Form            
}// END namespace

嗨,迈克。您很晚才回答这个问题。您能否在答案中解释为什么这种方法与已经针对该问题发布的非常好的答案不同(也许更好)?
Noel Widmer

直到我刚需要这样做时,我才碰到这个问题,并想与他人分享我如何解决该问题,以防他人想使用它。
迈克

当然,我被选中查看您的帖子,并想让您知道。如果您认为这是对社区的有益贡献,那么总是可以提供新的答案。
Noel Widmer

2
这个问题专门关于WPF,但是您的解决方案是针对WinForms的。
布赖恩·赖希勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.