让子窗口和父窗口同时处于激活状态
周银辉
一般情况下,激活父窗口的时候,子窗口会失去焦点,同理,激活子窗口的时候,父窗口也会失去焦点,这在某些时候不太好看,比如子窗口作为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();
}