简体   繁体   English

在 WPF 中将窗口置于最前面

[英]Bring a window to the front in WPF

How can I bring my WPF application to the front of the desktop?如何将我的 WPF 应用程序带到桌面的前面? So far I've tried:到目前为止,我已经尝试过:

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 SwitchToThisWindow with 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.编辑:我正在与全局热键一起执行此操作。

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;

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

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

Or by overriding the methods:或者通过覆盖这些方法:

protected override void OnContentRendered(EventArgs e)
{
    base.OnContentRendered(e);
    Topmost = false;
}

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    Topmost = true;
}

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.正如本页的评论中提到的,提出的几个解决方案在 XP 上不起作用,我需要在我的场景中支持。 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.虽然我同意 @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.将 WPF 窗口置于顶部的解决方案实际上是由我用来提供全局热键的相同代码提供给我的。 A blog article by Joseph Cooney contains a link to his code samples that contains the original code. Joseph Cooney 的一篇博客文章包含指向他的代码示例链接,其中包含原始代码。

I've cleaned up and modified the code a little, and implemented it as an extension method to System.Windows.Window.我对代码进行了一些清理和修改,并将其实现为 System.Windows.Window 的扩展方法。 I've tested this on XP 32 bit and Win7 64 bit, both of which work correctly.我已经在 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.我希望此代码可以帮助遇到此问题的其他人。

To make this a quick copy-paste one -为了使它成为一个快速复制粘贴 -
Use this class' DoOnProcess method 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

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.一旦第二个程序运行,计算机就会变得毫无用处,因为在每次任务切换时前景在两者之间跳来跳去。

Why some of the answers on this page are wrong!为什么这个页面上的一些答案是错误的!

  • Any answer that uses window.Focus() is wrong.任何使用window.Focus()答案都是错误的。

    • Why?为什么? If a notification message pops up, window.Focus() will grab the focus away from whatever the user is typing at the time.如果弹出通知消息, window.Focus()将把焦点从用户当时正在输入的内容中移开。 This is insanely frustrating for end users, especially if the popups occur quite frequently.这对最终用户来说非常令人沮丧,尤其是在弹出窗口频繁出现的情况下。
  • Any answer that uses window.Activate() is wrong.任何使用window.Activate()答案都是错误的。

    • Why?为什么? It will make any parent windows visible as well.它也会使任何父窗口可见。
  • Any answer that omits window.ShowActivated = false is wrong.任何省略window.ShowActivated = false答案都是错误的。
    • 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.任何不使用Visibility.Visible来隐藏/显示窗口的答案都是错误的。
    • 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.如果我们使用 Citrix,如果窗口在关闭时没有折叠,它会在屏幕上留下一个奇怪的黑色矩形。 Thus, we cannot use window.Show() and window.Hide() .因此,我们不能使用window.Show()window.Hide()

Essentially:本质上:

  • The window should not grab the focus away from any other window when it activates;窗口在激活时不应将焦点从任何其他窗口移开;
  • The window should not activate its parent when it is shown;窗口在显示时不应激活其父窗口;
  • The window should be compatible with Citrix.该窗口应与 Citrix 兼容。

MVVM Solution MVVM解决方案

This code is 100% compatible with Citrix (no blank areas of the screen).此代码与 Citrix 100% 兼容(屏幕上没有空白区域)。 It is tested with both normal WPF and DevExpress.它使用普通 WPF 和 DevExpress 进行了测试。

This answer is intended for any use case where we want a small notification window that is always in front of other windows (if the user selects this in the preferences).此答案适用于我们想要一个始终位于其他窗口前面的小通知窗口(如果用户在首选项中选择此选项)的任何用例。

If this answer seems more complex than the others, it's because it is robust, enterprise level code.如果这个答案看起来比其他答案更复杂,那是因为它是健壮的企业级代码。 Some of the other answers on this page are simple, but do not actually work.此页面上的其他一些答案很简单,但实际上并不奏效。

XAML - Attached Property XAML - 附加属性

Add this attached property to any UserControl within the window.将此附加属性添加到窗口中的任何UserControl The attached property will:附加的财产将:

  • Wait until the Loaded event is fired (otherwise it cannot look up the visual tree to find the parent window).等到Loaded事件被触发(否则它无法查找可视化树以找到父窗口)。
  • Add an event handler that ensures that the window is visible or not.添加一个事件处理程序,以确保窗口可见或不可见。

At any point, you can set the window to be in front or not, by flipping the value of the attached property.在任何时候,您都可以通过翻转附加属性的值来设置窗口在前面或不在前面。

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

C# - Helper Method 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".
        }
    }
}

Usage用法

In order to use this, you need to create the window in your ViewModel:为了使用它,您需要在 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);
    }
}

Additional links附加链接

For tips on how ensure that a notification window always shifts back onto the visible screen, see my answer: In WPF, how to shift a window onto the screen if it is off the screen?有关如何确保通知窗口始终移回可见屏幕的提示,请参阅我的回答: 在 WPF 中,如果窗口不在屏幕上,如何将其移回屏幕上? . .

I know that this is late answer, maybe helpful for researchers我知道这是迟到的答案,也许对研究人员有帮助

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

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

Well, since this is such a hot topic... here is what works for me.好吧,既然这是一个如此热门的话题……这对我有用。 I got errors if I didn't do it this way because Activate() will error out on you if you cannot see the window.如果我不这样做,我会出错,因为如果您看不到窗口,Activate() 会出错。

Xaml: Xml:

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

Codebehind:代码隐藏:

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

This was the only way for me to get the window to show on top.这是我让窗口显示在顶部的唯一方法。 Then activate it so you can type in the box without having to set focus with the mouse.然后激活它,这样您就可以在框中键入内容而无需使用鼠标设置焦点。 control.Focus() wont work unless the window is Active(); control.Focus() 不会工作,除非窗口是 Active();

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.如果我暂停将其放入 BackgroundWorker 中,该调用将按预期工作。 It's a kludge, but I have no idea why it wasn't working originally.这是一个混乱,但我不知道为什么它最初不起作用。

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

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

These codes will work fine all times.这些代码在任何时候都可以正常工作。

At first set the activated event handler in XAML:首先在 XAML 中设置激活的事件处理程序:

Activated="Window_Activated"

Add below line to your Main Window constructor block:将以下行添加到您的主窗口构造函数块:

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

And inside the activated event handler copy this codes:在激活的事件处理程序中复制以下代码:

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

These steps will works fine and will bring to front all other windows into their parents window.这些步骤将正常工作,并将所有其他窗口置于其父窗口的前面。

Just wanted to add another solution to this question.只是想为这个问题添加另一个解决方案。 This implementation works for my scenario, where CaliBurn is responsible for displaying the main Window.此实现适用于我的场景,其中 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();
    }
}

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 线程,以调用将窗口置于前台的代码。

If you are trying to hide the window, for example you minimize the window, I have found that using如果您试图隐藏窗口,例如最小化窗口,我发现使用

    this.Hide();

will hide it correctly, then simply using将正确隐藏它,然后只需使用

    this.Show();

will then show the window as the top most item once again.然后将再次将窗口显示为最顶部的项目。

Remember not to put the code that shows that window inside a PreviewMouseDoubleClick handler as the active window will switch back to the window who handled the event.请记住不要将显示该窗口的代码放在 PreviewMouseDoubleClick 处理程序中,因为活动窗口将切换回处理该事件的窗口。 Just put it in the MouseDoubleClick event handler or stop bubbling by setting e.Handled to True.只需将其放入 MouseDoubleClick 事件处理程序或通过将 e.Handled 设置为 True 来停止冒泡。

In my case i was handling the PreviewMouseDoubleClick on a Listview and was not setting the e.Handled = true then it raised the MouseDoubleClick event witch sat focus back to the original window.在我的情况下,我正在处理 Listview 上的 PreviewMouseDoubleClick 并且没有设置 e.Handled = true 然后它引发了 MouseDoubleClick 事件女巫坐焦点回到原始窗口。

This is a combination of a few suggestions above that works well and is simple.这是上述一些建议的组合,效果很好而且很简单。 It only comes to front when those events fire, so any window that pops up after the event will stay on top of course.只有当这些事件触发时它才会出现在前面,所以在事件发生后弹出的任何窗口都会保持在顶部。

public partial class MainWindow : Window
{
    protected override void OnContentRendered(EventArgs e)
    {
        base.OnContentRendered(e);

        Topmost = true;
        Topmost = false;
    }
    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        Topmost = true;
        Topmost = false;
    }

    ....
}

I built an extension method to make for easy reuse.我构建了一个扩展方法,以便于重用。

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

Call in the Form Constructor调用表单构造函数

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM