/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Input; using System.Windows.Interop; using System.Windows; using System.Diagnostics; using Xceed.Wpf.AvalonDock.Layout; using System.Windows.Media; using System.Windows.Threading; namespace Xceed.Wpf.AvalonDock.Controls { internal static class FocusElementManager { #region Member private static List _managers = new List(); private static FullWeakDictionary _modelFocusedElement = new FullWeakDictionary(); private static WeakDictionary _modelFocusedWindowHandle = new WeakDictionary(); private static WeakReference _lastFocusedElement; private static WindowHookHandler _windowHandler = null; private static DispatcherOperation _setFocusAsyncOperation; private static WeakReference _lastFocusedElementBeforeEnterMenuMode = null; #endregion #region Internal Methods internal static void SetupFocusManagement( DockingManager manager ) { if( _managers.Count == 0 ) { //InputManager.Current.EnterMenuMode += new EventHandler(InputManager_EnterMenuMode); //InputManager.Current.LeaveMenuMode += new EventHandler(InputManager_LeaveMenuMode); _windowHandler = new WindowHookHandler(); _windowHandler.FocusChanged += new EventHandler( WindowFocusChanging ); //_windowHandler.Activate += new EventHandler(WindowActivating); _windowHandler.Attach(); if( Application.Current != null ) Application.Current.Exit += new ExitEventHandler( Current_Exit ); } manager.PreviewGotKeyboardFocus += new KeyboardFocusChangedEventHandler( manager_PreviewGotKeyboardFocus ); _managers.Add( manager ); } internal static void FinalizeFocusManagement( DockingManager manager ) { manager.PreviewGotKeyboardFocus -= new KeyboardFocusChangedEventHandler( manager_PreviewGotKeyboardFocus ); _managers.Remove( manager ); if( _managers.Count == 0 ) { //InputManager.Current.EnterMenuMode -= new EventHandler(InputManager_EnterMenuMode); //InputManager.Current.LeaveMenuMode -= new EventHandler(InputManager_LeaveMenuMode); if( _windowHandler != null ) { _windowHandler.FocusChanged -= new EventHandler( WindowFocusChanging ); //_windowHandler.Activate -= new EventHandler(WindowActivating); _windowHandler.Detach(); _windowHandler = null; } } } /// /// Get the input element that was focused before user left the layout element /// /// Element to look for /// Input element internal static IInputElement GetLastFocusedElement( ILayoutElement model ) { IInputElement objectWithFocus; if( _modelFocusedElement.GetValue( model, out objectWithFocus ) ) return objectWithFocus; return null; } /// /// Get the last window handle focused before user left the element passed as argument /// /// /// internal static IntPtr GetLastWindowHandle( ILayoutElement model ) { IntPtr handleWithFocus; if( _modelFocusedWindowHandle.GetValue( model, out handleWithFocus ) ) return handleWithFocus; return IntPtr.Zero; } /// /// Given a layout element tries to set the focus of the keyword where it was before user moved to another element /// /// internal static void SetFocusOnLastElement( ILayoutElement model ) { bool focused = false; IInputElement objectToFocus; if( _modelFocusedElement.GetValue( model, out objectToFocus ) ) { focused = objectToFocus == Keyboard.Focus( objectToFocus ); } IntPtr handleToFocus; if( _modelFocusedWindowHandle.GetValue( model, out handleToFocus ) ) focused = IntPtr.Zero != Win32Helper.SetFocus( handleToFocus ); if( focused ) { _lastFocusedElement = new WeakReference( model ); } } #endregion #region Private Methods private static void Current_Exit( object sender, ExitEventArgs e ) { Application.Current.Exit -= new ExitEventHandler( Current_Exit ); if( _windowHandler != null ) { _windowHandler.FocusChanged -= new EventHandler( WindowFocusChanging ); //_windowHandler.Activate -= new EventHandler(WindowActivating); _windowHandler.Detach(); _windowHandler = null; } } private static void manager_PreviewGotKeyboardFocus( object sender, KeyboardFocusChangedEventArgs e ) { var focusedElement = e.NewFocus as Visual; if( focusedElement != null && !( focusedElement is LayoutAnchorableTabItem || focusedElement is LayoutDocumentTabItem ) ) //Avoid tracking focus for elements like this { var parentAnchorable = focusedElement.FindVisualAncestor(); if( parentAnchorable != null ) { _modelFocusedElement[ parentAnchorable.Model ] = e.NewFocus; } else { var parentDocument = focusedElement.FindVisualAncestor(); if( parentDocument != null ) { _modelFocusedElement[ parentDocument.Model ] = e.NewFocus; } } } } private static void WindowFocusChanging( object sender, FocusChangeEventArgs e ) { foreach( var manager in _managers ) { var hostContainingFocusedHandle = manager.FindLogicalChildren().FirstOrDefault( hw => Win32Helper.IsChild( hw.Handle, e.GotFocusWinHandle ) ); if( hostContainingFocusedHandle != null ) { var parentAnchorable = hostContainingFocusedHandle.FindVisualAncestor(); if( parentAnchorable != null ) { _modelFocusedWindowHandle[ parentAnchorable.Model ] = e.GotFocusWinHandle; if( parentAnchorable.Model != null ) parentAnchorable.Model.IsActive = true; } else { var parentDocument = hostContainingFocusedHandle.FindVisualAncestor(); if( parentDocument != null ) { _modelFocusedWindowHandle[ parentDocument.Model ] = e.GotFocusWinHandle; if( parentDocument.Model != null ) parentDocument.Model.IsActive = true; } } } } } private static void WindowActivating( object sender, WindowActivateEventArgs e ) { if( Keyboard.FocusedElement == null && _lastFocusedElement != null && _lastFocusedElement.IsAlive ) { var elementToSetFocus = _lastFocusedElement.Target as ILayoutElement; if( elementToSetFocus != null ) { var manager = elementToSetFocus.Root.Manager; if( manager == null ) return; IntPtr parentHwnd; if( !manager.GetParentWindowHandle( out parentHwnd ) ) return; if( e.HwndActivating != parentHwnd ) return; _setFocusAsyncOperation = Dispatcher.CurrentDispatcher.BeginInvoke( new Action( () => { try { SetFocusOnLastElement( elementToSetFocus ); } finally { _setFocusAsyncOperation = null; } } ), DispatcherPriority.Input ); } } } private static void InputManager_EnterMenuMode( object sender, EventArgs e ) { if( Keyboard.FocusedElement == null ) return; var lastfocusDepObj = Keyboard.FocusedElement as DependencyObject; if( lastfocusDepObj.FindLogicalAncestor() == null ) { _lastFocusedElementBeforeEnterMenuMode = null; return; } _lastFocusedElementBeforeEnterMenuMode = new WeakReference( Keyboard.FocusedElement ); } private static void InputManager_LeaveMenuMode( object sender, EventArgs e ) { if( _lastFocusedElementBeforeEnterMenuMode != null && _lastFocusedElementBeforeEnterMenuMode.IsAlive ) { var lastFocusedInputElement = _lastFocusedElementBeforeEnterMenuMode.GetValueOrDefault(); if( lastFocusedInputElement != null ) { if( lastFocusedInputElement != Keyboard.Focus( lastFocusedInputElement ) ) Debug.WriteLine( "Unable to activate the element" ); } } } #endregion } }