/************************************************************************************* 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.Text; 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 Focus Management static List _managers = new List(); 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; } } } 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; } } 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; } } } } static FullWeakDictionary _modelFocusedElement = new FullWeakDictionary(); static WeakDictionary _modelFocusedWindowHandle = new WeakDictionary(); /// /// 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; } static WeakReference _lastFocusedElement; /// /// 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); Trace.WriteLine( string.Format( "SetFocusOnLastElement(focused={0}, model={1}, element={2})", focused, model, handleToFocus == IntPtr.Zero ? ( objectToFocus == null ? "" : objectToFocus.ToString() ) : handleToFocus.ToString() ) ); if (focused) { _lastFocusedElement = new WeakReference(model); } } static WindowHookHandler _windowHandler = null; 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; } } } } } static DispatcherOperation _setFocusAsyncOperation; static void WindowActivating(object sender, WindowActivateEventArgs e) { Trace.WriteLine("WindowActivating"); 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.Background); } } } static WeakReference _lastFocusedElementBeforeEnterMenuMode = null; 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); } 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 } }