在WPF的窗口前面
我怎样才能将我的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
)。
如果我创build一个新的空白WPF应用程序,并用计时器调用SwitchToThisWindow
,它完全按预期工作,所以我不知道为什么它不工作在我的原始情况。
编辑 :我正在与全球热键一起做这个。
myWindow.Activate();
尝试将窗口置于前台并激活它。
这应该做的伎俩,除非我误解,你想永远在上面的行为。 在这种情况下,你想要:
myWindow.TopMost = true;
我find了一个解决scheme,把窗口放在最前面,但是它的行为像一个正常的窗口:
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
如果您需要窗口在第一次加载时在前面,那么您应该使用以下内容:
private void Window_ContentRendered(object sender, EventArgs e) { this.Topmost = false; } private void Window_Initialized(object sender, EventArgs e) { this.Topmost = true; }
为了使这个快速复制粘贴一个 –
使用这个类“ 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
我知道这个问题是相当古老的,但我刚刚遇到这种精确的情况,并希望分享我已经实施的解决scheme。
正如在这个页面的评论中所提到的,提出的几个解决scheme不能在XP上工作,我需要在我的scheme中支持这个XP。 虽然我赞同@Matthew Xavier的观点,一般来说这是一种糟糕的用户体验实践,但有些时候,这完全是一个可信的用户体验。
将WPF窗口置顶的解决scheme实际上是由我用来提供全局热键的相同代码提供给我的。 Joseph Cooney的一篇博客文章包含了一个包含原始代码的代码示例的链接 。
我已经清理并修改了一些代码,并将其作为System.Windows.Window的扩展方法实现。 我已经testing了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 } }
我希望这个代码可以帮助其他遇到这个问题的人。
如果用户正在与另一个应用程序进行交互,则可能无法将您带到前面。 一般来说,如果进程已经是前台进程,进程只能设置前台窗口。 (Microsoftlogging了SetForegroundWindow() MSDN条目中的限制) 。这是因为:
- 用户“拥有”前台。 例如,如果另一个程序在用户打字时偷走了前景,至less打断了她的工作stream程,并且可能造成意想不到的后果,这是非常烦人的,因为她的击键意味着对于一个应用程序被错误地解释,直到她注意到改变。
- 想象一下,两个程序中的每一个都检查它的窗口是否为前景,如果不是,则尝试将其设置为前景。 一旦第二个程序运行,当每个任务开关的前景在两者之间跳动时,计算机就变得无用了。
我有一个WPF应用程序通过Shell对象从Access应用程序调用类似的问题。
我的解决scheme如下 – 适用于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(); }
为什么这个网页上的一些答案是错误的!
-
任何使用
window.Focus()
答案都是错误的。- 为什么? 如果popup通知消息,
window.Focus()
将从当前用户正在input的内容中获取焦点。 这对最终用户来说是非常令人沮丧的,特别是如果popup窗口频繁出现的话。
- 为什么? 如果popup通知消息,
-
任何使用
window.Activate()
答案都是错误的。- 为什么? 这将使任何父窗口可见。
- 任何遗漏
window.ShowActivated = false
答案都是错误的。- 为什么? 当消息popup时,它将从另一个窗口抓住焦点,这非常烦人!
- 任何不使用
Visibility.Visible
答案隐藏/显示窗口是错误的。- 为什么? 如果我们使用的是Citrix,如果窗口在closures的时候没有被折叠,它会在屏幕上留下一个奇怪的黑色矩形。 因此,我们不能使用
window.Show()
和window.Hide()
。
- 为什么? 如果我们使用的是Citrix,如果窗口在closures的时候没有被折叠,它会在屏幕上留下一个奇怪的黑色矩形。 因此,我们不能使用
主要有:
- 当窗口激活时,窗口不应该把焦点从其他窗口中移走;
- 显示窗口时不应该激活父窗口;
- 该窗口应该与Citrix兼容。
MVVM解决scheme
此代码与Citrix 100%兼容(屏幕没有空白区域)。 它使用正常的WPF和DevExpress进行testing。
这个答案适用于任何使用情况,我们希望一个小的通知窗口总是在其他窗口的前面(如果用户在首选项中select)。
如果这个答案似乎比别人更复杂,那是因为它是健壮的企业级代码。 在这个页面上的其他一些答案很简单,但实际上并没有工作。
XAML – 附加属性
将此附加属性添加到窗口中的任何UserControl
。 所附物业将:
- 等到
Loaded
事件被触发(否则无法查找可视化树来查找父窗口)。 - 添加一个确保窗口可见或不可见的事件处理程序。
在任何时候,您都可以通过翻转附加属性的值来将窗口设置在前面或前面。
<UserControl x:Class="..." ... attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground= "{Binding EnsureWindowInForeground, Mode=OneWay}">
C# – Helper方法
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中创build窗口:
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中,如果窗口在屏幕之外,如何将窗口移动到屏幕上? 。
那么,因为这是一个如此热门的话题…这是对我有用的东西。 如果我没有这样做,我得到了错误,因为如果你看不到窗口,Activate()将会出错。
XAML:
<Window .... Topmost="True" .... ContentRendered="mainWindow_ContentRendered"> .... </Window>
代码隐藏:
private void mainWindow_ContentRendered(object sender, EventArgs e) { this.Topmost = false; this.Activate(); _UsernameTextBox.Focus(); }
这是让我把窗口展现在上面的唯一方法。 然后激活它,所以你可以input框,而不必用鼠标设置焦点。 control.Focus()不会工作,除非窗口是活动();
我知道这是迟到的答案,也许对研究人员有帮助
if (!WindowName.IsVisible) { WindowName.Show(); WindowName.Activate(); }
那么我想出了一个工作。 我正在使用键盘钩子来实现热键。 如果我把它放入一个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(); }
要显示任何当前打开的窗口导入这些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);
并在程序中我们search具有指定标题的应用程序(写没有第一个字母的标题(索引> 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); }
问题可能是从钩子调用你的代码的线程没有被运行时初始化,所以调用运行时方法不起作用。
也许你可以尝试做一个Invoke将你的代码编组到UI线程来调用你的代码,使窗口的前景。
这些代码将始终正常工作。
首先在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(); }
这些步骤可以正常工作,并将把所有其他窗口带到父母的窗口。
我build立了一个扩展方法,以方便重用。
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
如果你试图隐藏窗口,例如你最小化窗口,我发现使用
this.Hide();
将正确隐藏,然后简单地使用
this.Show();
然后再次将窗口显示为最上面的项目。
只是想为这个问题添加另一个解决scheme。 这个实现适用于我的场景,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(); } }
请记住,不要将代码显示在PreviewMouseDoubleClick处理程序中,因为活动窗口将切换回处理事件的窗口。 只要将它放在MouseDoubleClick事件处理程序中,或通过将e.Handled设置为True来停止冒泡。
在我的情况下,我正在处理ListView的PreviewMouseDoubleClick,并没有设置e.Handled = true,然后它提高了MouseDoubleClick事件女巫焦点回到原来的窗口。
您可以使用Control.BringToFront
FormName.BringToFront();