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.
523 lines
19 KiB
523 lines
19 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.Text;
|
|
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.Diagnostics;
|
|
using System.Windows.Documents;
|
|
using Xceed.Wpf.AvalonDock.Themes;
|
|
using Microsoft.Windows.Shell;
|
|
|
|
namespace Xceed.Wpf.AvalonDock.Controls
|
|
{
|
|
public abstract class LayoutFloatingWindowControl : Window, ILayoutControl
|
|
{
|
|
private ResourceDictionary currentThemeResourceDictionary; // = null
|
|
|
|
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));
|
|
}
|
|
|
|
static object CoerceContentValue(DependencyObject sender, object content)
|
|
{
|
|
return new FloatingWindowContentHost(sender as LayoutFloatingWindowControl) { Content = content as UIElement };
|
|
}
|
|
|
|
protected class FloatingWindowContentHost : HwndHost
|
|
{
|
|
LayoutFloatingWindowControl _owner;
|
|
public FloatingWindowContentHost(LayoutFloatingWindowControl owner)
|
|
{
|
|
_owner = owner;
|
|
var manager = _owner.Model.Root.Manager;
|
|
}
|
|
|
|
|
|
HwndSource _wpfContentHost = null;
|
|
Border _rootPresenter = null;
|
|
DockingManager _manager = null;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
public Visual RootVisual
|
|
{
|
|
get { return _rootPresenter; }
|
|
}
|
|
|
|
protected override Size MeasureOverride(Size constraint)
|
|
{
|
|
if (Content == null)
|
|
return base.MeasureOverride(constraint);
|
|
|
|
Content.Measure(constraint);
|
|
return Content.DesiredSize;
|
|
}
|
|
|
|
#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
|
|
}
|
|
|
|
ILayoutElement _model;
|
|
|
|
protected LayoutFloatingWindowControl(ILayoutElement model)
|
|
{
|
|
this.Loaded += new RoutedEventHandler(OnLoaded);
|
|
this.Unloaded += new RoutedEventHandler(OnUnloaded);
|
|
_model = model;
|
|
UpdateThemeResources();
|
|
}
|
|
|
|
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() });
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool _attachDrag = false;
|
|
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);
|
|
}
|
|
}
|
|
|
|
HwndSource _hwndSrc;
|
|
HwndSourceHook _hwndSrcHook;
|
|
|
|
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);
|
|
}
|
|
|
|
void OnUnloaded(object sender, RoutedEventArgs e)
|
|
{
|
|
this.Unloaded -= new RoutedEventHandler(OnUnloaded);
|
|
|
|
if (_hwndSrc != null)
|
|
{
|
|
_hwndSrc.RemoveHook(_hwndSrcHook);
|
|
InternalClose();
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
public abstract ILayoutElement Model { get; }
|
|
|
|
|
|
#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
|
|
|
|
DragService _dragService = null;
|
|
|
|
void UpdatePositionAndSizeOfPanes()
|
|
{
|
|
foreach (var posElement in Model.Descendents().OfType<ILayoutElementForFloatingWindow>())
|
|
{
|
|
posElement.FloatingLeft = Left;
|
|
posElement.FloatingTop = Top;
|
|
posElement.FloatingWidth = Width;
|
|
posElement.FloatingHeight = Height;
|
|
}
|
|
}
|
|
|
|
void UpdateMaximizedState( bool isMaximized )
|
|
{
|
|
foreach( var posElement in Model.Descendents().OfType<ILayoutElementForFloatingWindow>() )
|
|
{
|
|
posElement.IsMaximized = isMaximized;
|
|
}
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
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:
|
|
IntPtr wMaximize = new IntPtr( Win32Helper.SC_MAXIMIZE );
|
|
IntPtr wRestore = new IntPtr( Win32Helper.SC_RESTORE );
|
|
if( wParam == wMaximize || wParam == wRestore )
|
|
{
|
|
UpdateMaximizedState( wParam == wMaximize );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
private void UpdateDragPosition()
|
|
{
|
|
if (_dragService == null)
|
|
{
|
|
_dragService = new DragService(this);
|
|
SetIsDragging(true);
|
|
}
|
|
|
|
var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition());
|
|
_dragService.UpdateMouseLocation(mousePosition);
|
|
}
|
|
|
|
bool _internalCloseFlag = false;
|
|
|
|
internal void InternalClose()
|
|
{
|
|
_internalCloseFlag = true;
|
|
Close();
|
|
}
|
|
|
|
|
|
protected bool CloseInitiatedByUser
|
|
{
|
|
get { return !_internalCloseFlag; }
|
|
}
|
|
|
|
internal bool KeepContentVisibleOnClose
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
|
{
|
|
base.OnMouseLeftButtonUp(e);
|
|
}
|
|
|
|
#region IsMaximized
|
|
|
|
/// <summary>
|
|
/// IsMaximized Read-Only Dependency Property
|
|
/// </summary>
|
|
private static readonly DependencyPropertyKey IsMaximizedPropertyKey
|
|
= DependencyProperty.RegisterReadOnly("IsMaximized", typeof(bool), typeof(LayoutFloatingWindowControl),
|
|
new FrameworkPropertyMetadata((bool)false));
|
|
|
|
public static readonly DependencyProperty IsMaximizedProperty
|
|
= IsMaximizedPropertyKey.DependencyProperty;
|
|
|
|
/// <summary>
|
|
/// Gets the IsMaximized property. This dependency property
|
|
/// indicates if the window is maximized.
|
|
/// </summary>
|
|
public bool IsMaximized
|
|
{
|
|
get { return (bool)GetValue(IsMaximizedProperty); }
|
|
}
|
|
|
|
/// <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 void SetIsMaximized(bool value)
|
|
{
|
|
SetValue(IsMaximizedPropertyKey, value);
|
|
}
|
|
|
|
protected override void OnStateChanged(EventArgs e)
|
|
{
|
|
SetIsMaximized(WindowState == System.Windows.WindowState.Maximized);
|
|
base.OnStateChanged(e);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|