[英]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 条目中记录了限制。)这是因为:
Any answer that uses window.Focus()
is wrong.任何使用window.Focus()
答案都是错误的。
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()
答案都是错误的。
window.ShowActivated = false
is wrong.任何省略window.ShowActivated = false
答案都是错误的。
Visibility.Visible
to hide/show the window is wrong.任何不使用Visibility.Visible
来隐藏/显示窗口的答案都是错误的。
window.Show()
and window.Hide()
.因此,我们不能使用window.Show()
和window.Hide()
。Essentially:本质上:
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.此页面上的其他一些答案很简单,但实际上并不奏效。
Add this attached property to any UserControl
within the window.将此附加属性添加到窗口中的任何UserControl
。 The attached property will:附加的财产将:
Loaded
event is fired (otherwise it cannot look up the visual tree to find the parent window).等到Loaded
事件被触发(否则它无法查找可视化树以找到父窗口)。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}">
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".
}
}
}
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);
}
}
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.