/// <summary>
/// 前置窗口,这个方法解决许多窗口显示时没有前置的问题
/// </summary>
/// <param name="window"></param>
public static void ActivateWindow(this Window window)
{
try
{
var hwnd = new WindowInteropHelper(window).EnsureHandle();
for (int i = 0; i < 5; i++)
{
var isActive = ActivateWindow(hwnd);
if (isActive) return;
LogHelper.Info($"activewindow status:{isActive} count:{i} title:{window.Title}");
}
}
catch (Exception ex)
{
LogHelper.Error(ex);
}
}
/// <summary>
/// 前置窗口
/// </summary>
/// <param name="hwnd"></param>
/// <returns></returns>
private static bool ActivateWindow(IntPtr hwnd)
{
var threadId1 = GetWindowThreadProcessId(GetForegroundWindow(), out _);
var threadId2 = GetWindowThreadProcessId(hwnd, out _);
if (threadId1 != threadId2)
{
AttachThreadInput(threadId1, threadId2, true);
var isTrue = ShowWindow(hwnd, SW_SHOWNORMAL);
if (isTrue)
{
SetWindowPos(hwnd, new IntPtr(HWND_TOP), 0, 0, 0, 0, SetWindowPosFlags.IgnoreMoveAndResize);
isTrue = SetForegroundWindow(hwnd);
}
AttachThreadInput(threadId1, threadId2, false);
if (isTrue)
{
var curForegroundWindowHwnd = GetForegroundWindow();
return curForegroundWindowHwnd == hwnd;
}
return false;
}
else
return SetForegroundWindow(hwnd);
}
/// <summary>
/// 显示窗口
/// </summary>
/// <param name="hWnd"></param>
/// <param name="nCmdShow"></param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
/// <summary>
/// 返回创建指定窗口线程的标识和创建窗口的进程的标识符,后一项是可选的。
/// </summary>
/// <param name="hWnd">窗口句柄。</param>
/// <param name="processId">返回进程id</param>
/// <returns>创建窗口的线程id</returns>
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
/// <summary>
/// 将一个线程的输入处理机制贴附到另一个线程
/// </summary>
/// <param name="fromThreadId"></param>
/// <param name="toThread"></param>
/// <param name="isAttach"></param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern int AttachThreadInput(int fromThreadId, int toThread, bool isAttach);
/// <summary>
/// 查找windows活动窗口
/// </summary>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
/// <summary>
/// 设置活动窗口
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
#region setwindowpos
/// <summary>
/// <see cref="SetWindowPos"/> 参数hWndInsertAfter的可选值:在前面
/// </summary>
public const int HWND_TOP = 0;
/// <summary>
/// <see cref="SetWindowPos"/> 参数hWndInsertAfter的可选值:在后面
/// </summary>
public const int HWND_BOTTOM = 1;
/// <summary>
/// <see cref="SetWindowPos"/> 参数hWndInsertAfter的可选值:在前面,位于任何顶部窗口的前面
/// </summary>
public const int HWND_TOPMOST = -1;
/// <summary>
/// <see cref="SetWindowPos"/> 参数hWndInsertAfter的可选值:在前面,位于任何顶部窗口的后面
/// </summary>
public const int HWND_NOTOPMOST = -2;
/// <summary>
/// 设置窗口位置
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="hWndInsertAfter">在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:
/// <para><see cref="HWND_TOP"/></para>
/// <para><see cref="HWND_BOTTOM"/></para>
/// <para><see cref="HWND_TOPMOST"/></para>
/// <para><see cref="HWND_NOTOPMOST"/></para></param>
/// <param name="x">以客户坐标指定窗口新位置的左边界</param>
/// <param name="y">以客户坐标指定窗口新位置的顶边界</param>
/// <param name="cx">以像素指定窗口的新的宽度</param>
/// <param name="cy">以像素指定窗口的新的高度</param>
/// <param name="uFlags">窗口尺寸和定位的标志</param>
/// <returns></returns>
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
SetWindowPosFlags uFlags);
[Flags]
public enum SetWindowPosFlags : uint
{
/// <summary>
/// 如果调用进程不拥有窗口,系统会向拥有窗口的线程发出需求。这就防止调用线程在其他线程处理需求的时候发生死锁。
/// </summary>
SWP_ASNCWINDOWPOS = 0x4000,
/// <summary>
/// 防止产生WM_SYNCPAINT消息。
/// </summary>
SWP_DEFERERASE = 0x2000,
/// <summary>
/// 在窗口周围画一个边框(定义在窗口类描述中)。
/// </summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>
/// 给窗口发送WM_NCCALCSIZE消息,即使窗口尺寸没有改变也会发送该消息。如果未指定这个标志,只有在改变了窗口尺寸时才发送WM_NCCALCSIZE。
/// </summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>
/// 隐藏窗口。
/// </summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>
/// 不激活窗口。如果未设置标志,则窗口被激活,并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)。
/// </summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>
/// 清除客户区的所有内容。如果未设置该标志,客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区。
/// </summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>
/// 维持当前位置(忽略X和Y参数)。
/// </summary>
SWP_NOMOVE = 0x0002,
/// <summary>
/// 不改变z序中的所有者窗口的位置。
/// </summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>
/// 不重画改变的内容。如果设置了这个标志,则不发生任何重画动作。适用于客户区和非客户区(包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志,应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分。
/// </summary>
SWP_NOREDRAW = 0x0008,
/// <summary>
/// 与SWP_NOOWNERZORDER标志相同。
/// </summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>
/// 防止窗口接收WM_WINDOWPOSCHANGING消息。
/// </summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>
/// 维持当前尺寸(忽略cx和Cy参数)。
/// </summary>
SWP_NOSIZE = 0x0001,
/// <summary>
/// 维持当前Z序(忽略hWndlnsertAfter参数)。
/// </summary>
SWP_NOZORDER = 0x0004,
/// <summary>
/// 显示窗口。
/// </summary>
SWP_SHOWWINDOW = 0x0040,
/// <summary>
/// 空
/// </summary>
NoFlag = 0x0000,
/// <summary>
/// 忽略位置和尺寸
/// </summary>
IgnoreMoveAndResize = SWP_NOMOVE | SWP_NOSIZE,
}
#endregion
这个方法需要在窗口已经show后才能调用,如果窗口时一个模态对话框,那么窗口ShowDialog后,是不能调用此方法的,那么如果和解决模态对话框的窗口也能设置呢,方法很简单,在窗口的Loaded事件里面调用ActiveWindow方法即可。