首页 文章

在WPF中为窗口打开一个窗口

提问于
浏览
185

如何将我的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 ,它完全按预期工作,所以我'm not sure why it'在我的原始情况下不起作用 .

Edit :我正在与全球热键一起做这件事 .

18 回答

  • 0

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

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

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

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

    • 为什么?如果弹出通知消息, window.Focus() 将从当时用户输入的内容中获取焦点 . 这对最终用户来说是极其令人沮丧的,特别是如果弹出窗口经常发生的话 .

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

    • 为什么?它还可以使任何父窗口可见 .

    • 任何省略 window.ShowActivated = false 的答案都是错误的 .

    • 为什么?当消息弹出时,它将从另一个窗口 grab 焦点,这非常烦人!

    • 任何不使用 Visibility.Visible 隐藏/显示窗口的答案都是错误的 .

    • 为什么?如果我们使用Citrix,如果窗口在关闭时没有折叠,它将在屏幕上留下一个奇怪的黑色矩形保持 . 因此,我们不能使用 window.Show()window.Hide() .

    实质上:

    • 当窗口激活时,窗口不应将焦点从任何其他窗口移开;

    • 窗口显示时不应激活其父窗口;

    • 窗口应该兼容与Citrix .

    MVVM解决方案

    此代码与Citrix 100%兼容(屏幕没有空白区域) . 它使用普通WPF和DevExpress进行测试 .

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

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

    XAML - 附属 property

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

    • 等到 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);
        }
    }
    

    其他链接

    有关如何确保通知窗口始终切换回可见屏幕的提示,请参阅我的回答:In WPF, how to shift a window onto the screen if it is off the screen? .

  • 5
    myWindow.Activate();
    

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

    这应该是诀窍,除非我误解了你想要永远在行为上 . 在这种情况下,你想要:

    myWindow.TopMost = true;
    
  • 17

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

    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
    
  • 0

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

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

    为了使这个快速复制粘贴一个 -
    使用此类' 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

  • 149

    我知道这个问题相当陈旧,但我刚刚遇到这个精确的场景,并希望分享我实施的解决方案 .

    正如本页评论中所提到的,所提出的一些解决方案不适用于XP,我需要在我的场景中支持 . 虽然我同意@Matthew Xavier的观点,即一般来说这是一个糟糕的用户体验,但有时候它完全是一个可信的用户体验 .

    将WPF窗口置于顶端的解决方案实际上是通过我用来提供全局热键的相同代码提供给我的 . A blog article by Joseph Cooney包含link to his code samples,其中包含原始代码 .

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

    我希望这段代码可以帮助遇到这个问题的其他人 .

  • 6

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

    • 用户"owns"前景 . 例如,如果另一个程序在用户输入时偷走了前台,至少打断了她的工作流程,并且可能导致意外后果,因为她对一个应用程序的击键被犯罪者误解,直到她注意到更改,这将是非常烦人的 .

    • 想象一下,两个程序中的每一个都检查其窗口是否为前景,如果不是,则尝试将其设置为前台 . 一旦第二个程序运行,计算机就会变得无用,因为前台在每个任务切换时都会在两个程序之间反弹 .

  • 4

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

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

    我宁愿这样做比模拟一个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();
    }
    
  • 1

    那么,因为这是一个热门话题......这对我有用 . 如果我没有这样做,我会收到错误,因为如果你看不到窗口,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();

  • 6

    好吧,我想出了一个解决方法 . 我正在通过用于实现热键的键盘钩子进行呼叫 . 如果我将其置于具有暂停的BackgroundWorker中,则调用将按预期工作 . 这是一个kludge,但我不知道为什么它最初没有工作 .

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

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

    在程序中我们搜索具有指定 Headers 的应用程序(没有第一个字母的写 Headers (索引> 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);
                    }
    
  • 1

    问题可能是从钩子调用代码的线程尚未被运行时初始化,因此调用运行时方法不起作用 .

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

  • 21

    这些代码一直都可以正常工作 .

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

    Activated="Window_Activated"
    

    将以下行添加到主窗口构造函数块:

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

    这些步骤将正常工作,并将所有其他窗口带到他们的父窗口 .

  • 13

    如果你试图隐藏窗口,例如你最小化窗口,我发现使用

    this.Hide();
    

    将正确隐藏它,然后简单地使用

    this.Show();
    

    然后将再次将窗口显示为最顶级的项目 .

  • 2

    只是想为这个问题添加另一个解决方案 . 此实现适用于我的场景,其中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事件,使焦点回到原始窗口 .

  • 0

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

    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
    

    在Form Constructor中调用

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

相关问题