/************************************************************************************* 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; 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 OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) { Trace.WriteLine("FloatingWindowContentHost.GotKeyboardFocus"); base.OnGotKeyboardFocus(e); } protected override void DestroyWindowCore(HandleRef hwnd) { _manager.InternalRemoveLogicalChild(_rootPresenter); if (_wpfContentHost != null) { _wpfContentHost.Dispose(); _wpfContentHost = null; } } protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case Win32Helper.WM_SETFOCUS: Trace.WriteLine("FloatingWindowContentHost.WM_SETFOCUS"); break; case Win32Helper.WM_KILLFOCUS: Trace.WriteLine("FloatingWindowContentHost.WM_KILLFOCUS"); break; } return base.WndProc(hwnd, msg, wParam, lParam, ref handled); } 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 /// /// Content Dependency Property /// public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(UIElement), typeof(FloatingWindowContentHost), new FrameworkPropertyMetadata((UIElement)null, new PropertyChangedCallback(OnContentChanged))); /// /// Gets or sets the Content property. This dependency property /// indicates .... /// public UIElement Content { get { return (UIElement)GetValue(ContentProperty); } set { SetValue(ContentProperty, value); } } /// /// Handles changes to the Content property. /// private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((FloatingWindowContentHost)d).OnContentChanged(e); } /// /// Provides derived classes an opportunity to handle changes to the Content property. /// 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); _hwndSrc.Dispose(); _hwndSrc = null; } } 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 /// /// IsDragging Read-Only Dependency Property /// 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; /// /// Gets the IsDragging property. This dependency property /// indicates that this floating window is being dragged. /// public bool IsDragging { get { return (bool)GetValue(IsDraggingProperty); } } /// /// Provides a secure method for setting the IsDragging property. /// This dependency property indicates that this floating window is being dragged. /// /// The new value for the property. protected void SetIsDragging(bool value) { SetValue(IsDraggingPropertyKey, value); } /// /// Handles changes to the IsDragging property. /// private static void OnIsDraggingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((LayoutFloatingWindowControl)d).OnIsDraggingChanged(e); } /// /// Provides derived classes an opportunity to handle changes to the IsDragging property. /// protected virtual void OnIsDraggingChanged(DependencyPropertyChangedEventArgs e) { //Trace.WriteLine("IsDragging={0}", e.NewValue); } #endregion DragService _dragService = null; void UpdatePositionAndSizeOfPanes() { foreach (var posElement in Model.Descendents().OfType()) { posElement.FloatingLeft = Left; posElement.FloatingTop = Top; posElement.FloatingWidth = Width; posElement.FloatingHeight = Height; } } void UpdateMaximizedState( bool isMaximized ) { foreach( var posElement in Model.Descendents().OfType() ) { 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 /// /// IsMaximized Read-Only Dependency Property /// private static readonly DependencyPropertyKey IsMaximizedPropertyKey = DependencyProperty.RegisterReadOnly("IsMaximized", typeof(bool), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata((bool)false)); public static readonly DependencyProperty IsMaximizedProperty = IsMaximizedPropertyKey.DependencyProperty; /// /// Gets the IsMaximized property. This dependency property /// indicates if the window is maximized. /// public bool IsMaximized { get { return (bool)GetValue(IsMaximizedProperty); } } /// /// Provides a secure method for setting the IsMaximized property. /// This dependency property indicates if the window is maximized. /// /// The new value for the property. protected void SetIsMaximized(bool value) { SetValue(IsMaximizedPropertyKey, value); } protected override void OnStateChanged(EventArgs e) { SetIsMaximized(WindowState == System.Windows.WindowState.Maximized); base.OnStateChanged(e); } #endregion } }