You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
9.7 KiB
272 lines
9.7 KiB
/*************************************************************************************
|
|
|
|
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<DockingManager> _managers = new List<DockingManager>();
|
|
private static FullWeakDictionary<ILayoutElement, IInputElement> _modelFocusedElement = new FullWeakDictionary<ILayoutElement, IInputElement>();
|
|
private static WeakDictionary<ILayoutElement, IntPtr> _modelFocusedWindowHandle = new WeakDictionary<ILayoutElement, IntPtr>();
|
|
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<FocusChangeEventArgs>( WindowFocusChanging );
|
|
//_windowHandler.Activate += new EventHandler<WindowActivateEventArgs>(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<FocusChangeEventArgs>( WindowFocusChanging );
|
|
//_windowHandler.Activate -= new EventHandler<WindowActivateEventArgs>(WindowActivating);
|
|
_windowHandler.Detach();
|
|
_windowHandler = null;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the input element that was focused before user left the layout element
|
|
/// </summary>
|
|
/// <param name="model">Element to look for</param>
|
|
/// <returns>Input element </returns>
|
|
internal static IInputElement GetLastFocusedElement( ILayoutElement model )
|
|
{
|
|
IInputElement objectWithFocus;
|
|
if( _modelFocusedElement.GetValue( model, out objectWithFocus ) )
|
|
return objectWithFocus;
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get the last window handle focused before user left the element passed as argument
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
/// <returns></returns>
|
|
internal static IntPtr GetLastWindowHandle( ILayoutElement model )
|
|
{
|
|
IntPtr handleWithFocus;
|
|
if( _modelFocusedWindowHandle.GetValue( model, out handleWithFocus ) )
|
|
return handleWithFocus;
|
|
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given a layout element tries to set the focus of the keyword where it was before user moved to another element
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
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<FocusChangeEventArgs>( WindowFocusChanging );
|
|
//_windowHandler.Activate -= new EventHandler<WindowActivateEventArgs>(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<LayoutAnchorableControl>();
|
|
if( parentAnchorable != null )
|
|
{
|
|
_modelFocusedElement[ parentAnchorable.Model ] = e.NewFocus;
|
|
}
|
|
else
|
|
{
|
|
var parentDocument = focusedElement.FindVisualAncestor<LayoutDocumentControl>();
|
|
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<HwndHost>().FirstOrDefault( hw => Win32Helper.IsChild( hw.Handle, e.GotFocusWinHandle ) );
|
|
|
|
if( hostContainingFocusedHandle != null )
|
|
{
|
|
var parentAnchorable = hostContainingFocusedHandle.FindVisualAncestor<LayoutAnchorableControl>();
|
|
if( parentAnchorable != null )
|
|
{
|
|
_modelFocusedWindowHandle[ parentAnchorable.Model ] = e.GotFocusWinHandle;
|
|
if( parentAnchorable.Model != null )
|
|
parentAnchorable.Model.IsActive = true;
|
|
}
|
|
else
|
|
{
|
|
var parentDocument = hostContainingFocusedHandle.FindVisualAncestor<LayoutDocumentControl>();
|
|
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<DockingManager>() == 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<UIElement>();
|
|
if( lastFocusedInputElement != null )
|
|
{
|
|
if( lastFocusedInputElement != Keyboard.Focus( lastFocusedInputElement ) )
|
|
Debug.WriteLine( "Unable to activate the element" );
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|