diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 74e48082..e2d538c2 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -23,172 +23,172 @@ This program is provided to you under the terms of the Microsoft Public namespace AvalonDock.Controls { - /// - /// - /// - /// Implements an abstraction layer for floating windows that can host other controls - /// (eg. documents and/or ). - /// - /// A floating window can be dragged around independently of the . - /// - /// - /// - public abstract class LayoutFloatingWindowControl : Window, ILayoutControl - { - #region fields - private ResourceDictionary currentThemeResourceDictionary; // = null - private bool _isInternalChange; //false - private readonly ILayoutElement _model; - private bool _attachDrag = false; - private HwndSource _hwndSrc; - private HwndSourceHook _hwndSrcHook; - private DragService _dragService = null; - private bool _internalCloseFlag = false; - private bool _isClosing = false; - - /// - /// Is false until the margins have been found once. - /// - /// + /// + /// + /// + /// Implements an abstraction layer for floating windows that can host other controls + /// (eg. documents and/or ). + /// + /// A floating window can be dragged around independently of the . + /// + /// + /// + public abstract class LayoutFloatingWindowControl : Window, ILayoutControl + { + #region fields + private ResourceDictionary currentThemeResourceDictionary; // = null + private bool _isInternalChange; //false + private readonly ILayoutElement _model; + private bool _attachDrag = false; + private HwndSource _hwndSrc; + private HwndSourceHook _hwndSrcHook; + private DragService _dragService = null; + private bool _internalCloseFlag = false; + private bool _isClosing = false; + + /// + /// Is false until the margins have been found once. + /// + /// private bool _isTotalMarginSet = false; #endregion fields - #region Constructors - - static LayoutFloatingWindowControl() - { - AllowsTransparencyProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(false)); - ContentProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(null, null, CoerceContentValue)); - ShowInTaskbarProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(false)); - WindowStyleProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(WindowStyle.None)); - } - - protected LayoutFloatingWindowControl(ILayoutElement model) - { - Loaded += OnLoaded; - Unloaded += OnUnloaded; - Closing += OnClosing; - SizeChanged += OnSizeChanged; - _model = model; - } - - protected LayoutFloatingWindowControl(ILayoutElement model, bool isContentImmutable) - : this(model) - { - IsContentImmutable = isContentImmutable; - } - - #endregion Constructors - - #region Properties - /// - /// Gets/Sets the X,Y delta between the element being dragged and the - /// mouse position. The value of this property is used during the drag - /// cycle to position the dragged item under the mouse pointer. - /// - /// Set this property on initialization to ensure that - /// the delta between mouse and control being dragged - /// remains constant. - /// - internal Point DragDelta { get; set; } - - public abstract ILayoutElement Model { get; } - - #region IsContentImmutable - - /// dependency property. - public static readonly DependencyProperty IsContentImmutableProperty = DependencyProperty.Register(nameof(IsContentImmutable), typeof(bool), typeof(LayoutFloatingWindowControl), - new FrameworkPropertyMetadata(false)); - - /// - /// Gets/sets the property. This dependency property - /// indicates if the content can be modified. - /// - public bool IsContentImmutable - { - get => (bool)GetValue(IsContentImmutableProperty); - private set => SetValue(IsContentImmutableProperty, value); - } - - #endregion IsContentImmutable - - #region IsDragging - - /// Read-Only dependency property. - private static readonly DependencyPropertyKey IsDraggingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsDragging), typeof(bool), typeof(LayoutFloatingWindowControl), - new FrameworkPropertyMetadata(false, OnIsDraggingChanged)); - - public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty; - - /// - /// Gets the property. This dependency property - /// indicates that this floating window is being dragged. - /// - public bool IsDragging => (bool)GetValue(IsDraggingProperty); - - /// - /// Provides a secure method for setting the 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 property. - private static void OnIsDraggingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((LayoutFloatingWindowControl)d).OnIsDraggingChanged(e); - - /// Provides derived classes an opportunity to handle changes to the property. - protected virtual void OnIsDraggingChanged(DependencyPropertyChangedEventArgs e) - { - if ((bool) e.NewValue) - CaptureMouse(); - else - ReleaseMouseCapture(); - } - - #endregion IsDragging - - #region CloseInitiatedByUser - - protected bool CloseInitiatedByUser => !_internalCloseFlag; - - #endregion CloseInitiatedByUser - - internal bool KeepContentVisibleOnClose { get; set; } - - #region IsMaximized - - /// dependency property. - public static readonly DependencyProperty IsMaximizedProperty = DependencyProperty.Register(nameof(IsMaximized), typeof(bool), typeof(LayoutFloatingWindowControl), - new FrameworkPropertyMetadata(false)); - - /// Gets/sets the property. This dependency property indicates if the window is maximized. - /// Provides a secure method for setting the property. - public bool IsMaximized - { - get => (bool)GetValue(IsMaximizedProperty); - private set - { - SetValue(IsMaximizedProperty, value); - UpdatePositionAndSizeOfPanes(); - } - } - - /// - protected override void OnStateChanged(EventArgs e) - { - if (!_isInternalChange) - UpdateMaximizedState(WindowState == WindowState.Maximized); - - base.OnStateChanged(e); - } - - #endregion IsMaximized + #region Constructors + + static LayoutFloatingWindowControl() + { + AllowsTransparencyProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(false)); + ContentProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(null, null, CoerceContentValue)); + ShowInTaskbarProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(false)); + WindowStyleProperty.OverrideMetadata(typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(WindowStyle.None)); + } + + protected LayoutFloatingWindowControl(ILayoutElement model) + { + Loaded += OnLoaded; + Unloaded += OnUnloaded; + Closing += OnClosing; + SizeChanged += OnSizeChanged; + _model = model; + } + + protected LayoutFloatingWindowControl(ILayoutElement model, bool isContentImmutable) + : this(model) + { + IsContentImmutable = isContentImmutable; + } + + #endregion Constructors + + #region Properties + /// + /// Gets/Sets the X,Y delta between the element being dragged and the + /// mouse position. The value of this property is used during the drag + /// cycle to position the dragged item under the mouse pointer. + /// + /// Set this property on initialization to ensure that + /// the delta between mouse and control being dragged + /// remains constant. + /// + internal Point DragDelta { get; set; } + + public abstract ILayoutElement Model { get; } + + #region IsContentImmutable + + /// dependency property. + public static readonly DependencyProperty IsContentImmutableProperty = DependencyProperty.Register(nameof(IsContentImmutable), typeof(bool), typeof(LayoutFloatingWindowControl), + new FrameworkPropertyMetadata(false)); + + /// + /// Gets/sets the property. This dependency property + /// indicates if the content can be modified. + /// + public bool IsContentImmutable + { + get => (bool)GetValue(IsContentImmutableProperty); + private set => SetValue(IsContentImmutableProperty, value); + } + + #endregion IsContentImmutable + + #region IsDragging + + /// Read-Only dependency property. + private static readonly DependencyPropertyKey IsDraggingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsDragging), typeof(bool), typeof(LayoutFloatingWindowControl), + new FrameworkPropertyMetadata(false, OnIsDraggingChanged)); + + public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty; + + /// + /// Gets the property. This dependency property + /// indicates that this floating window is being dragged. + /// + public bool IsDragging => (bool)GetValue(IsDraggingProperty); + + /// + /// Provides a secure method for setting the 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 property. + private static void OnIsDraggingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((LayoutFloatingWindowControl)d).OnIsDraggingChanged(e); + + /// Provides derived classes an opportunity to handle changes to the property. + protected virtual void OnIsDraggingChanged(DependencyPropertyChangedEventArgs e) + { + if ((bool)e.NewValue) + CaptureMouse(); + else + ReleaseMouseCapture(); + } + + #endregion IsDragging + + #region CloseInitiatedByUser + + protected bool CloseInitiatedByUser => !_internalCloseFlag; + + #endregion CloseInitiatedByUser + + internal bool KeepContentVisibleOnClose { get; set; } + + #region IsMaximized + + /// dependency property. + public static readonly DependencyProperty IsMaximizedProperty = DependencyProperty.Register(nameof(IsMaximized), typeof(bool), typeof(LayoutFloatingWindowControl), + new FrameworkPropertyMetadata(false)); + + /// Gets/sets the property. This dependency property indicates if the window is maximized. + /// Provides a secure method for setting the property. + public bool IsMaximized + { + get => (bool)GetValue(IsMaximizedProperty); + private set + { + SetValue(IsMaximizedProperty, value); + UpdatePositionAndSizeOfPanes(); + } + } + + /// + protected override void OnStateChanged(EventArgs e) + { + if (!_isInternalChange) + UpdateMaximizedState(WindowState == WindowState.Maximized); + + base.OnStateChanged(e); + } + + #endregion IsMaximized #region TotalMargin - private static readonly DependencyPropertyKey TotalMarginPropertyKey = + private static readonly DependencyPropertyKey TotalMarginPropertyKey = DependencyProperty.RegisterReadOnly(nameof(TotalMargin), - typeof(Thickness), + typeof(Thickness), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(default(Thickness))); @@ -200,186 +200,159 @@ protected override void OnStateChanged(EventArgs e) /// The margin is queried from the visual tree the first time it is rendered, zero until the first call of FilterMessage(WM_ACTIVATE) /// public Thickness TotalMargin - { - get { return (Thickness) GetValue(TotalMarginProperty); } + { + get { return (Thickness)GetValue(TotalMarginProperty); } protected set { SetValue(TotalMarginPropertyKey, value); } } - #endregion + #endregion - #region ContentMinHeight - public static readonly DependencyPropertyKey ContentMinHeightPropertyKey = DependencyProperty.RegisterReadOnly( + #region ContentMinHeight + public static readonly DependencyPropertyKey ContentMinHeightPropertyKey = DependencyProperty.RegisterReadOnly( nameof(ContentMinHeight), typeof(double), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(0.0)); public static readonly DependencyProperty ContentMinHeightProperty = ContentMinHeightPropertyKey.DependencyProperty; - /// - /// The MinHeight of the content of the window, will be 0 until the window has been rendered, or if the MinHeight is unset for the content - /// - public double ContentMinHeight + /// + /// The MinHeight of the content of the window, will be 0 until the window has been rendered, or if the MinHeight is unset for the content + /// + public double ContentMinHeight { - get { return (double) GetValue(ContentMinHeightProperty); } + get { return (double)GetValue(ContentMinHeightProperty); } set { SetValue(ContentMinHeightPropertyKey, value); } } - #endregion + #endregion - #region ContentMinWidth - public static readonly DependencyPropertyKey ContentMinWidthPropertyKey = DependencyProperty.RegisterReadOnly( + #region ContentMinWidth + public static readonly DependencyPropertyKey ContentMinWidthPropertyKey = DependencyProperty.RegisterReadOnly( nameof(ContentMinWidth), typeof(double), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(0.0)); public static readonly DependencyProperty ContentMinWidthProperty = ContentMinWidthPropertyKey.DependencyProperty; - /// - /// The MinWidth ocf the content of the window, will be 0 until the window has been rendered, or if the MinWidth is unset for the content - /// - public double ContentMinWidth + /// + /// The MinWidth ocf the content of the window, will be 0 until the window has been rendered, or if the MinWidth is unset for the content + /// + public double ContentMinWidth { - get { return (double) GetValue(ContentMinWidthProperty); } + get { return (double)GetValue(ContentMinWidthProperty); } set { SetValue(ContentMinWidthPropertyKey, value); } } - #endregion + #endregion - #region SetWindowSizeWhenOpened + #endregion Properties - public static readonly DependencyProperty SetWindowSizeWhenOpenedProperty = DependencyProperty.Register( - nameof(SetWindowSizeWhenOpened), typeof(bool), typeof(LayoutFloatingWindowControl), new PropertyMetadata(false, - (sender, args) => + #region Internal Methods + + internal virtual void UpdateThemeResources(Theme oldTheme = null) + { + if (oldTheme != null) + { + if (oldTheme is DictionaryTheme) { - if (args.OldValue != args.NewValue && - sender is LayoutFloatingWindowControl control) + if (currentThemeResourceDictionary != null) { - // This will resize the window when this property is set to true and the window is open - control._isTotalMarginSet = false; + Resources.MergedDictionaries.Remove(currentThemeResourceDictionary); + currentThemeResourceDictionary = null; } - })); - - /// - /// If true the MinHeight and MinWidth of the content will be used together with the margins to determine the initial size of the floating window - /// - /// - /// - /// - public bool SetWindowSizeWhenOpened + } + 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) return; + if (manager.Theme is DictionaryTheme dictionaryTheme) + { + currentThemeResourceDictionary = dictionaryTheme.ThemeResourceDictionary; + Resources.MergedDictionaries.Add(currentThemeResourceDictionary); + } + else + Resources.MergedDictionaries.Add(new ResourceDictionary { Source = manager.Theme.GetResourceUri() }); + } + + internal void AttachDrag(bool onActivated = true) { - get { return (bool) GetValue(SetWindowSizeWhenOpenedProperty); } - set { SetValue(SetWindowSizeWhenOpenedProperty, value); } + if (onActivated) + { + _attachDrag = true; + Activated += OnActivated; + } + else + { + var windowHandle = new WindowInteropHelper(this).Handle; + var lParam = new IntPtr(((int)Left & 0xFFFF) | ((int)Top << 16)); + Win32Helper.SendMessage(windowHandle, Win32Helper.WM_NCLBUTTONDOWN, new IntPtr(Win32Helper.HT_CAPTION), lParam); + } } - #endregion - #endregion Properties - - #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) return; - if (manager.Theme is DictionaryTheme dictionaryTheme) - { - currentThemeResourceDictionary = dictionaryTheme.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; - Activated += OnActivated; - } - else - { - var windowHandle = new WindowInteropHelper(this).Handle; - var lParam = new IntPtr(((int)Left & 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; - } - } + 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; + } + } UpdateWindowsSizeBasedOnMinSize(); break; - case Win32Helper.WM_EXITSIZEMOVE: - UpdatePositionAndSizeOfPanes(); - - if (_dragService != null) - { - var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition()); - _dragService.Drop(mousePosition, out var dropFlag); - _dragService = null; - SetIsDragging(false); - if (dropFlag) InternalClose(); - } - break; - case Win32Helper.WM_MOVING: - { - UpdateDragPosition(); - if (IsMaximized) 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: - var command = (int)wParam & 0xFFF0; - if (command == Win32Helper.SC_MAXIMIZE || command == Win32Helper.SC_RESTORE) UpdateMaximizedState(command == Win32Helper.SC_MAXIMIZE); - break; - } - return IntPtr.Zero; - } - - /// - /// Set the margins of the window control (including the borders of the floating window and the title bar). - /// The result will be stored in _totalMargin. - /// - /// If the control is not loaded _totalMargin will not be set. - private void UpdateMargins() + case Win32Helper.WM_EXITSIZEMOVE: + UpdatePositionAndSizeOfPanes(); + + if (_dragService != null) + { + var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition()); + _dragService.Drop(mousePosition, out var dropFlag); + _dragService = null; + SetIsDragging(false); + if (dropFlag) InternalClose(); + } + break; + case Win32Helper.WM_MOVING: + { + UpdateDragPosition(); + if (IsMaximized) 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: + var command = (int)wParam & 0xFFF0; + if (command == Win32Helper.SC_MAXIMIZE || command == Win32Helper.SC_RESTORE) UpdateMaximizedState(command == Win32Helper.SC_MAXIMIZE); + break; + } + return IntPtr.Zero; + } + + /// + /// Set the margins of the window control (including the borders of the floating window and the title bar). + /// The result will be stored in _totalMargin. + /// + /// If the control is not loaded _totalMargin will not be set. + private void UpdateMargins() { - // The grid with window bar and content + // The grid with window bar and content var grid = this.GetChildrenRecursive() .OfType() .FirstOrDefault(g => g.RowDefinitions.Count > 0); @@ -389,15 +362,15 @@ private void UpdateMargins() if (contentControl == null) return; // The content control in the grid, this has a different tree to walk up - var layoutContent = (LayoutContent)contentControl.Content; - if (grid != null && layoutContent.Content is FrameworkElement content) + var layoutContent = (LayoutContent)contentControl.Content; + if (grid != null && layoutContent.Content is FrameworkElement content) { var parents = content.GetParents().ToArray(); var children = this.GetChildrenRecursive() .TakeWhile(c => c != grid) .ToArray(); - var borders = children - .OfType() + var borders = children + .OfType() .Concat(parents .OfType()) .ToArray(); @@ -411,7 +384,7 @@ private void UpdateMargins() .Concat(parents .OfType()) .ToArray(); - var padding = controls.Sum(b => b.Padding); + var padding = controls.Sum(b => b.Padding); var border = borders.Sum(b => b.BorderThickness); var margin = frameworkElements.Sum(f => f.Margin); margin = margin.Add(padding).Add(border).Add(grid.Margin); @@ -421,30 +394,30 @@ private void UpdateMargins() } } - /// - /// Update the floating window size based on the MinHeight and MinWidth of the content of the control. - /// - /// This will only be run once, when the window is rendered the first time and _totalMargin is identified. - private void UpdateWindowsSizeBasedOnMinSize() + /// + /// Update the floating window size based on the MinHeight and MinWidth of the content of the control. + /// + /// This will only be run once, when the window is rendered the first time and _totalMargin is identified. + private void UpdateWindowsSizeBasedOnMinSize() { if (!_isTotalMarginSet) { UpdateMargins(); - if(_isTotalMarginSet) + if (_isTotalMarginSet) { - // The LayoutAnchorableControl is bound via the ContentPresenter, hence it is best to do below in code and not in a style - // See https://github.com/Dirkster99/AvalonDock/pull/146#issuecomment-609974424 - var layoutContents = this.GetChildrenRecursive() + // The LayoutAnchorableControl is bound via the ContentPresenter, hence it is best to do below in code and not in a style + // See https://github.com/Dirkster99/AvalonDock/pull/146#issuecomment-609974424 + var layoutContents = this.GetChildrenRecursive() .OfType() .Select(c => c.Content) .OfType() .Select(lc => lc.Content); - var contents = layoutContents.OfType(); + var contents = layoutContents.OfType(); foreach (var content in contents) { ContentMinHeight = Math.Max(content.MinHeight, ContentMinHeight); ContentMinWidth = Math.Max(content.MinWidth, ContentMinWidth); - if (SetWindowSizeWhenOpened) + if ((this.Model?.Root?.Manager?.AutoWindowSizeWhenOpened).GetValueOrDefault()) { var parent = content.GetParents() .OfType() @@ -468,297 +441,297 @@ private void UpdateWindowsSizeBasedOnMinSize() } internal void InternalClose(bool closeInitiatedByUser = false) - { - _internalCloseFlag = !closeInitiatedByUser; - if (_isClosing) return; - _isClosing = true; - Close(); - } - - #endregion Internal Methods - - #region Overrides - - /// - protected override void OnClosed(EventArgs e) - { - SizeChanged -= OnSizeChanged; - if (Content != null) - { - (Content as FloatingWindowContentHost)?.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, - (s, args) => Microsoft.Windows.Shell.SystemCommands.CloseWindow((Window)args.Parameter))); - CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.MaximizeWindowCommand, - (s, args) => Microsoft.Windows.Shell.SystemCommands.MaximizeWindow((Window)args.Parameter))); - CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.MinimizeWindowCommand, - (s, args) => Microsoft.Windows.Shell.SystemCommands.MinimizeWindow((Window)args.Parameter))); - CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.RestoreWindowCommand, - (s, args) => Microsoft.Windows.Shell.SystemCommands.RestoreWindow((Window)args.Parameter))); - //Debug.Assert(this.Owner != null); - base.OnInitialized(e); - } - - #endregion Overrides - - #region Private Methods - - private static object CoerceContentValue(DependencyObject sender, object content) - { - if (!(sender is LayoutFloatingWindowControl lfwc)) return null; - if (lfwc.IsLoaded && lfwc.IsContentImmutable) return lfwc.Content; - return new FloatingWindowContentHost((LayoutFloatingWindowControl) sender) { Content = content as UIElement }; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - Loaded -= OnLoaded; - this.SetParentToMainWindowOf(Model.Root.Manager); - _hwndSrc = PresentationSource.FromDependencyObject(this) as HwndSource; - _hwndSrcHook = FilterMessage; - _hwndSrc.AddHook(_hwndSrcHook); - // Restore maximize state - var maximized = Model.Descendents().OfType().Any(l => l.IsMaximized); - UpdateMaximizedState(maximized); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - Unloaded -= OnUnloaded; - if (_hwndSrc == null) return; - _hwndSrc.RemoveHook(_hwndSrcHook); - InternalClose(); - } - - private void OnClosing(object sender, CancelEventArgs e) - { - Closing -= OnClosing; - // If this window was Closed not from InternalClose method, - // mark it as closing to avoid "InvalidOperationException: : Cannot set Visibility to Visible or call Show, ShowDialog, - // Close, or WindowInteropHelper.EnsureHandle while a Window is closing". - if (!_isClosing) _isClosing = true; - } - - private void OnSizeChanged(object sender, SizeChangedEventArgs e) - { - foreach (var posElement in Model.Descendents().OfType()) - { - posElement.FloatingWidth = ActualWidth; - posElement.FloatingHeight = ActualHeight; - posElement.RaiseFloatingPropertiesUpdated(); - } - } - - private void OnActivated(object sender, EventArgs e) - { - Activated -= OnActivated; - - if (!_attachDrag || Mouse.LeftButton != MouseButtonState.Pressed) return; - var windowHandle = new WindowInteropHelper(this).Handle; - var mousePosition = this.PointToScreenDPI(Mouse.GetPosition(this)); - - // BugFix Issue #6 - // This code is initializes the drag when content (document or toolwindow) is dragged - // A second chance back up plan if DragDelta is not set - if (DragDelta == default) DragDelta = new Point(3, 3); - Left = mousePosition.X - DragDelta.X; // BugFix Issue #6 - Top = mousePosition.Y - DragDelta.Y; - _attachDrag = false; - var lParam = new IntPtr(((int)mousePosition.X & 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()) - { - posElement.FloatingLeft = Left; - posElement.FloatingTop = Top; - posElement.FloatingWidth = Width; - posElement.FloatingHeight = Height; - posElement.RaiseFloatingPropertiesUpdated(); - } - } - - private void UpdateMaximizedState(bool isMaximized) - { - foreach (var posElement in Model.Descendents().OfType()) - 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 Private Methods - - public virtual void EnableBindings() - { - } - - public virtual void DisableBindings() - { - } - - #region Internal Classes - - protected internal class FloatingWindowContentHost : HwndHost - { - #region fields - private readonly LayoutFloatingWindowControl _owner; - private HwndSource _wpfContentHost = null; - private Border _rootPresenter = null; - private DockingManager _manager = null; - #endregion fields - - #region Constructors - - public FloatingWindowContentHost(LayoutFloatingWindowControl owner) - { - _owner = owner; - var binding = new Binding(nameof(SizeToContent)) { Source = _owner }; - BindingOperations.SetBinding(this, SizeToContentProperty, binding); - } - - #endregion Constructors - - #region Properties - - public Visual RootVisual => _rootPresenter; - - #region Content - - /// dependency property. - public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(UIElement), typeof(FloatingWindowContentHost), - new FrameworkPropertyMetadata(null, OnContentChanged)); - - /// - /// Gets or sets the property. This dependency property - /// indicates .... - /// - public UIElement Content - { - get => (UIElement)GetValue(ContentProperty); - set => SetValue(ContentProperty, value); - } - - /// Handles changes to the property. - private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((FloatingWindowContentHost)d).OnContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue); - - /// Provides derived classes an opportunity to handle changes to the property. - protected virtual void OnContentChanged(UIElement oldValue, UIElement newValue) - { - if (_rootPresenter != null) _rootPresenter.Child = Content; - if (oldValue is FrameworkElement oldContent) oldContent.SizeChanged -= Content_SizeChanged; - if (newValue is FrameworkElement newContent) newContent.SizeChanged += Content_SizeChanged; - } - - #endregion Content - - #region SizeToContent - - /// dependency property. - public static readonly DependencyProperty SizeToContentProperty = DependencyProperty.Register(nameof(SizeToContent), typeof(SizeToContent), typeof(FloatingWindowContentHost), - new FrameworkPropertyMetadata(SizeToContent.Manual, OnSizeToContentChanged)); - - /// Gets or sets the property. - public SizeToContent SizeToContent - { - get => (SizeToContent)GetValue(SizeToContentProperty); - set => SetValue(SizeToContentProperty, value); - } - - /// Handles changes to the property. - private static void OnSizeToContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((FloatingWindowContentHost)d).OnSizeToContentChanged((SizeToContent)e.OldValue, (SizeToContent)e.NewValue); - - /// Provides derived classes an opportunity to handle changes to the property. - protected virtual void OnSizeToContentChanged(SizeToContent oldValue, SizeToContent newValue) - { - if (_wpfContentHost != null) _wpfContentHost.SizeToContent = newValue; - } - - #endregion SizeToContent - - #endregion Properties - - #region Overrides - - /// - protected override HandleRef BuildWindowCore(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, - UsesPerPixelOpacity = true, - }); - - _rootPresenter = new Border { Child = new AdornerDecorator { Child = Content }, Focusable = true }; - _rootPresenter.SetBinding(Border.BackgroundProperty, new Binding(nameof(Background)) { Source = _owner }); - _wpfContentHost.RootVisual = _rootPresenter; - _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) return; - _wpfContentHost.Dispose(); - _wpfContentHost = null; - } - - /// - protected override Size MeasureOverride(Size constraint) - { - if (Content == null) return base.MeasureOverride(constraint); - Content.Measure(constraint); - return Content.DesiredSize; - } - - #endregion Overrides - - #region Methods - - /// - /// Content_SizeChanged event handler. - /// - private void Content_SizeChanged(object sender, SizeChangedEventArgs e) - { - InvalidateMeasure(); - InvalidateArrange(); - } - - #endregion Methods - } - - #endregion Internal Classes - } + { + _internalCloseFlag = !closeInitiatedByUser; + if (_isClosing) return; + _isClosing = true; + Close(); + } + + #endregion Internal Methods + + #region Overrides + + /// + protected override void OnClosed(EventArgs e) + { + SizeChanged -= OnSizeChanged; + if (Content != null) + { + (Content as FloatingWindowContentHost)?.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, + (s, args) => Microsoft.Windows.Shell.SystemCommands.CloseWindow((Window)args.Parameter))); + CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.MaximizeWindowCommand, + (s, args) => Microsoft.Windows.Shell.SystemCommands.MaximizeWindow((Window)args.Parameter))); + CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.MinimizeWindowCommand, + (s, args) => Microsoft.Windows.Shell.SystemCommands.MinimizeWindow((Window)args.Parameter))); + CommandBindings.Add(new CommandBinding(Microsoft.Windows.Shell.SystemCommands.RestoreWindowCommand, + (s, args) => Microsoft.Windows.Shell.SystemCommands.RestoreWindow((Window)args.Parameter))); + //Debug.Assert(this.Owner != null); + base.OnInitialized(e); + } + + #endregion Overrides + + #region Private Methods + + private static object CoerceContentValue(DependencyObject sender, object content) + { + if (!(sender is LayoutFloatingWindowControl lfwc)) return null; + if (lfwc.IsLoaded && lfwc.IsContentImmutable) return lfwc.Content; + return new FloatingWindowContentHost((LayoutFloatingWindowControl)sender) { Content = content as UIElement }; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + Loaded -= OnLoaded; + this.SetParentToMainWindowOf(Model.Root.Manager); + _hwndSrc = PresentationSource.FromDependencyObject(this) as HwndSource; + _hwndSrcHook = FilterMessage; + _hwndSrc.AddHook(_hwndSrcHook); + // Restore maximize state + var maximized = Model.Descendents().OfType().Any(l => l.IsMaximized); + UpdateMaximizedState(maximized); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + Unloaded -= OnUnloaded; + if (_hwndSrc == null) return; + _hwndSrc.RemoveHook(_hwndSrcHook); + InternalClose(); + } + + private void OnClosing(object sender, CancelEventArgs e) + { + Closing -= OnClosing; + // If this window was Closed not from InternalClose method, + // mark it as closing to avoid "InvalidOperationException: : Cannot set Visibility to Visible or call Show, ShowDialog, + // Close, or WindowInteropHelper.EnsureHandle while a Window is closing". + if (!_isClosing) _isClosing = true; + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + foreach (var posElement in Model.Descendents().OfType()) + { + posElement.FloatingWidth = ActualWidth; + posElement.FloatingHeight = ActualHeight; + posElement.RaiseFloatingPropertiesUpdated(); + } + } + + private void OnActivated(object sender, EventArgs e) + { + Activated -= OnActivated; + + if (!_attachDrag || Mouse.LeftButton != MouseButtonState.Pressed) return; + var windowHandle = new WindowInteropHelper(this).Handle; + var mousePosition = this.PointToScreenDPI(Mouse.GetPosition(this)); + + // BugFix Issue #6 + // This code is initializes the drag when content (document or toolwindow) is dragged + // A second chance back up plan if DragDelta is not set + if (DragDelta == default) DragDelta = new Point(3, 3); + Left = mousePosition.X - DragDelta.X; // BugFix Issue #6 + Top = mousePosition.Y - DragDelta.Y; + _attachDrag = false; + var lParam = new IntPtr(((int)mousePosition.X & 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()) + { + posElement.FloatingLeft = Left; + posElement.FloatingTop = Top; + posElement.FloatingWidth = Width; + posElement.FloatingHeight = Height; + posElement.RaiseFloatingPropertiesUpdated(); + } + } + + private void UpdateMaximizedState(bool isMaximized) + { + foreach (var posElement in Model.Descendents().OfType()) + 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 Private Methods + + public virtual void EnableBindings() + { + } + + public virtual void DisableBindings() + { + } + + #region Internal Classes + + protected internal class FloatingWindowContentHost : HwndHost + { + #region fields + private readonly LayoutFloatingWindowControl _owner; + private HwndSource _wpfContentHost = null; + private Border _rootPresenter = null; + private DockingManager _manager = null; + #endregion fields + + #region Constructors + + public FloatingWindowContentHost(LayoutFloatingWindowControl owner) + { + _owner = owner; + var binding = new Binding(nameof(SizeToContent)) { Source = _owner }; + BindingOperations.SetBinding(this, SizeToContentProperty, binding); + } + + #endregion Constructors + + #region Properties + + public Visual RootVisual => _rootPresenter; + + #region Content + + /// dependency property. + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(UIElement), typeof(FloatingWindowContentHost), + new FrameworkPropertyMetadata(null, OnContentChanged)); + + /// + /// Gets or sets the property. This dependency property + /// indicates .... + /// + public UIElement Content + { + get => (UIElement)GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + /// Handles changes to the property. + private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((FloatingWindowContentHost)d).OnContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue); + + /// Provides derived classes an opportunity to handle changes to the property. + protected virtual void OnContentChanged(UIElement oldValue, UIElement newValue) + { + if (_rootPresenter != null) _rootPresenter.Child = Content; + if (oldValue is FrameworkElement oldContent) oldContent.SizeChanged -= Content_SizeChanged; + if (newValue is FrameworkElement newContent) newContent.SizeChanged += Content_SizeChanged; + } + + #endregion Content + + #region SizeToContent + + /// dependency property. + public static readonly DependencyProperty SizeToContentProperty = DependencyProperty.Register(nameof(SizeToContent), typeof(SizeToContent), typeof(FloatingWindowContentHost), + new FrameworkPropertyMetadata(SizeToContent.Manual, OnSizeToContentChanged)); + + /// Gets or sets the property. + public SizeToContent SizeToContent + { + get => (SizeToContent)GetValue(SizeToContentProperty); + set => SetValue(SizeToContentProperty, value); + } + + /// Handles changes to the property. + private static void OnSizeToContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((FloatingWindowContentHost)d).OnSizeToContentChanged((SizeToContent)e.OldValue, (SizeToContent)e.NewValue); + + /// Provides derived classes an opportunity to handle changes to the property. + protected virtual void OnSizeToContentChanged(SizeToContent oldValue, SizeToContent newValue) + { + if (_wpfContentHost != null) _wpfContentHost.SizeToContent = newValue; + } + + #endregion SizeToContent + + #endregion Properties + + #region Overrides + + /// + protected override HandleRef BuildWindowCore(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, + UsesPerPixelOpacity = true, + }); + + _rootPresenter = new Border { Child = new AdornerDecorator { Child = Content }, Focusable = true }; + _rootPresenter.SetBinding(Border.BackgroundProperty, new Binding(nameof(Background)) { Source = _owner }); + _wpfContentHost.RootVisual = _rootPresenter; + _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) return; + _wpfContentHost.Dispose(); + _wpfContentHost = null; + } + + /// + protected override Size MeasureOverride(Size constraint) + { + if (Content == null) return base.MeasureOverride(constraint); + Content.Measure(constraint); + return Content.DesiredSize; + } + + #endregion Overrides + + #region Methods + + /// + /// Content_SizeChanged event handler. + /// + private void Content_SizeChanged(object sender, SizeChangedEventArgs e) + { + InvalidateMeasure(); + InvalidateArrange(); + } + + #endregion Methods + } + + #endregion Internal Classes + } } diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index fe45fac0..ee78d7dd 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1341,6 +1341,25 @@ public bool AllowMixedOrientation #endregion IsVirtualizingLayoutDocument IsVirtualizingLayoutAnchorable + + #region AutoWindowSizeWhenOpened + + /// + /// Gets/sets the float window is auto size when dragged out + /// + [Bindable(true), Description("Gets/sets the floating window is auto size when floated out"), Category("FloatingWindow")] + public bool AutoWindowSizeWhenOpened + { + get { return (bool)GetValue(AutoWindowSizeWhenOpenedProperty); } + set { SetValue(AutoWindowSizeWhenOpenedProperty, value); } + } + + public static readonly DependencyProperty AutoWindowSizeWhenOpenedProperty = + DependencyProperty.Register("AutoWindowSizeWhenOpened", typeof(bool), typeof(DockingManager), new PropertyMetadata(false)); + + + #endregion AutoWindowSizeWhenOpened + #endregion Public Properties #region Private Properties diff --git a/source/TestApp/App.xaml b/source/TestApp/App.xaml index b6cf5818..687deff4 100644 --- a/source/TestApp/App.xaml +++ b/source/TestApp/App.xaml @@ -25,7 +25,7 @@ Set this property to true to auto-size the demo LayoutAnchorable to the size of its containing control when being dragged into floating mode --> - + diff --git a/source/TestApp/MainWindow.xaml b/source/TestApp/MainWindow.xaml index 6a4da229..24fbd34a 100644 --- a/source/TestApp/MainWindow.xaml +++ b/source/TestApp/MainWindow.xaml @@ -64,6 +64,7 @@ x:Name="dockManager" Grid.Row="1" AllowMixedOrientation="True" + AutoWindowSizeWhenOpened="True" DocumentClosing="DockManager_DocumentClosing" IsVirtualizingAnchorable="True" IsVirtualizingDocument="True">