在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)。

如果我创建一个新的空白WPF应用程序,并使用计时器调用SwitchToThisWindow,它完全按照预期工作,所以我不确定为什么它在我最初的情况下不起作用。

编辑:我正在与一个全局热键一起做这个。

206954 次浏览

我想到了一个变通办法。我从一个用于实现热键的键盘钩子进行调用。如果我把它放入一个暂停的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();
}
myWindow.Activate();

试图将窗口带到前台并激活它。

这应该做的把戏,除非我误解了,你想要永远在顶端的行为。在这种情况下,你需要:

myWindow.TopMost = true;

如果用户正在与另一个应用程序交互,则可能无法将您的应用程序放在前面。作为一般规则,只有当进程已经是前台进程时,该进程才能期望设置前台窗口。(微软在窗体提前() MSDN条目中记录了限制。)这是因为:

  1. 用户“拥有”前台。例如,如果另一个程序在用户正在输入时窃取了前台,这将是非常令人讨厌的,至少会中断她的工作流程,并可能导致意想不到的后果,因为她对一个应用程序的击键被违法者误解了,直到她注意到这个变化。
  2. 想象一下,两个程序都检查自己的窗口是否是前台,如果不是前台,就尝试将其设置为前台。只要第二个程序正在运行,计算机就会变得无用,因为在每次任务切换时,前台都会在两个程序之间来回切换。

问题可能是线程从钩子调用你的代码还没有被运行时初始化,所以调用运行时方法不起作用。

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

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

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


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

或者重写方法:

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


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

我有一个类似的问题与WPF应用程序,从访问应用程序通过Shell对象调用。

我的解决方案如下-工作在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();
}

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

我已经找到了一个解决方案,将窗口带到顶部,但它表现为一个正常的窗口:

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
要使它快速复制粘贴-
使用这个类的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

我知道这个问题相当古老,但我刚刚遇到了这个精确的场景,并想分享我实现的解决方案。

正如本页的评论中提到的,提出的几个解决方案不能在XP上工作,而我需要在我的场景中支持XP。虽然我同意@Matthew Xavier的观点,他认为这是一种糟糕的用户体验实践,但有时这完全是一种合理的用户体验。

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

我希望这段代码能帮助其他遇到这个问题的人。

既然这是个热门话题…以下是对我有效的方法。如果我不这样做,我就会得到错误,因为如果你看不到窗口,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();

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

    this.Hide();

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

    this.Show();

然后将窗口再次显示为最上面的项目。

我知道这是一个很晚的答案,也许对研究人员有帮助

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

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

  • 任何使用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事件被触发(否则它无法查找可视树来找到父窗口)。
  • 添加一个事件处理程序,以确保窗口是否可见。

在任何时候,您都可以通过翻转附加属性的值来设置窗口在前面或不在前面。


辅助方法

public static class HideAndShowWindowHelper
{
/// 
///     Intent: Ensure that small notification window is on top of other windows.
/// 
/// 
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".
}
}


/// 
///     Intent: Ensure that small notification window can be hidden by other windows.
/// 
/// 
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(this, this, _toastViewWindow, true);
}
ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}


private void HideWindow()
{
if (_toastViewWindow != null)
{
HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
}
}

额外的链接

有关如何确保通知窗口总是移回可见屏幕的提示,请参阅我的回答:在WPF中,如何将窗口移到屏幕上,如果它不在屏幕上?

这些代码在任何时候都能正常工作。

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

这些步骤将工作良好,并将前面所有其他窗口进入他们的父母窗口。

只是想给这个问题加上一个解。这个实现适用于我的场景,其中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来停止冒泡。

在我的情况下,我正在处理一个列表视图上的PreviewMouseDoubleClick,并没有设置e.Handled = true,然后它会引发MouseDoubleClick事件,将焦点集中到原始窗口。

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

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

这是上面几个简单有效的建议的结合。它只在那些事件触发时才出现在前面,因此事件之后弹出的任何窗口当然都会保持在顶部。

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


....
}