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.
587 lines
18 KiB
587 lines
18 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.Linq;
|
|
using System.Windows;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Input;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Media;
|
|
using Xceed.Wpf.AvalonDock.Layout;
|
|
using System.Windows.Documents;
|
|
using Xceed.Wpf.AvalonDock.Themes;
|
|
|
|
namespace Xceed.Wpf.AvalonDock.Controls
|
|
{
|
|
public abstract class LayoutFloatingWindowControl : Window, ILayoutControl
|
|
{
|
|
#region Members
|
|
|
|
private ResourceDictionary currentThemeResourceDictionary; // = null
|
|
private bool _isInternalChange; //false
|
|
private ILayoutElement _model;
|
|
private bool _attachDrag = false;
|
|
private HwndSource _hwndSrc;
|
|
private HwndSourceHook _hwndSrcHook;
|
|
private DragService _dragService = null;
|
|
private bool _internalCloseFlag = false;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
static LayoutFloatingWindowControl()
|
|
{
|
|
LayoutFloatingWindowControl.ContentProperty.OverrideMetadata( typeof( LayoutFloatingWindowControl ), new FrameworkPropertyMetadata( null, null, new CoerceValueCallback( CoerceContentValue ) ) );
|
|
AllowsTransparencyProperty.OverrideMetadata( typeof( LayoutFloatingWindowControl ), new FrameworkPropertyMetadata( false ) );
|
|
ShowInTaskbarProperty.OverrideMetadata( typeof( LayoutFloatingWindowControl ), new FrameworkPropertyMetadata( false ) );
|
|
}
|
|
|
|
protected LayoutFloatingWindowControl( ILayoutElement model )
|
|
{
|
|
this.Loaded += new RoutedEventHandler( OnLoaded );
|
|
this.Unloaded += new RoutedEventHandler( OnUnloaded );
|
|
_model = model;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
#region Model
|
|
|
|
public abstract ILayoutElement Model
|
|
{
|
|
get;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IsDragging
|
|
|
|
/// <summary>
|
|
/// IsDragging Read-Only Dependency Property
|
|
/// </summary>
|
|
private static readonly DependencyPropertyKey IsDraggingPropertyKey = DependencyProperty.RegisterReadOnly( "IsDragging", typeof( bool ), typeof( LayoutFloatingWindowControl ),
|
|
new FrameworkPropertyMetadata( ( bool )false, new PropertyChangedCallback( OnIsDraggingChanged ) ) );
|
|
|
|
public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty;
|
|
|
|
/// <summary>
|
|
/// Gets the IsDragging property. This dependency property
|
|
/// indicates that this floating window is being dragged.
|
|
/// </summary>
|
|
public bool IsDragging
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( IsDraggingProperty );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides a secure method for setting the IsDragging property.
|
|
/// This dependency property indicates that this floating window is being dragged.
|
|
/// </summary>
|
|
/// <param name="value">The new value for the property.</param>
|
|
protected void SetIsDragging( bool value )
|
|
{
|
|
SetValue( IsDraggingPropertyKey, value );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles changes to the IsDragging property.
|
|
/// </summary>
|
|
private static void OnIsDraggingChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
( ( LayoutFloatingWindowControl )d ).OnIsDraggingChanged( e );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides derived classes an opportunity to handle changes to the IsDragging property.
|
|
/// </summary>
|
|
protected virtual void OnIsDraggingChanged( DependencyPropertyChangedEventArgs e )
|
|
{
|
|
if( ( bool )e.NewValue )
|
|
{
|
|
CaptureMouse();
|
|
}
|
|
else
|
|
{
|
|
ReleaseMouseCapture();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CloseInitiatedByUser
|
|
|
|
protected bool CloseInitiatedByUser
|
|
{
|
|
get
|
|
{
|
|
return !_internalCloseFlag;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region KeepContentVisibleOnClose
|
|
|
|
internal bool KeepContentVisibleOnClose
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IsMaximized
|
|
|
|
/// <summary>
|
|
/// IsMaximized Dependency Property
|
|
/// </summary>
|
|
public static readonly DependencyProperty IsMaximizedProperty = DependencyProperty.Register( "IsMaximized", typeof( bool ), typeof( LayoutFloatingWindowControl ),
|
|
new FrameworkPropertyMetadata( ( bool )false ) );
|
|
|
|
/// <summary>
|
|
/// Gets/sets the IsMaximized property. This dependency property
|
|
/// indicates if the window is maximized.
|
|
/// </summary>
|
|
public bool IsMaximized
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( IsMaximizedProperty );
|
|
}
|
|
private set
|
|
{
|
|
SetValue( IsMaximizedProperty, value );
|
|
UpdatePositionAndSizeOfPanes();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides a secure method for setting the IsMaximized property.
|
|
/// This dependency property indicates if the window is maximized.
|
|
/// </summary>
|
|
/// <param name="value">The new value for the property.</param>
|
|
|
|
protected override void OnStateChanged( EventArgs e )
|
|
{
|
|
if( !_isInternalChange )
|
|
{
|
|
if( WindowState == WindowState.Maximized )
|
|
{
|
|
UpdateMaximizedState( true );
|
|
}
|
|
else
|
|
{
|
|
WindowState = IsMaximized ? WindowState.Maximized : WindowState.Normal;
|
|
}
|
|
}
|
|
|
|
base.OnStateChanged( e );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Overrides
|
|
|
|
protected override void OnClosed( EventArgs e )
|
|
{
|
|
if( Content != null )
|
|
{
|
|
var host = Content as FloatingWindowContentHost;
|
|
host.Dispose();
|
|
|
|
if( _hwndSrc != null )
|
|
{
|
|
_hwndSrc.RemoveHook( _hwndSrcHook );
|
|
_hwndSrc.Dispose();
|
|
_hwndSrc = null;
|
|
}
|
|
}
|
|
|
|
base.OnClosed( e );
|
|
}
|
|
|
|
protected override void OnInitialized( EventArgs e )
|
|
{
|
|
CommandBindings.Add( new CommandBinding( Microsoft.Windows.Shell.SystemCommands.CloseWindowCommand,
|
|
new ExecutedRoutedEventHandler( ( s, args ) => Microsoft.Windows.Shell.SystemCommands.CloseWindow( ( Window )args.Parameter ) ) ) );
|
|
CommandBindings.Add( new CommandBinding( Microsoft.Windows.Shell.SystemCommands.MaximizeWindowCommand,
|
|
new ExecutedRoutedEventHandler( ( s, args ) => Microsoft.Windows.Shell.SystemCommands.MaximizeWindow( ( Window )args.Parameter ) ) ) );
|
|
CommandBindings.Add( new CommandBinding( Microsoft.Windows.Shell.SystemCommands.MinimizeWindowCommand,
|
|
new ExecutedRoutedEventHandler( ( s, args ) => Microsoft.Windows.Shell.SystemCommands.MinimizeWindow( ( Window )args.Parameter ) ) ) );
|
|
CommandBindings.Add( new CommandBinding( Microsoft.Windows.Shell.SystemCommands.RestoreWindowCommand,
|
|
new ExecutedRoutedEventHandler( ( s, args ) => Microsoft.Windows.Shell.SystemCommands.RestoreWindow( ( Window )args.Parameter ) ) ) );
|
|
//Debug.Assert(this.Owner != null);
|
|
base.OnInitialized( e );
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Internal Methods
|
|
|
|
internal virtual void UpdateThemeResources( Theme oldTheme = null )
|
|
{
|
|
if( oldTheme != null )
|
|
{
|
|
if( oldTheme is DictionaryTheme )
|
|
{
|
|
if( currentThemeResourceDictionary != null )
|
|
{
|
|
Resources.MergedDictionaries.Remove( currentThemeResourceDictionary );
|
|
currentThemeResourceDictionary = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var resourceDictionaryToRemove =
|
|
Resources.MergedDictionaries.FirstOrDefault( r => r.Source == oldTheme.GetResourceUri() );
|
|
if( resourceDictionaryToRemove != null )
|
|
Resources.MergedDictionaries.Remove(
|
|
resourceDictionaryToRemove );
|
|
}
|
|
}
|
|
|
|
var manager = _model.Root.Manager;
|
|
if( manager.Theme != null )
|
|
{
|
|
if( manager.Theme is DictionaryTheme )
|
|
{
|
|
currentThemeResourceDictionary = ( ( DictionaryTheme )manager.Theme ).ThemeResourceDictionary;
|
|
Resources.MergedDictionaries.Add( currentThemeResourceDictionary );
|
|
}
|
|
else
|
|
{
|
|
Resources.MergedDictionaries.Add( new ResourceDictionary() { Source = manager.Theme.GetResourceUri() } );
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void AttachDrag( bool onActivated = true )
|
|
{
|
|
if( onActivated )
|
|
{
|
|
_attachDrag = true;
|
|
this.Activated += new EventHandler( OnActivated );
|
|
}
|
|
else
|
|
{
|
|
IntPtr windowHandle = new WindowInteropHelper( this ).Handle;
|
|
IntPtr lParam = new IntPtr( ( ( int )Left & ( int )0xFFFF ) | ( ( ( int )Top ) << 16 ) );
|
|
Win32Helper.SendMessage( windowHandle, Win32Helper.WM_NCLBUTTONDOWN, new IntPtr( Win32Helper.HT_CAPTION ), lParam );
|
|
}
|
|
}
|
|
|
|
protected virtual IntPtr FilterMessage( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
|
|
{
|
|
handled = false;
|
|
|
|
switch( msg )
|
|
{
|
|
case Win32Helper.WM_ACTIVATE:
|
|
if( ( ( int )wParam & 0xFFFF ) == Win32Helper.WA_INACTIVE )
|
|
{
|
|
if( lParam == this.GetParentWindowHandle() )
|
|
{
|
|
Win32Helper.SetActiveWindow( _hwndSrc.Handle );
|
|
handled = true;
|
|
}
|
|
}
|
|
break;
|
|
case Win32Helper.WM_EXITSIZEMOVE:
|
|
UpdatePositionAndSizeOfPanes();
|
|
|
|
if( _dragService != null )
|
|
{
|
|
bool dropFlag;
|
|
var mousePosition = this.TransformToDeviceDPI( Win32Helper.GetMousePosition() );
|
|
_dragService.Drop( mousePosition, out dropFlag );
|
|
_dragService = null;
|
|
SetIsDragging( false );
|
|
|
|
if( dropFlag )
|
|
InternalClose();
|
|
}
|
|
|
|
break;
|
|
case Win32Helper.WM_MOVING:
|
|
{
|
|
UpdateDragPosition();
|
|
if( this.IsMaximized )
|
|
{
|
|
this.UpdateMaximizedState( false );
|
|
}
|
|
}
|
|
break;
|
|
case Win32Helper.WM_LBUTTONUP: //set as handled right button click on title area (after showing context menu)
|
|
if( _dragService != null && Mouse.LeftButton == MouseButtonState.Released )
|
|
{
|
|
_dragService.Abort();
|
|
_dragService = null;
|
|
SetIsDragging( false );
|
|
}
|
|
break;
|
|
case Win32Helper.WM_SYSCOMMAND:
|
|
int command = ( int )wParam & 0xFFF0;
|
|
if( command == Win32Helper.SC_MAXIMIZE || command == Win32Helper.SC_RESTORE )
|
|
{
|
|
UpdateMaximizedState( command == Win32Helper.SC_MAXIMIZE );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
internal void InternalClose()
|
|
{
|
|
_internalCloseFlag = true;
|
|
Close();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
private static object CoerceContentValue( DependencyObject sender, object content )
|
|
{
|
|
return new FloatingWindowContentHost( sender as LayoutFloatingWindowControl ) { Content = content as UIElement };
|
|
}
|
|
|
|
private void OnLoaded( object sender, RoutedEventArgs e )
|
|
{
|
|
this.Loaded -= new RoutedEventHandler( OnLoaded );
|
|
|
|
this.SetParentToMainWindowOf( Model.Root.Manager );
|
|
|
|
_hwndSrc = HwndSource.FromDependencyObject( this ) as HwndSource;
|
|
_hwndSrcHook = new HwndSourceHook( FilterMessage );
|
|
_hwndSrc.AddHook( _hwndSrcHook );
|
|
|
|
// Restore maximize state
|
|
var maximized = Model.Descendents().OfType<ILayoutElementForFloatingWindow>().Any( l => l.IsMaximized );
|
|
UpdateMaximizedState( maximized );
|
|
}
|
|
|
|
private void OnUnloaded( object sender, RoutedEventArgs e )
|
|
{
|
|
this.Unloaded -= new RoutedEventHandler( OnUnloaded );
|
|
|
|
if( _hwndSrc != null )
|
|
{
|
|
_hwndSrc.RemoveHook( _hwndSrcHook );
|
|
InternalClose();
|
|
}
|
|
}
|
|
|
|
private void OnActivated( object sender, EventArgs e )
|
|
{
|
|
this.Activated -= new EventHandler( OnActivated );
|
|
|
|
if( _attachDrag && Mouse.LeftButton == MouseButtonState.Pressed )
|
|
{
|
|
IntPtr windowHandle = new WindowInteropHelper( this ).Handle;
|
|
var mousePosition = this.PointToScreenDPI( Mouse.GetPosition( this ) );
|
|
var clientArea = Win32Helper.GetClientRect( windowHandle );
|
|
var windowArea = Win32Helper.GetWindowRect( windowHandle );
|
|
|
|
Left = mousePosition.X - windowArea.Width / 2.0;
|
|
Top = mousePosition.Y - ( windowArea.Height - clientArea.Height ) / 2.0;
|
|
_attachDrag = false;
|
|
|
|
IntPtr lParam = new IntPtr( ( ( int )mousePosition.X & ( int )0xFFFF ) | ( ( ( int )mousePosition.Y ) << 16 ) );
|
|
Win32Helper.SendMessage( windowHandle, Win32Helper.WM_NCLBUTTONDOWN, new IntPtr( Win32Helper.HT_CAPTION ), lParam );
|
|
}
|
|
}
|
|
|
|
private void UpdatePositionAndSizeOfPanes()
|
|
{
|
|
foreach( var posElement in Model.Descendents().OfType<ILayoutElementForFloatingWindow>() )
|
|
{
|
|
posElement.FloatingLeft = Left;
|
|
posElement.FloatingTop = Top;
|
|
posElement.FloatingWidth = Width;
|
|
posElement.FloatingHeight = Height;
|
|
}
|
|
}
|
|
|
|
private void UpdateMaximizedState( bool isMaximized )
|
|
{
|
|
foreach( var posElement in Model.Descendents().OfType<ILayoutElementForFloatingWindow>() )
|
|
{
|
|
posElement.IsMaximized = isMaximized;
|
|
}
|
|
IsMaximized = isMaximized;
|
|
_isInternalChange = true;
|
|
WindowState = isMaximized ? WindowState.Maximized : WindowState.Normal;
|
|
_isInternalChange = false;
|
|
}
|
|
|
|
private void UpdateDragPosition()
|
|
{
|
|
if( _dragService == null )
|
|
{
|
|
_dragService = new DragService( this );
|
|
SetIsDragging( true );
|
|
}
|
|
|
|
var mousePosition = this.TransformToDeviceDPI( Win32Helper.GetMousePosition() );
|
|
_dragService.UpdateMouseLocation( mousePosition );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Classes
|
|
|
|
protected internal class FloatingWindowContentHost : HwndHost
|
|
{
|
|
#region Members
|
|
|
|
private LayoutFloatingWindowControl _owner;
|
|
private HwndSource _wpfContentHost = null;
|
|
private Border _rootPresenter = null;
|
|
private DockingManager _manager = null;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public FloatingWindowContentHost( LayoutFloatingWindowControl owner )
|
|
{
|
|
_owner = owner;
|
|
var manager = _owner.Model.Root.Manager;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
#region RootVisual
|
|
|
|
public Visual RootVisual
|
|
{
|
|
get
|
|
{
|
|
return _rootPresenter;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Content
|
|
|
|
/// <summary>
|
|
/// Content Dependency Property
|
|
/// </summary>
|
|
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( "Content", typeof( UIElement ), typeof( FloatingWindowContentHost ),
|
|
new FrameworkPropertyMetadata( ( UIElement )null, new PropertyChangedCallback( OnContentChanged ) ) );
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Content property. This dependency property
|
|
/// indicates ....
|
|
/// </summary>
|
|
public UIElement Content
|
|
{
|
|
get
|
|
{
|
|
return ( UIElement )GetValue( ContentProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( ContentProperty, value );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles changes to the Content property.
|
|
/// </summary>
|
|
private static void OnContentChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
( ( FloatingWindowContentHost )d ).OnContentChanged( e );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides derived classes an opportunity to handle changes to the Content property.
|
|
/// </summary>
|
|
protected virtual void OnContentChanged( DependencyPropertyChangedEventArgs e )
|
|
{
|
|
if( _rootPresenter != null )
|
|
_rootPresenter.Child = Content;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Overrides
|
|
|
|
protected override System.Runtime.InteropServices.HandleRef BuildWindowCore( System.Runtime.InteropServices.HandleRef hwndParent )
|
|
{
|
|
_wpfContentHost = new HwndSource( new HwndSourceParameters()
|
|
{
|
|
ParentWindow = hwndParent.Handle,
|
|
WindowStyle = Win32Helper.WS_CHILD | Win32Helper.WS_VISIBLE | Win32Helper.WS_CLIPSIBLINGS | Win32Helper.WS_CLIPCHILDREN,
|
|
Width = 1,
|
|
Height = 1
|
|
} );
|
|
|
|
_rootPresenter = new Border() { Child = new AdornerDecorator() { Child = Content }, Focusable = true };
|
|
_rootPresenter.SetBinding( Border.BackgroundProperty, new Binding( "Background" ) { Source = _owner } );
|
|
_wpfContentHost.RootVisual = _rootPresenter;
|
|
_wpfContentHost.SizeToContent = SizeToContent.Manual;
|
|
_manager = _owner.Model.Root.Manager;
|
|
_manager.InternalAddLogicalChild( _rootPresenter );
|
|
|
|
return new HandleRef( this, _wpfContentHost.Handle );
|
|
}
|
|
|
|
protected override void DestroyWindowCore( HandleRef hwnd )
|
|
{
|
|
_manager.InternalRemoveLogicalChild( _rootPresenter );
|
|
if( _wpfContentHost != null )
|
|
{
|
|
_wpfContentHost.Dispose();
|
|
_wpfContentHost = null;
|
|
}
|
|
}
|
|
|
|
protected override Size MeasureOverride( Size constraint )
|
|
{
|
|
if( Content == null )
|
|
return base.MeasureOverride( constraint );
|
|
|
|
Content.Measure( constraint );
|
|
return Content.DesiredSize;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|