让子窗口和父窗口同时处于激活状态

时间:2022-08-18 19:00:22

                                    让子窗口和父窗口同时处于激活状态
                                             周银辉

 

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

 

如何使用上面的代码:
在子窗口Show之前,调用RegisterAsActivePreemptionDisabledWindow()方法。比如上图中,点击主窗口的Button,然后:

        private void Button_Click( object  sender, RoutedEventArgs e)
        {
            var child = 
new  ChildWindow {Owner = this};
            
            child.RegisterAsActivePreemptionDisabledWindow(this);

            child.Show();
        }