让子窗口和父窗口同时处于激活状态
让子窗口和父窗口同时处于激活状态
周银辉
一般情况下,激活父窗口的时候,子窗口会失去焦点,同理,激活子窗口的时候,父窗口也会失去焦点,这在某些时候不太好看,比如子窗口作为ToolWindow漂浮在父窗口上面时。Visual Studio好像也有这个问题,当激活其中某个处于浮动状态的DockingPanel时,Visual Studio主窗口会失去焦点。
上两幅图,更明白点,
一般效果如下:
我们现在追求的效果如下:
恩,这里有个简单的包装,呵呵,复制下代码就可以直接使用了(Framework版本需要3.5):
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace YourNamespace
{
public static class WindowActiveService
{
internal class WindowActiveHelper
{
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static int SendMessage(
IntPtr handle, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
// ReSharper disable InconsistentNaming
private const int WM_NCACTIVATE = 0x086;
// ReSharper restore InconsistentNaming
private IntPtr ownerHwnd = IntPtr.Zero;
private IntPtr childHwnd = IntPtr.Zero;
private HwndSource ownerHwndSource;
private HwndSource childHwndSource;
private HwndSourceHook ownerHook;
private HwndSourceHook childHook;
private bool childActive;
private bool ownerActive;
private static IntPtr GetWindowHwnd(Window window)
{
var helper = new WindowInteropHelper(window);
return helper.Handle;
}
private IntPtr FilterChildMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_NCACTIVATE)
{
if (((int)wParam) == 0)
{
IntPtr handle = GetForegroundWindow();
if (handle == childHwnd || handle == ownerHwnd)
{
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
else
{
if (childActive)
{
childActive = false;
SendMessage(childHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
if (ownerActive)
{
ownerActive = false;
SendMessage(ownerHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
}
}
if (((int)wParam) == 1)
{
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
}
return IntPtr.Zero;
}
private IntPtr FilterOwnerMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_NCACTIVATE)
{
if (((int)wParam) == 0)
{
IntPtr handle = GetForegroundWindow();
try
{
if (handle == ownerHwnd || handle == childHwnd)
{
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
else
{
if (ownerActive)
{
ownerActive = false;
SendMessage(ownerHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
if (childActive)
{
childActive = false;
SendMessage(childHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
}
}
// ReSharper disable EmptyGeneralCatchClause
catch (Exception)
// ReSharper restore EmptyGeneralCatchClause
{
}
}
if (((int)wParam) == 1)
{
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
}
return IntPtr.Zero;
}
private void RemoveChildHook()
{
if (childHwndSource != null && childHook != null)
{
childHwndSource.RemoveHook(childHook);
}
}
private void RemoveOwnerHook()
{
if (ownerHwndSource != null && ownerHook != null)
{
ownerHwndSource.RemoveHook(ownerHook);
}
}
public void RegisterFloatingToolWindow(Window childWindow, Window ownerWindow)
{
childWindow.Owner = ownerWindow;
childWindow.Unloaded += ((sender, args) => RemoveChildHook());
ownerWindow.Unloaded += ((sender, args) => RemoveOwnerHook());
childWindow.Activated += ((sender, args) => childActive = true);
ownerWindow.Activated += ((sender, args) => ownerActive = true);
childWindow.Deactivated += ((sender, args) => childActive = false);
ownerWindow.Deactivated += ((sender, args) => ownerActive = false);
childHwnd = GetWindowHwnd(childWindow);
ownerHwnd = GetWindowHwnd(ownerWindow);
childHwndSource = HwndSource.FromHwnd(childHwnd);
ownerHwndSource = HwndSource.FromHwnd(ownerHwnd);
childHook = new HwndSourceHook(FilterChildMessage);
ownerHook = new HwndSourceHook(FilterOwnerMessage);
if (childHwndSource != null)
{
childHwndSource.AddHook(childHook);
}
if (ownerHwndSource != null)
{
ownerHwndSource.AddHook(ownerHook);
}
}
}
/// <summary>
/// (notes: call this before loading)
/// </summary>
public static void RegisterAsActivePreemptionDisabledWindow(this Window window, Window owner)
{
var helper = new WindowActiveHelper();
window.Loaded += delegate
{
helper.RegisterFloatingToolWindow(window, owner);
owner.Activate();
};
}
}
}
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace YourNamespace
{
public static class WindowActiveService
{
internal class WindowActiveHelper
{
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static int SendMessage(
IntPtr handle, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
// ReSharper disable InconsistentNaming
private const int WM_NCACTIVATE = 0x086;
// ReSharper restore InconsistentNaming
private IntPtr ownerHwnd = IntPtr.Zero;
private IntPtr childHwnd = IntPtr.Zero;
private HwndSource ownerHwndSource;
private HwndSource childHwndSource;
private HwndSourceHook ownerHook;
private HwndSourceHook childHook;
private bool childActive;
private bool ownerActive;
private static IntPtr GetWindowHwnd(Window window)
{
var helper = new WindowInteropHelper(window);
return helper.Handle;
}
private IntPtr FilterChildMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_NCACTIVATE)
{
if (((int)wParam) == 0)
{
IntPtr handle = GetForegroundWindow();
if (handle == childHwnd || handle == ownerHwnd)
{
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
else
{
if (childActive)
{
childActive = false;
SendMessage(childHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
if (ownerActive)
{
ownerActive = false;
SendMessage(ownerHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
}
}
if (((int)wParam) == 1)
{
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
}
return IntPtr.Zero;
}
private IntPtr FilterOwnerMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_NCACTIVATE)
{
if (((int)wParam) == 0)
{
IntPtr handle = GetForegroundWindow();
try
{
if (handle == ownerHwnd || handle == childHwnd)
{
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
else
{
if (ownerActive)
{
ownerActive = false;
SendMessage(ownerHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
if (childActive)
{
childActive = false;
SendMessage(childHwnd, WM_NCACTIVATE, 0, IntPtr.Zero);
}
}
}
// ReSharper disable EmptyGeneralCatchClause
catch (Exception)
// ReSharper restore EmptyGeneralCatchClause
{
}
}
if (((int)wParam) == 1)
{
if (ownerActive == false)
{
ownerActive = true;
SendMessage(ownerHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
if (childActive == false)
{
childActive = true;
SendMessage(childHwnd, WM_NCACTIVATE, 1, IntPtr.Zero);
}
}
}
return IntPtr.Zero;
}
private void RemoveChildHook()
{
if (childHwndSource != null && childHook != null)
{
childHwndSource.RemoveHook(childHook);
}
}
private void RemoveOwnerHook()
{
if (ownerHwndSource != null && ownerHook != null)
{
ownerHwndSource.RemoveHook(ownerHook);
}
}
public void RegisterFloatingToolWindow(Window childWindow, Window ownerWindow)
{
childWindow.Owner = ownerWindow;
childWindow.Unloaded += ((sender, args) => RemoveChildHook());
ownerWindow.Unloaded += ((sender, args) => RemoveOwnerHook());
childWindow.Activated += ((sender, args) => childActive = true);
ownerWindow.Activated += ((sender, args) => ownerActive = true);
childWindow.Deactivated += ((sender, args) => childActive = false);
ownerWindow.Deactivated += ((sender, args) => ownerActive = false);
childHwnd = GetWindowHwnd(childWindow);
ownerHwnd = GetWindowHwnd(ownerWindow);
childHwndSource = HwndSource.FromHwnd(childHwnd);
ownerHwndSource = HwndSource.FromHwnd(ownerHwnd);
childHook = new HwndSourceHook(FilterChildMessage);
ownerHook = new HwndSourceHook(FilterOwnerMessage);
if (childHwndSource != null)
{
childHwndSource.AddHook(childHook);
}
if (ownerHwndSource != null)
{
ownerHwndSource.AddHook(ownerHook);
}
}
}
/// <summary>
/// (notes: call this before loading)
/// </summary>
public static void RegisterAsActivePreemptionDisabledWindow(this Window window, Window owner)
{
var helper = new WindowActiveHelper();
window.Loaded += delegate
{
helper.RegisterFloatingToolWindow(window, owner);
owner.Activate();
};
}
}
}
如何使用上面的代码:
在子窗口Show之前,调用RegisterAsActivePreemptionDisabledWindow()方法。比如上图中,点击主窗口的Button,然后:
private void Button_Click(object sender, RoutedEventArgs e)
{
var child = new ChildWindow {Owner = this};
child.RegisterAsActivePreemptionDisabledWindow(this);
child.Show();
}
{
var child = new ChildWindow {Owner = this};
child.RegisterAsActivePreemptionDisabledWindow(this);
child.Show();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述