From fff172f3622f8c3bc1073341064d16744e5c2882 Mon Sep 17 00:00:00 2001 From: mpondo <565423+mpondo@users.noreply.github.com> Date: Wed, 4 May 2022 10:54:20 -0400 Subject: [PATCH 01/53] Fix window going behind main window when dragging. --- .../LayoutAnchorableFloatingWindowControl.cs | 18 ++++++++++++++---- .../LayoutDocumentFloatingWindowControl.cs | 18 ++++++++++++++---- source/Components/AvalonDock/DockingManager.cs | 15 ++++++++++++--- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 73c3aa3c..8f84e548 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -162,7 +162,7 @@ void IOverlayWindowHost.HideOverlayWindow() IOverlayWindow IOverlayWindowHost.ShowOverlayWindow(LayoutFloatingWindowControl draggingWindow) { - CreateOverlayWindow(); + CreateOverlayWindow(draggingWindow); _overlayWindow.EnableDropTargets(); _overlayWindow.Show(); return _overlayWindow; @@ -295,10 +295,20 @@ private void _model_IsVisibleChanged(object sender, EventArgs e) if (!IsVisible && _model.IsVisible) Show(); } - private void CreateOverlayWindow() + private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow) { if (_overlayWindow == null) _overlayWindow = new OverlayWindow(this); - _overlayWindow.Owner = Window.GetWindow(this); + + // Usually, the overlay window is made a child of the main window. However, if the floating + // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow + // is set to false to allow the parent window to be minimized independently of floating windows), + // this causes the floating window to be moved behind the main window. Just not setting the parent + // seems to work acceptably here in that case. + if (draggingWindow?.OwnedByDockingManagerWindow ?? true) + _overlayWindow.Owner = Window.GetWindow(this); + else + _overlayWindow.Owner = null; + var rectWindow = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); _overlayWindow.Left = rectWindow.Left; _overlayWindow.Top = rectWindow.Top; @@ -417,4 +427,4 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index db93a0f1..c394fbea 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -225,10 +225,20 @@ bool HitTest(Point dragPoint) private OverlayWindow _overlayWindow = null; - private void CreateOverlayWindow() + private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow) { if (_overlayWindow == null) _overlayWindow = new OverlayWindow(this); - _overlayWindow.Owner = Window.GetWindow(this); + + // Usually, the overlay window is made a child of the main window. However, if the floating + // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow + // is set to false to allow the parent window to be minimized independently of floating windows), + // this causes the floating window to be moved behind the main window. Just not setting the parent + // seems to work acceptably here in that case. + if (draggingWindow?.OwnedByDockingManagerWindow ?? true) + _overlayWindow.Owner = Window.GetWindow(this); + else + _overlayWindow.Owner = null; + var rectWindow = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); _overlayWindow.Left = rectWindow.Left; _overlayWindow.Top = rectWindow.Top; @@ -238,7 +248,7 @@ private void CreateOverlayWindow() IOverlayWindow IOverlayWindowHost.ShowOverlayWindow(LayoutFloatingWindowControl draggingWindow) { - CreateOverlayWindow(); + CreateOverlayWindow(draggingWindow); _overlayWindow.EnableDropTargets(); _overlayWindow.Show(); return _overlayWindow; @@ -419,4 +429,4 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 7ccb27ba..2d0ac381 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1436,7 +1436,7 @@ bool HitTest(Point dragPoint) /// IOverlayWindow IOverlayWindowHost.ShowOverlayWindow(LayoutFloatingWindowControl draggingWindow) { - CreateOverlayWindow(); + CreateOverlayWindow(draggingWindow); _overlayWindow.EnableDropTargets(); _overlayWindow.Show(); return _overlayWindow; @@ -2095,13 +2095,22 @@ private void SetupAutoHideWindow() SetAutoHideWindow(new LayoutAutoHideWindowControl()); } - private void CreateOverlayWindow() + private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow = null) { if (_overlayWindow == null) { _overlayWindow = new OverlayWindow(this); } - _overlayWindow.Owner = Window.GetWindow(this); + + // Usually, the overlay window is made a child of the main window. However, if the floating + // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow + // is set to false to allow the parent window to be minimized independently of floating windows), + // this causes the floating window to be moved behind the main window. Just not setting the parent + // seems to work acceptably here in that case. + if (draggingWindow?.OwnedByDockingManagerWindow ?? true) + _overlayWindow.Owner = Window.GetWindow(this); + else + _overlayWindow.Owner = null; var rectWindow = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); _overlayWindow.Left = rectWindow.Left; From dfa21a9c5805b6329d43a2da9bb40e3b8836aea5 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 20 Sep 2022 11:55:21 +0200 Subject: [PATCH 02/53] Prevents a known bug in WPF If a childwindow ist opened and closed and this control is closed the main window is minimized The workaround with setting the owner to null is not working, therefor Activate is used https://stackoverflow.com/questions/19156373/closing-child-window-minimizes-parent --- .../Controls/LayoutFloatingWindowControl.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 71d0602e..ce134df3 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -536,6 +536,26 @@ protected override void OnInitialized(EventArgs e) base.OnInitialized(e); } + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + AssureOwnerIsNotMinimized(); + } + + /// + /// Prevents a known bug in WPF, which wronlgy minimizes the parent window, when closing this control + /// + private void AssureOwnerIsNotMinimized() + { + try + { + Owner?.Activate(); + } + catch (Exception) + { + } + } + #endregion Overrides #region Private Methods From ad09dfffe7a03cae0753eeb727c602253723043a Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 31 Oct 2022 21:43:41 +0800 Subject: [PATCH 03/53] Add Null check for GetWindowChrome --- .../LayoutAnchorableFloatingWindowControl.cs | 7 ++++++- .../Controls/LayoutDocumentFloatingWindowControl.cs | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 8f84e548..98b4f87f 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -257,7 +257,12 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int if (wParam.ToInt32() == Win32Helper.HT_CAPTION) { if (OpenContextMenu()) handled = true; - WindowChrome.GetWindowChrome(this).ShowSystemMenu = _model.Root.Manager.ShowSystemMenu && !handled; + + var windowChrome = WindowChrome.GetWindowChrome(this); + if (windowChrome != null) + { + windowChrome.ShowSystemMenu = _model.Root.Manager.ShowSystemMenu && !handled; + } } break; } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index c394fbea..057da3d7 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -137,10 +137,15 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int { if (OpenContextMenu()) handled = true; - if (_model.Root.Manager.ShowSystemMenu) - WindowChrome.GetWindowChrome(this).ShowSystemMenu = !handled; - else - WindowChrome.GetWindowChrome(this).ShowSystemMenu = false; + + var windowChrome = WindowChrome.GetWindowChrome(this); + if (windowChrome != null) + { + if (_model.Root.Manager.ShowSystemMenu) + windowChrome.ShowSystemMenu = !handled; + else + windowChrome.ShowSystemMenu = false; + } } break; From e77aaaf717add5d67e37c9b370a9034727f4e93e Mon Sep 17 00:00:00 2001 From: Noisrev Date: Tue, 1 Nov 2022 18:03:34 +0800 Subject: [PATCH 04/53] Move OpenContextMenu after GetWindowChrome --- .../Controls/LayoutAnchorableFloatingWindowControl.cs | 3 +-- .../Controls/LayoutDocumentFloatingWindowControl.cs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 98b4f87f..5e238fa3 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -256,11 +256,10 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int case Win32Helper.WM_NCRBUTTONUP: if (wParam.ToInt32() == Win32Helper.HT_CAPTION) { - if (OpenContextMenu()) handled = true; - var windowChrome = WindowChrome.GetWindowChrome(this); if (windowChrome != null) { + if (OpenContextMenu()) handled = true; windowChrome.ShowSystemMenu = _model.Root.Manager.ShowSystemMenu && !handled; } } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 057da3d7..92f96376 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -135,12 +135,12 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int case Win32Helper.WM_NCRBUTTONUP: if (wParam.ToInt32() == Win32Helper.HT_CAPTION) { - if (OpenContextMenu()) - handled = true; - var windowChrome = WindowChrome.GetWindowChrome(this); if (windowChrome != null) { + if (OpenContextMenu()) + handled = true; + if (_model.Root.Manager.ShowSystemMenu) windowChrome.ShowSystemMenu = !handled; else From b5a41ef207918da8e5bb0c5653226801d2b34274 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Tue, 1 Nov 2022 20:45:11 +0800 Subject: [PATCH 05/53] Fix the get owner window in the CreateOverlayWindow (https://github.com/Dirkster99/AvalonDock/pull/350) --- .../Controls/LayoutAnchorableFloatingWindowControl.cs | 6 ++---- .../Controls/LayoutDocumentFloatingWindowControl.cs | 7 ++----- source/Components/AvalonDock/DockingManager.cs | 8 +++----- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 8f84e548..59cdde26 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -301,11 +301,9 @@ private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow) // Usually, the overlay window is made a child of the main window. However, if the floating // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow - // is set to false to allow the parent window to be minimized independently of floating windows), - // this causes the floating window to be moved behind the main window. Just not setting the parent - // seems to work acceptably here in that case. + // is set to false to allow the parent window to be minimized independently of floating windows) if (draggingWindow?.OwnedByDockingManagerWindow ?? true) - _overlayWindow.Owner = Window.GetWindow(this); + _overlayWindow.Owner = Window.GetWindow(_model.Root.Manager); else _overlayWindow.Owner = null; diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index c394fbea..50bafc4d 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -16,7 +16,6 @@ This program is provided to you under the terms of the Microsoft Public using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Input; -using System.Windows.Interop; namespace AvalonDock.Controls { @@ -231,11 +230,9 @@ private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow) // Usually, the overlay window is made a child of the main window. However, if the floating // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow - // is set to false to allow the parent window to be minimized independently of floating windows), - // this causes the floating window to be moved behind the main window. Just not setting the parent - // seems to work acceptably here in that case. + // is set to false to allow the parent window to be minimized independently of floating windows) if (draggingWindow?.OwnedByDockingManagerWindow ?? true) - _overlayWindow.Owner = Window.GetWindow(this); + _overlayWindow.Owner = Window.GetWindow(_model.Root.Manager); else _overlayWindow.Owner = null; diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 2d0ac381..58616214 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1621,7 +1621,7 @@ internal UIElement CreateUIElementForModel(ILayoutElement model) { //Owner = Window.GetWindow(this) }; - + newFW.UpdateOwnership(); // Fill list before calling Show (issue #254) @@ -1665,7 +1665,7 @@ internal UIElement CreateUIElementForModel(ILayoutElement model) { //Owner = Window.GetWindow(this) }; - + newFW.UpdateOwnership(); // Fill list before calling Show (issue #254) @@ -2104,9 +2104,7 @@ private void CreateOverlayWindow(LayoutFloatingWindowControl draggingWindow = nu // Usually, the overlay window is made a child of the main window. However, if the floating // window being dragged isn't also a child of the main window (because OwnedByDockingManagerWindow - // is set to false to allow the parent window to be minimized independently of floating windows), - // this causes the floating window to be moved behind the main window. Just not setting the parent - // seems to work acceptably here in that case. + // is set to false to allow the parent window to be minimized independently of floating windows) if (draggingWindow?.OwnedByDockingManagerWindow ?? true) _overlayWindow.Owner = Window.GetWindow(this); else From 67691d2ba067a6c552719c3331208ec74d59a392 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Wed, 2 Nov 2022 23:49:19 +0800 Subject: [PATCH 06/53] Implement new drag and drop handling for Windows with different owners --- .../AvalonDock/Controls/DragService.cs | 77 ++++++++++++++++++- .../Controls/LayoutFloatingWindowControl.cs | 5 ++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index d08f8e34..de0477ad 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -8,9 +8,11 @@ This program is provided to you under the terms of the Microsoft Public ************************************************************************/ using AvalonDock.Layout; +using System; using System.Collections.Generic; using System.Linq; using System.Windows; +using System.Windows.Interop; namespace AvalonDock.Controls { @@ -111,6 +113,14 @@ internal void UpdateMouseLocation(Point dragPosition) _currentWindow.DragLeave(_floatingWindow); if (_currentHost != null) _currentHost.HideOverlayWindow(); + + if (_currentHost is LayoutFloatingWindowControl floatingWindowControl && !floatingWindowControl.OwnedByDockingManagerWindow) + { + floatingWindowControl._canShowDragOnMainWindow = true; + // Update + GetOverlayWindowHosts(); + } + _currentHost = null; } @@ -119,6 +129,35 @@ internal void UpdateMouseLocation(Point dragPosition) _currentHost = newHost; _currentWindow = _currentHost.ShowOverlayWindow(_floatingWindow); _currentWindow.DragEnter(_floatingWindow); + + // Set the target window to topmost + if (_currentHost is LayoutFloatingWindowControl floatingWindowControl) + { + if (!floatingWindowControl.OwnedByDockingManagerWindow && !_floatingWindow.OwnedByDockingManagerWindow) + { + var manager = floatingWindowControl.Model?.Root?.Manager; + if (manager != null) + { + floatingWindowControl._canShowDragOnMainWindow = true; + } + } + + BringWindowToTop2(floatingWindowControl); + } + else if (_currentHost is DockingManager dockingManager) + { + BringWindowToTop2(Window.GetWindow(dockingManager)); + dockingManager.GetFloatingWindowsByZOrder().ForEach((item) => item._canShowDragOnMainWindow = false); + } + + // Update + GetOverlayWindowHosts(); + + BringWindowToTop2(_floatingWindow); + if (_currentWindow is Window overlayWindow) + { + BringWindowToTop2(overlayWindow); + } } } @@ -135,7 +174,6 @@ internal void UpdateMouseLocation(Point dragPosition) List areasToRemove = new List(); _currentWindowAreas.ForEach(a => { - //is mouse still inside this area? if (!a.DetectionRect.Contains(a.TransformToDeviceDPI(dragPosition))) { @@ -197,6 +235,12 @@ internal void Drop(Point dropLocation, out bool dropHandled) if (_currentHost != null) _currentHost.HideOverlayWindow(); + else if (_currentHost is LayoutFloatingWindowControl floatingWindow && !floatingWindow.OwnedByDockingManagerWindow) + { + floatingWindow._canShowDragOnMainWindow = false; + GetOverlayWindowHosts(); + } + if (_currentDropTarget != null) { @@ -209,6 +253,9 @@ internal void Drop(Point dropLocation, out bool dropHandled) if (_currentDropTarget != null) _currentWindow.DragLeave(_currentDropTarget); + else + Win32Helper.BringWindowToTop(new WindowInteropHelper(_floatingWindow).Handle); + if (_currentWindow != null) _currentWindow.DragLeave(_floatingWindow); @@ -246,6 +293,12 @@ internal void Abort() #region Private Methods + private void BringWindowToTop2(Window window) + { + Win32Helper.SetWindowPos(new WindowInteropHelper(window).Handle, + IntPtr.Zero, 0, 0, 0, 0, Win32Helper.SetWindowPosFlags.IgnoreResize | Win32Helper.SetWindowPosFlags.IgnoreMove | Win32Helper.SetWindowPosFlags.DoNotActivate); + } + /// /// Adds s into a private collection of possible /// drop target hosts that can show a drop target button to drop a dragged @@ -256,14 +309,32 @@ private void GetOverlayWindowHosts() { if (_manager.Layout.RootPanel.CanDock) { + _overlayWindowHosts.Clear(); + // Add LayoutFloatingWindowControls as drop target hosts // 1) Don't drop a floating window on to itself // 2) Use this Drop target if its visible - _overlayWindowHosts.AddRange(_manager.GetFloatingWindowsByZOrder().OfType().Where(fw => fw != _floatingWindow && fw.IsVisible)); - _overlayWindowHosts.AddRange(_manager.GetFloatingWindowsByZOrder().OfType().Where(fw => fw != _floatingWindow && fw.IsVisible)); + + var topFloatingWindows = new List(); + var bottomFloatingWindows = new List(); + + foreach (var fw in _manager.GetFloatingWindowsByZOrder()) + { + if (fw != _floatingWindow && fw.IsVisible) + { + if (fw.OwnedByDockingManagerWindow || fw._canShowDragOnMainWindow) + topFloatingWindows.Add(fw as IOverlayWindowHost); + else + bottomFloatingWindows.Add(fw as IOverlayWindowHost); + } + } + + _overlayWindowHosts.AddRange(topFloatingWindows); // Add dockingManager itself as a drop target host _overlayWindowHosts.Add(_manager); + + _overlayWindowHosts.AddRange(bottomFloatingWindows); } } diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 71d0602e..90923c34 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -53,6 +53,11 @@ public abstract class LayoutFloatingWindowControl : Window, ILayoutControl /// private bool _isTotalMarginSet = false; + /// + /// This field is controlled by the DragService and is used to set whether to display in front of the main window. + /// + internal bool _canShowDragOnMainWindow; + #endregion fields #region Constructors From 7b3ada7b558b31ba0054b1ab0136f59360066732 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 3 Nov 2022 16:15:10 +0800 Subject: [PATCH 07/53] Remove **_canShowDragOnMainWindow** and Update DragService - Add **GetWindowZOrder** to Win32Helper - Add **GetOverlayWindowHostsByZOrder** in order to get all the floating window and the child window - Optimize the code in **DragService** --- .../AvalonDock/Controls/DragService.cs | 62 ++----------------- .../Controls/LayoutFloatingWindowControl.cs | 7 +-- .../Components/AvalonDock/DockingManager.cs | 38 ++++++++++++ source/Components/AvalonDock/Win32Helper.cs | 25 +++++++- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index de0477ad..e1e2b2c6 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -89,6 +89,7 @@ internal void UpdateMouseLocation(Point dragPosition) ////var floatingWindowModel = _floatingWindow.Model as LayoutFloatingWindow; // TODO - pass in without DPI adjustment, screen co-ords, adjust inside the target window + GetOverlayWindowHosts(); var newHost = _overlayWindowHosts.FirstOrDefault(oh => oh.HitTestScreen(dragPosition)); if (_currentHost != null || _currentHost != newHost) @@ -114,13 +115,6 @@ internal void UpdateMouseLocation(Point dragPosition) if (_currentHost != null) _currentHost.HideOverlayWindow(); - if (_currentHost is LayoutFloatingWindowControl floatingWindowControl && !floatingWindowControl.OwnedByDockingManagerWindow) - { - floatingWindowControl._canShowDragOnMainWindow = true; - // Update - GetOverlayWindowHosts(); - } - _currentHost = null; } @@ -131,28 +125,16 @@ internal void UpdateMouseLocation(Point dragPosition) _currentWindow.DragEnter(_floatingWindow); // Set the target window to topmost - if (_currentHost is LayoutFloatingWindowControl floatingWindowControl) + if (_currentHost is LayoutFloatingWindowControl fwc && + (fwc.OwnedByDockingManagerWindow == _floatingWindow.OwnedByDockingManagerWindow || fwc.OwnedByDockingManagerWindow)) { - if (!floatingWindowControl.OwnedByDockingManagerWindow && !_floatingWindow.OwnedByDockingManagerWindow) - { - var manager = floatingWindowControl.Model?.Root?.Manager; - if (manager != null) - { - floatingWindowControl._canShowDragOnMainWindow = true; - } - } - - BringWindowToTop2(floatingWindowControl); + BringWindowToTop2(fwc); } else if (_currentHost is DockingManager dockingManager) { BringWindowToTop2(Window.GetWindow(dockingManager)); - dockingManager.GetFloatingWindowsByZOrder().ForEach((item) => item._canShowDragOnMainWindow = false); } - // Update - GetOverlayWindowHosts(); - BringWindowToTop2(_floatingWindow); if (_currentWindow is Window overlayWindow) { @@ -235,12 +217,6 @@ internal void Drop(Point dropLocation, out bool dropHandled) if (_currentHost != null) _currentHost.HideOverlayWindow(); - else if (_currentHost is LayoutFloatingWindowControl floatingWindow && !floatingWindow.OwnedByDockingManagerWindow) - { - floatingWindow._canShowDragOnMainWindow = false; - GetOverlayWindowHosts(); - } - if (_currentDropTarget != null) { @@ -253,9 +229,6 @@ internal void Drop(Point dropLocation, out bool dropHandled) if (_currentDropTarget != null) _currentWindow.DragLeave(_currentDropTarget); - else - Win32Helper.BringWindowToTop(new WindowInteropHelper(_floatingWindow).Handle); - if (_currentWindow != null) _currentWindow.DragLeave(_floatingWindow); @@ -309,32 +282,7 @@ private void GetOverlayWindowHosts() { if (_manager.Layout.RootPanel.CanDock) { - _overlayWindowHosts.Clear(); - - // Add LayoutFloatingWindowControls as drop target hosts - // 1) Don't drop a floating window on to itself - // 2) Use this Drop target if its visible - - var topFloatingWindows = new List(); - var bottomFloatingWindows = new List(); - - foreach (var fw in _manager.GetFloatingWindowsByZOrder()) - { - if (fw != _floatingWindow && fw.IsVisible) - { - if (fw.OwnedByDockingManagerWindow || fw._canShowDragOnMainWindow) - topFloatingWindows.Add(fw as IOverlayWindowHost); - else - bottomFloatingWindows.Add(fw as IOverlayWindowHost); - } - } - - _overlayWindowHosts.AddRange(topFloatingWindows); - - // Add dockingManager itself as a drop target host - _overlayWindowHosts.Add(_manager); - - _overlayWindowHosts.AddRange(bottomFloatingWindows); + _manager.GetOverlayWindowHostsByZOrder(ref _overlayWindowHosts, _floatingWindow); } } diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 90923c34..01b078eb 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -52,12 +52,7 @@ public abstract class LayoutFloatingWindowControl : Window, ILayoutControl /// /// private bool _isTotalMarginSet = false; - - /// - /// This field is controlled by the DragService and is used to set whether to display in front of the main window. - /// - internal bool _canShowDragOnMainWindow; - + #endregion fields #region Constructors diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 58616214..762917bc 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1777,6 +1777,44 @@ internal IEnumerable GetFloatingWindowsByZOrder() } } + internal void GetOverlayWindowHostsByZOrder(ref List overlayWindowHosts, LayoutFloatingWindowControl dragFloatingWindow) + { + overlayWindowHosts.Clear(); + + var topFloatingWindows = new List(); + var bottomFloatingWindows = new List(); + + var parentWindow = Window.GetWindow(this); + var windowParentHandle = parentWindow != null ? new WindowInteropHelper(parentWindow).Handle : Process.GetCurrentProcess().MainWindowHandle; + var b = Win32Helper.GetWindowZOrder(windowParentHandle, out var mainWindow_z); + var currentHandle = Win32Helper.GetWindow(windowParentHandle, (uint)Win32Helper.GetWindow_Cmd.GW_HWNDFIRST); + while (currentHandle != IntPtr.Zero) + { + for (int i = 0; i < _fwList.Count; i++) + { + var fw = _fwList[i]; + if (fw is IOverlayWindowHost host && fw != dragFloatingWindow && fw.IsVisible) + { + var fw_hwnd = new WindowInteropHelper(fw).Handle; + if (currentHandle == fw_hwnd && fw.Model.Root != null && fw.Model.Root.Manager == this) + { + if (fw.OwnedByDockingManagerWindow || (Win32Helper.GetWindowZOrder(fw_hwnd, out var fw_z) && fw_z > mainWindow_z)) + topFloatingWindows.Add(host); + else + bottomFloatingWindows.Add(host); + break; + } + } + } + + currentHandle = Win32Helper.GetWindow(currentHandle, (uint)Win32Helper.GetWindow_Cmd.GW_HWNDNEXT); + } + + overlayWindowHosts.AddRange(topFloatingWindows); + overlayWindowHosts.Add(this); + overlayWindowHosts.AddRange(bottomFloatingWindows); + } + internal void RemoveFloatingWindow(LayoutFloatingWindowControl floatingWindow) => _fwList.Remove(floatingWindow); internal void ExecuteCloseCommand(LayoutDocument document) diff --git a/source/Components/AvalonDock/Win32Helper.cs b/source/Components/AvalonDock/Win32Helper.cs index b700572a..9c66b608 100644 --- a/source/Components/AvalonDock/Win32Helper.cs +++ b/source/Components/AvalonDock/Win32Helper.cs @@ -323,9 +323,6 @@ internal static RECT GetWindowRect(IntPtr hWnd) [DllImport("user32.dll")] internal static extern IntPtr GetTopWindow(IntPtr hWnd); - internal const uint GW_HWNDNEXT = 2; - internal const uint GW_HWNDPREV = 3; - [DllImport("user32.dll", SetLastError = true)] internal static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); @@ -340,6 +337,28 @@ internal enum GetWindow_Cmd : uint GW_ENABLEDPOPUP = 6 } + public static bool GetWindowZOrder(IntPtr hwnd, out int zOrder) + { + var lowestHwnd = GetWindow(hwnd, (uint)GetWindow_Cmd.GW_HWNDLAST); + + var z = 0; + var hwndTmp = lowestHwnd; + while (hwndTmp != IntPtr.Zero) + { + if (hwnd == hwndTmp) + { + zOrder = z; + return true; + } + + hwndTmp = GetWindow(hwndTmp, (uint)GetWindow_Cmd.GW_HWNDPREV); + z++; + } + + zOrder = int.MinValue; + return false; + } + internal static int MakeLParam(int LoWord, int HiWord) { return (int)((HiWord << 16) | (LoWord & 0xffff)); From 015764478a74eef103a911ce86cfbbbf1be95295 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 3 Nov 2022 20:36:11 +0800 Subject: [PATCH 08/53] Add _isDrag to check for drag and update overlay window hosts --- .../Components/AvalonDock/Controls/DragService.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index e1e2b2c6..e67aa61b 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -57,6 +57,7 @@ internal class DragService private IOverlayWindow _currentWindow; private List _currentWindowAreas = new List(); private IDropTarget _currentDropTarget; + private bool _isDrag; #endregion fields @@ -89,7 +90,12 @@ internal void UpdateMouseLocation(Point dragPosition) ////var floatingWindowModel = _floatingWindow.Model as LayoutFloatingWindow; // TODO - pass in without DPI adjustment, screen co-ords, adjust inside the target window - GetOverlayWindowHosts(); + if (!_isDrag) + { + GetOverlayWindowHosts(); + _isDrag = true; + } + var newHost = _overlayWindowHosts.FirstOrDefault(oh => oh.HitTestScreen(dragPosition)); if (_currentHost != null || _currentHost != newHost) @@ -113,7 +119,10 @@ internal void UpdateMouseLocation(Point dragPosition) if (_currentWindow != null) _currentWindow.DragLeave(_floatingWindow); if (_currentHost != null) + { _currentHost.HideOverlayWindow(); + GetOverlayWindowHosts(); + } _currentHost = null; } @@ -135,6 +144,8 @@ internal void UpdateMouseLocation(Point dragPosition) BringWindowToTop2(Window.GetWindow(dockingManager)); } + GetOverlayWindowHosts(); + BringWindowToTop2(_floatingWindow); if (_currentWindow is Window overlayWindow) { @@ -235,6 +246,7 @@ internal void Drop(Point dropLocation, out bool dropHandled) _currentWindow = null; _currentHost = null; + _isDrag = false; } /// From b6c2964fa5c3b3fab6d48b883b28755547b9f682 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 3 Nov 2022 20:44:52 +0800 Subject: [PATCH 09/53] Delete GetOverlayWindowHosts in the DragService constructor --- source/Components/AvalonDock/Controls/DragService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index e67aa61b..2b5d18e8 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -72,8 +72,6 @@ public DragService(LayoutFloatingWindowControl floatingWindow) { _floatingWindow = floatingWindow; _manager = floatingWindow.Model.Root.Manager; - - GetOverlayWindowHosts(); } #endregion Constructors From a9750089375449db8321ad07aeda6ffcf8510b6e Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Fri, 4 Nov 2022 16:51:17 +0100 Subject: [PATCH 10/53] Avalondock Version 4.70.3 --- README.md | 8 ++++++++ .../AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj | 8 ++++---- .../AvalonDock.Themes.Expression.csproj | 8 ++++---- .../AvalonDock.Themes.Metro.csproj | 8 ++++---- .../AvalonDock.Themes.VS2010.csproj | 8 ++++---- .../AvalonDock.Themes.VS2013.csproj | 8 ++++---- source/Components/AvalonDock/AvalonDock.csproj | 8 ++++---- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5647167a..5fdeff4c 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,14 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History +## Fixes Added in Version 4.70.3 + +- [#394 Fix the get owner DockingManagerWindow and Update drag and drop](https://github.com/Dirkster99/AvalonDock/pull/394) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#393 Add Null check for GetWindowChrome](https://github.com/Dirkster99/AvalonDock/pull/393) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#376 Prevents a known bug in WPF](https://github.com/Dirkster99/AvalonDock/pull/376) (thanx to [Ben Buerger](https://github.com/bbuerger)) + ## Fixes Added in Version 4.70.2 - [#338 Fixes #309 Anchorable Header not visible in generic theme](https://github.com/Dirkster99/AvalonDock/pull/338) (thanx to [Darren Gosbell](https://github.com/dgosbell)) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 620bac15..812d2559 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index dfcd8db6..f167e031 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index 377c14e6..d64aaf06 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index aef169ad..09db8298 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index 57009720..68ca2a8d 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index 3c088be9..4b5de5ca 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,10 +6,10 @@ true sn.snk true - 4.70.2 - 4.70.2 - 4.70.2 - 4.70.2 + 4.70.3 + 4.70.3 + 4.70.3 + 4.70.3 Open Source Dirkster.AvalonDock 2017-2022 From 3cd644869f71c2bcf4a6a48c2470a423aa09cda3 Mon Sep 17 00:00:00 2001 From: Denis Smirnov Date: Tue, 11 Oct 2022 10:52:56 +0300 Subject: [PATCH 11/53] set ResizeOverlay 's owner always null --- source/Components/AvalonDock/Controls/LayoutGridControl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutGridControl.cs b/source/Components/AvalonDock/Controls/LayoutGridControl.cs index cd339085..b405a397 100644 --- a/source/Components/AvalonDock/Controls/LayoutGridControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutGridControl.cs @@ -637,10 +637,9 @@ private void ShowResizerOverlayWindow(LayoutGridResizerControl splitter) Left = ptTopLeftScreen.X, Top = ptTopLeftScreen.Y, ShowActivated = false, - Owner = Window.GetWindow(this), + Owner = null, Content = panelHostResizer }; - _resizerWindowHost.Loaded += (s, e) => _resizerWindowHost.SetParentToMainWindowOf(this); _resizerWindowHost.Show(); } From fdf4af76bf0ece1d8c2d743ca354e8fbffdba835 Mon Sep 17 00:00:00 2001 From: Denis Smirnov Date: Tue, 11 Oct 2022 11:17:55 +0300 Subject: [PATCH 12/53] add open/close LayoutFlayoutingWindowsControl events --- .../Components/AvalonDock/DockingManager.cs | 25 ++++++++++++++++++- ...outFloatingWindowControlClosedEventArgs.cs | 16 ++++++++++++ ...utFloatingWindowControlCreatedEventArgs.cs | 16 ++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 source/Components/AvalonDock/LayoutFloatingWindowControlClosedEventArgs.cs create mode 100644 source/Components/AvalonDock/LayoutFloatingWindowControlCreatedEventArgs.cs diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 762917bc..2a47f99e 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -119,6 +119,17 @@ public DockingManager() /// public event EventHandler ActiveContentChanged; + /// + /// Event is raised when LayoutFloatingWindowControl created + /// + public event EventHandler LayoutFloatingWindowControlCreated; + + /// + /// Event is raised when LayoutFloatingWindowControl closed + /// + public event EventHandler LayoutFloatingWindowControlClosed; + + #endregion Events #region Public Properties @@ -1736,8 +1747,12 @@ internal void StartDraggingFloatingWindowForContent(LayoutContent contentModel, var show = fwc == null; // Do not show already visible floating window if (fwc == null) + { fwc = CreateFloatingWindow(contentModel, false); + LayoutFloatingWindowControlCreated?.Invoke(this, new LayoutFloatingWindowControlCreatedEventArgs(fwc)); + } + if (fwc != null) { Dispatcher.BeginInvoke(new Action(() => @@ -1758,6 +1773,9 @@ internal void StartDraggingFloatingWindowForPane(LayoutAnchorablePane paneModel) { var fwc = CreateFloatingWindowForLayoutAnchorableWithoutParent(paneModel, false); if (fwc == null) return; + + LayoutFloatingWindowControlCreated?.Invoke(this, new LayoutFloatingWindowControlCreatedEventArgs(fwc)); + fwc.AttachDrag(); fwc.Show(); } @@ -1815,7 +1833,12 @@ internal void GetOverlayWindowHostsByZOrder(ref List overlay overlayWindowHosts.AddRange(bottomFloatingWindows); } - internal void RemoveFloatingWindow(LayoutFloatingWindowControl floatingWindow) => _fwList.Remove(floatingWindow); + internal void RemoveFloatingWindow(LayoutFloatingWindowControl floatingWindow) + { + _fwList.Remove(floatingWindow); + + LayoutFloatingWindowControlClosed?.Invoke(this, new LayoutFloatingWindowControlClosedEventArgs(floatingWindow)); + } internal void ExecuteCloseCommand(LayoutDocument document) { diff --git a/source/Components/AvalonDock/LayoutFloatingWindowControlClosedEventArgs.cs b/source/Components/AvalonDock/LayoutFloatingWindowControlClosedEventArgs.cs new file mode 100644 index 00000000..3769684a --- /dev/null +++ b/source/Components/AvalonDock/LayoutFloatingWindowControlClosedEventArgs.cs @@ -0,0 +1,16 @@ +using System; +using AvalonDock.Controls; + +namespace AvalonDock +{ + public sealed class LayoutFloatingWindowControlClosedEventArgs : EventArgs + { + public LayoutFloatingWindowControlClosedEventArgs(LayoutFloatingWindowControl layoutFloatingWindowControl) + { + LayoutFloatingWindowControl = layoutFloatingWindowControl; + } + + public LayoutFloatingWindowControl LayoutFloatingWindowControl { get; } + } + +} diff --git a/source/Components/AvalonDock/LayoutFloatingWindowControlCreatedEventArgs.cs b/source/Components/AvalonDock/LayoutFloatingWindowControlCreatedEventArgs.cs new file mode 100644 index 00000000..de7f9a19 --- /dev/null +++ b/source/Components/AvalonDock/LayoutFloatingWindowControlCreatedEventArgs.cs @@ -0,0 +1,16 @@ +using AvalonDock.Controls; +using System; + +namespace AvalonDock +{ + public sealed class LayoutFloatingWindowControlCreatedEventArgs : EventArgs + { + public LayoutFloatingWindowControlCreatedEventArgs(LayoutFloatingWindowControl layoutFloatingWindowControl) + { + LayoutFloatingWindowControl = layoutFloatingWindowControl; + } + + public LayoutFloatingWindowControl LayoutFloatingWindowControl { get; } + } + +} From c461e8bd690c09e5429ef36767f9362e7906a125 Mon Sep 17 00:00:00 2001 From: Denis Smirnov Date: Thu, 1 Dec 2022 12:34:52 +0300 Subject: [PATCH 13/53] remove unused variable from DocumentPaneTabPanel --- source/Components/AvalonDock/Controls/DocumentPaneTabPanel.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/Components/AvalonDock/Controls/DocumentPaneTabPanel.cs b/source/Components/AvalonDock/Controls/DocumentPaneTabPanel.cs index 156b2056..f8eb5866 100644 --- a/source/Components/AvalonDock/Controls/DocumentPaneTabPanel.cs +++ b/source/Components/AvalonDock/Controls/DocumentPaneTabPanel.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -36,8 +36,6 @@ public DocumentPaneTabPanel() protected override Size MeasureOverride(Size availableSize) { - var visibleChildren = Children.Cast().Where(ch => ch.Visibility != System.Windows.Visibility.Collapsed); - Size desideredSize = new Size(); foreach (FrameworkElement child in Children) { From 2b637553967308ced51b46c5aa3d7121f8b46b86 Mon Sep 17 00:00:00 2001 From: pkindruk Date: Fri, 2 Dec 2022 01:24:27 +0300 Subject: [PATCH 14/53] Add XmlSerializer cache to fix memory leaks. --- .../Layout/LayoutAnchorableFloatingWindow.cs | 4 +-- .../Layout/LayoutDocumentFloatingWindow.cs | 4 +-- .../AvalonDock/Layout/LayoutFloatingWindow.cs | 2 +- .../AvalonDock/Layout/LayoutGroup.cs | 4 +-- .../AvalonDock/Layout/LayoutRoot.cs | 2 +- .../Serialization/XmlLayoutSerializer.cs | 2 +- .../AvalonDock/XmlSerializersCache.cs | 25 +++++++++++++++++++ 7 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 source/Components/AvalonDock/XmlSerializersCache.cs diff --git a/source/Components/AvalonDock/Layout/LayoutAnchorableFloatingWindow.cs b/source/Components/AvalonDock/Layout/LayoutAnchorableFloatingWindow.cs index 6bfe812c..abf7b830 100644 --- a/source/Components/AvalonDock/Layout/LayoutAnchorableFloatingWindow.cs +++ b/source/Components/AvalonDock/Layout/LayoutAnchorableFloatingWindow.cs @@ -155,13 +155,13 @@ public override void ReadXml(XmlReader reader) XmlSerializer serializer; if (reader.LocalName.Equals(nameof(LayoutAnchorablePaneGroup))) - serializer = XmlSerializer.FromTypes(new[] { typeof(LayoutAnchorablePaneGroup) })[0]; + serializer = XmlSerializersCache.GetSerializer(); else { var type = LayoutRoot.FindType(reader.LocalName); if (type == null) throw new ArgumentException("AvalonDock.LayoutAnchorableFloatingWindow doesn't know how to deserialize " + reader.LocalName); - serializer = XmlSerializer.FromTypes(new[] { type })[0]; + serializer = XmlSerializersCache.GetSerializer(type); } RootPanel = (LayoutAnchorablePaneGroup)serializer.Deserialize(reader); } diff --git a/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs b/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs index 68d07d6a..d716b884 100644 --- a/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs +++ b/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs @@ -153,12 +153,12 @@ public override void ReadXml(XmlReader reader) XmlSerializer serializer; if (reader.LocalName.Equals(nameof(LayoutDocument))) - serializer = new XmlSerializer(typeof(LayoutDocument)); + serializer = XmlSerializersCache.GetSerializer(); else { var type = LayoutRoot.FindType(reader.LocalName); if (type == null) throw new ArgumentException("AvalonDock.LayoutDocumentFloatingWindow doesn't know how to deserialize " + reader.LocalName); - serializer = new XmlSerializer(type); + serializer = XmlSerializersCache.GetSerializer(type); } RootPanel = (LayoutDocumentPaneGroup)serializer.Deserialize(reader); } diff --git a/source/Components/AvalonDock/Layout/LayoutFloatingWindow.cs b/source/Components/AvalonDock/Layout/LayoutFloatingWindow.cs index 44ae1f14..d0b409e2 100644 --- a/source/Components/AvalonDock/Layout/LayoutFloatingWindow.cs +++ b/source/Components/AvalonDock/Layout/LayoutFloatingWindow.cs @@ -61,7 +61,7 @@ public virtual void WriteXml(XmlWriter writer) foreach (var child in Children) { var type = child.GetType(); - var serializer = XmlSerializer.FromTypes(new[] { type })[0]; + var serializer = XmlSerializersCache.GetSerializer(type); serializer.Serialize(writer, child); } } diff --git a/source/Components/AvalonDock/Layout/LayoutGroup.cs b/source/Components/AvalonDock/Layout/LayoutGroup.cs index 7b243e8b..a019a711 100644 --- a/source/Components/AvalonDock/Layout/LayoutGroup.cs +++ b/source/Components/AvalonDock/Layout/LayoutGroup.cs @@ -159,7 +159,7 @@ public virtual void ReadXml(System.Xml.XmlReader reader) if (typeForSerializer == null) throw new ArgumentException("AvalonDock.LayoutGroup doesn't know how to deserialize " + reader.LocalName); - XmlSerializer serializer = XmlSerializer.FromTypes(new[] { typeForSerializer })[0]; + XmlSerializer serializer = XmlSerializersCache.GetSerializer(typeForSerializer); Children.Add((T)serializer.Deserialize(reader)); } @@ -173,7 +173,7 @@ public virtual void WriteXml(System.Xml.XmlWriter writer) foreach (var child in Children) { var type = child.GetType(); - var serializer = XmlSerializer.FromTypes(new[] { type })[0]; + var serializer = XmlSerializersCache.GetSerializer(type); serializer.Serialize(writer, child); } } diff --git a/source/Components/AvalonDock/Layout/LayoutRoot.cs b/source/Components/AvalonDock/Layout/LayoutRoot.cs index ed905d49..957b8a8f 100644 --- a/source/Components/AvalonDock/Layout/LayoutRoot.cs +++ b/source/Components/AvalonDock/Layout/LayoutRoot.cs @@ -966,7 +966,7 @@ private object ReadElement(XmlReader reader) throw new ArgumentException("AvalonDock.LayoutRoot doesn't know how to deserialize " + reader.LocalName); break; } - XmlSerializer serializer = XmlSerializer.FromTypes(new[] { typeToSerialize })[0]; + XmlSerializer serializer = XmlSerializersCache.GetSerializer(typeToSerialize); return serializer.Deserialize(reader); } diff --git a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs index d883b82d..66e8d8b5 100644 --- a/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/XmlLayoutSerializer.cs @@ -55,7 +55,7 @@ private void DeserializeCommon(DeserializeFunction function) #region Public Methods - readonly XmlSerializer _serializer = XmlSerializer.FromTypes(new[] { typeof(LayoutRoot) })[0]; + readonly XmlSerializer _serializer = XmlSerializersCache.GetSerializer(); /// Serialize the layout into a . /// diff --git a/source/Components/AvalonDock/XmlSerializersCache.cs b/source/Components/AvalonDock/XmlSerializersCache.cs new file mode 100644 index 00000000..43f7d469 --- /dev/null +++ b/source/Components/AvalonDock/XmlSerializersCache.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace AvalonDock +{ + internal static class XmlSerializersCache + { + private static readonly Dictionary s_cache = new Dictionary(); + + + public static XmlSerializer GetSerializer(Type targetType) + { + if (!s_cache.TryGetValue(targetType, out var serializer)) + { + serializer = XmlSerializer.FromTypes(new[] { targetType })[0]; + s_cache.Add(targetType, serializer); + } + + return serializer; + } + + public static XmlSerializer GetSerializer() => GetSerializer(typeof(T)); + } +} From 38b2d500added632714c7bf8b4ed35dd8b69137d Mon Sep 17 00:00:00 2001 From: pkindruk Date: Fri, 2 Dec 2022 22:13:03 +0300 Subject: [PATCH 15/53] Fix deserialized layout document close. --- source/Components/AvalonDock/DockingManager.cs | 1 + .../Components/AvalonDock/Layout/LayoutElement.cs | 14 ++++++++++++++ .../Layout/Serialization/LayoutSerializer.cs | 2 ++ 3 files changed, 17 insertions(+) diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 762917bc..2592d712 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -2230,6 +2230,7 @@ private void DocumentsSourceElementsChanged(object sender, NotifyCollectionChang var documentsToRemove = Layout.Descendents().OfType().Where(d => e.OldItems.Contains(d.Content)).ToArray(); foreach (var documentToRemove in documentsToRemove) { + documentToRemove.Content = null; documentToRemove.Parent.RemoveChild(documentToRemove); RemoveViewFromLogicalChild(documentToRemove); } diff --git a/source/Components/AvalonDock/Layout/LayoutElement.cs b/source/Components/AvalonDock/Layout/LayoutElement.cs index 29da33f2..d49e1f6a 100644 --- a/source/Components/AvalonDock/Layout/LayoutElement.cs +++ b/source/Components/AvalonDock/Layout/LayoutElement.cs @@ -109,6 +109,20 @@ public virtual void ConsoleDump(int tab) #endregion Public Methods + #region Internal Methods + + /// + /// When deserializing layout enclosing element parent is set later than this parent + /// We need to update it, otherwise when deleting this element will no be called + /// + internal void FixCachedRootOnDeserialize() + { + if (_root == null) + _root = Root; + } + + #endregion Internal Methods + #region protected methods /// Provides derived classes an opportunity to handle execute code before to the property changes. diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs index 7244045f..8c133bb5 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs @@ -60,6 +60,8 @@ public LayoutSerializer(DockingManager manager) protected virtual void FixupLayout(LayoutRoot layout) { + foreach (var element in layout.Descendents().OfType()) element.FixCachedRootOnDeserialize(); + //fix container panes foreach (var lcToAttach in layout.Descendents().OfType().Where(lc => lc.PreviousContainerId != null)) { From ef2eae0f6b3f535f4103e191ccfb0936ef5fcf0c Mon Sep 17 00:00:00 2001 From: pkindruk Date: Mon, 5 Dec 2022 14:50:04 +0300 Subject: [PATCH 16/53] Add thread safety to xml serializer cache. --- .../AvalonDock/XmlSerializersCache.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/XmlSerializersCache.cs b/source/Components/AvalonDock/XmlSerializersCache.cs index 43f7d469..06846d09 100644 --- a/source/Components/AvalonDock/XmlSerializersCache.cs +++ b/source/Components/AvalonDock/XmlSerializersCache.cs @@ -1,20 +1,29 @@ using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Xml.Serialization; namespace AvalonDock { internal static class XmlSerializersCache { - private static readonly Dictionary s_cache = new Dictionary(); + private static readonly object s_lock = new object(); + private static readonly ConcurrentDictionary s_cache = new ConcurrentDictionary(); public static XmlSerializer GetSerializer(Type targetType) { - if (!s_cache.TryGetValue(targetType, out var serializer)) + if (s_cache.TryGetValue(targetType, out var serializer)) + return serializer; + + lock (s_lock) { - serializer = XmlSerializer.FromTypes(new[] { targetType })[0]; - s_cache.Add(targetType, serializer); + // we can have multiple threads waiting on lock + // one of them could create what we were looking for + if (!s_cache.TryGetValue(targetType, out serializer)) + { + serializer = XmlSerializer.FromTypes(new[] { targetType })[0]; + _ = s_cache.TryAdd(targetType, serializer); + } } return serializer; From 108cc408146ac92900351d0ce2819d31145b9cc9 Mon Sep 17 00:00:00 2001 From: L45eMy Date: Tue, 13 Dec 2022 18:10:55 +0100 Subject: [PATCH 17/53] Update LayoutDocumentPane.cs Restore previously activated document on close --- .../Components/AvalonDock/Layout/LayoutDocumentPane.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs b/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs index f51452f9..966e820f 100644 --- a/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs +++ b/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs @@ -155,7 +155,7 @@ private void AutoFixSelectedContent() { if (!_autoFixSelectedContent) return; if (SelectedContentIndex >= ChildrenCount) SelectedContentIndex = Children.Count - 1; - if (SelectedContentIndex == -1 && ChildrenCount > 0) SetNextSelectedIndex(); + if (SelectedContentIndex == -1 && ChildrenCount > 0) SetLastActivatedIndex(); } /// @@ -223,6 +223,13 @@ internal void SetNextSelectedIndex() } } + /// Sets the current to the last activated child with IsEnabled == true + private void SetLastActivatedIndex() + { + var lastActivatedDocument = Children.Where(c => c.IsEnabled).OrderByDescending(c => c.LastActivationTimeStamp.GetValueOrDefault()).FirstOrDefault(); + SelectedContentIndex = Children.IndexOf(lastActivatedDocument); + } + /// Updates the property of this object. internal void UpdateIsDirectlyHostedInFloatingWindow() => RaisePropertyChanged(nameof(IsDirectlyHostedInFloatingWindow)); From 39b4b4f7bcc904ca388a39a61a44472b5ad7ca0b Mon Sep 17 00:00:00 2001 From: L45eMy Date: Tue, 13 Dec 2022 18:31:12 +0100 Subject: [PATCH 18/53] Set last activated after close in anchorables --- .../Components/AvalonDock/Layout/LayoutAnchorablePane.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Layout/LayoutAnchorablePane.cs b/source/Components/AvalonDock/Layout/LayoutAnchorablePane.cs index b8d6d3e6..72517f3f 100644 --- a/source/Components/AvalonDock/Layout/LayoutAnchorablePane.cs +++ b/source/Components/AvalonDock/Layout/LayoutAnchorablePane.cs @@ -235,7 +235,14 @@ private void AutoFixSelectedContent() { if (!_autoFixSelectedContent) return; if (SelectedContentIndex >= ChildrenCount) SelectedContentIndex = Children.Count - 1; - if (SelectedContentIndex == -1 && ChildrenCount > 0) SetNextSelectedIndex(); + if (SelectedContentIndex == -1 && ChildrenCount > 0) SetLastActivatedIndex(); + } + + /// Sets the current to the last activated child with IsEnabled == true + private void SetLastActivatedIndex() + { + var lastActivatedDocument = Children.Where(c => c.IsEnabled).OrderByDescending(c => c.LastActivationTimeStamp.GetValueOrDefault()).FirstOrDefault(); + SelectedContentIndex = Children.IndexOf(lastActivatedDocument); } private void OnParentChildrenCollectionChanged(object sender, EventArgs e) => RaisePropertyChanged(nameof(IsDirectlyHostedInFloatingWindow)); From 619985f2303708da5c684bff919373c827f66532 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Wed, 21 Dec 2022 19:57:24 +0800 Subject: [PATCH 19/53] Improved floating window activation state - Update the IsActive property of the content when the floating window is active or inactive --- .../LayoutAnchorableFloatingWindowControl.cs | 15 +++++++++------ .../LayoutDocumentFloatingWindowControl.cs | 18 +++++++++--------- .../Controls/LayoutFloatingWindowControl.cs | 8 -------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 54b6d454..cd9025c9 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -243,14 +243,17 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int { switch (msg) { - case Win32Helper.WM_NCLBUTTONDOWN: //Left button down on title -> start dragging over docking manager - if (wParam.ToInt32() == Win32Helper.HT_CAPTION) + case Win32Helper.WM_ACTIVATE: + var anchorablePane = _model.Descendents().OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + + if (anchorablePane != null) { - var anchorablePane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - if (anchorablePane != null) anchorablePane.SelectedContent.IsActive = true; - handled = true; + var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); + anchorablePane.SelectedContent.IsActive = isActive; } + + handled = true; break; case Win32Helper.WM_NCRBUTTONUP: diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 5c25b593..34435a9f 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -118,17 +118,17 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int { switch (msg) { - case Win32Helper.WM_NCLBUTTONDOWN: //Left button down on title -> start dragging over docking manager - if (wParam.ToInt32() == Win32Helper.HT_CAPTION) + case Win32Helper.WM_ACTIVATE: + var layoutDocumentPane = _model.Descendents().OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + + if (layoutDocumentPane != null) { - LayoutDocumentPane layoutDocumentPane = _model.Descendents().OfType().FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - if (layoutDocumentPane != null) - { - layoutDocumentPane.SelectedContent.IsActive = true; - } - - handled = true; + var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); + layoutDocumentPane.SelectedContent.IsActive = isActive; } + + handled = true; break; case Win32Helper.WM_NCRBUTTONUP: diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index ab398e1b..fa31758f 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -348,14 +348,6 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP 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; From 783ce8e9f0d3e1bf1dfb790a9a584ae488bba4c1 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 22 Dec 2022 12:02:57 +0800 Subject: [PATCH 20/53] Move the "handled" variable inside the code block --- .../Controls/LayoutAnchorableFloatingWindowControl.cs | 3 ++- .../AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs | 3 ++- .../AvalonDock/Controls/LayoutFloatingWindowControl.cs | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index cd9025c9..75f417da 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -251,9 +251,10 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int { var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); anchorablePane.SelectedContent.IsActive = isActive; + + handled = true; } - handled = true; break; case Win32Helper.WM_NCRBUTTONUP: diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 34435a9f..13845d3c 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -126,9 +126,10 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int { var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); layoutDocumentPane.SelectedContent.IsActive = isActive; + + handled = true; } - handled = true; break; case Win32Helper.WM_NCRBUTTONUP: diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index fa31758f..c1377a92 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -349,7 +349,6 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP { case Win32Helper.WM_ACTIVATE: UpdateWindowsSizeBasedOnMinSize(); - break; case Win32Helper.WM_EXITSIZEMOVE: From b49f1d08048bede7a24eb84795cad31ff529fdde Mon Sep 17 00:00:00 2001 From: John Stewien Date: Thu, 22 Dec 2022 14:33:29 +1030 Subject: [PATCH 21/53] Added event notifications for when an Anchorable is being hidden or closed. --- .../AvalonDock/AnchorableClosedEventArgs.cs | 35 +++++++++++++++ .../AvalonDock/AnchorableClosingEventArgs.cs | 35 +++++++++++++++ .../AvalonDock/AnchorableHiddenEventArgs.cs | 35 +++++++++++++++ .../AvalonDock/AnchorableHidingEventArgs.cs | 40 +++++++++++++++++ .../Components/AvalonDock/DockingManager.cs | 45 +++++++++++++++++-- .../AvalonDock/Layout/LayoutAnchorable.cs | 5 ++- 6 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 source/Components/AvalonDock/AnchorableClosedEventArgs.cs create mode 100644 source/Components/AvalonDock/AnchorableClosingEventArgs.cs create mode 100644 source/Components/AvalonDock/AnchorableHiddenEventArgs.cs create mode 100644 source/Components/AvalonDock/AnchorableHidingEventArgs.cs diff --git a/source/Components/AvalonDock/AnchorableClosedEventArgs.cs b/source/Components/AvalonDock/AnchorableClosedEventArgs.cs new file mode 100644 index 00000000..eb24cef0 --- /dev/null +++ b/source/Components/AvalonDock/AnchorableClosedEventArgs.cs @@ -0,0 +1,35 @@ +/************************************************************************ + AvalonDock + + 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 https://opensource.org/licenses/MS-PL + ************************************************************************/ + +using AvalonDock.Layout; +using System; + +namespace AvalonDock +{ + /// + /// Implements an event that can be raised to inform the client application about an + /// anchorable that been closed and removed its content (viewmodel) from the docking framework. + /// + public class AnchorableClosedEventArgs : EventArgs + { + /// + /// Class constructor from the anchorables layout model. + /// + /// + public AnchorableClosedEventArgs(LayoutAnchorable anchorable) + { + Anchorable = anchorable; + } + + /// + /// Gets the model of the anchorable that has been closed. + /// + public LayoutAnchorable Anchorable { get; private set; } + } +} \ No newline at end of file diff --git a/source/Components/AvalonDock/AnchorableClosingEventArgs.cs b/source/Components/AvalonDock/AnchorableClosingEventArgs.cs new file mode 100644 index 00000000..4f29a4d3 --- /dev/null +++ b/source/Components/AvalonDock/AnchorableClosingEventArgs.cs @@ -0,0 +1,35 @@ +/************************************************************************ + AvalonDock + + 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 https://opensource.org/licenses/MS-PL + ************************************************************************/ + +using AvalonDock.Layout; +using System.ComponentModel; + +namespace AvalonDock +{ + /// + /// Implements a Cancelable event that can be raised to ask the client application whether closing this anchorable + /// and removing its content (viewmodel) is OK or not. + /// + public class AnchorableClosingEventArgs : CancelEventArgs + { + /// + /// Class constructor from the anchorable layout model. + /// + /// + public AnchorableClosingEventArgs(LayoutAnchorable anchorable) + { + Anchorable = anchorable; + } + + /// + /// Gets the model of the anchorable that is about to be closed. + /// + public LayoutAnchorable Anchorable { get; private set; } + } +} \ No newline at end of file diff --git a/source/Components/AvalonDock/AnchorableHiddenEventArgs.cs b/source/Components/AvalonDock/AnchorableHiddenEventArgs.cs new file mode 100644 index 00000000..f595612e --- /dev/null +++ b/source/Components/AvalonDock/AnchorableHiddenEventArgs.cs @@ -0,0 +1,35 @@ +/************************************************************************ + AvalonDock + + 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 https://opensource.org/licenses/MS-PL + ************************************************************************/ + +using AvalonDock.Layout; +using System; + +namespace AvalonDock +{ + /// + /// Implements an event that can be raised to inform the client application about an + /// anchorable that been hidden. + /// + public class AnchorableHiddenEventArgs : EventArgs + { + /// + /// Class constructor from the anchorables layout model. + /// + /// + public AnchorableHiddenEventArgs(LayoutAnchorable anchorable) + { + Anchorable = anchorable; + } + + /// + /// Gets the model of the anchorable that has been hidden. + /// + public LayoutAnchorable Anchorable { get; private set; } + } +} \ No newline at end of file diff --git a/source/Components/AvalonDock/AnchorableHidingEventArgs.cs b/source/Components/AvalonDock/AnchorableHidingEventArgs.cs new file mode 100644 index 00000000..2367199f --- /dev/null +++ b/source/Components/AvalonDock/AnchorableHidingEventArgs.cs @@ -0,0 +1,40 @@ +/************************************************************************ + AvalonDock + + 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 https://opensource.org/licenses/MS-PL + ************************************************************************/ + +using AvalonDock.Layout; +using System.ComponentModel; + +namespace AvalonDock +{ + /// + /// Implements a Cancelable event that can be raised to ask the client application whether hiding this anchorable + /// is OK or not. + /// + public class AnchorableHidingEventArgs : CancelEventArgs + { + /// + /// Class constructor from the anchorable layout model. + /// + /// + public AnchorableHidingEventArgs(LayoutAnchorable anchorable) + { + Anchorable = anchorable; + } + + /// + /// Gets the model of the anchorable that is about to be hidden. + /// + public LayoutAnchorable Anchorable { get; private set; } + + /// + /// Gets or sets a value indicating whether an anchorable should be closed instead of hidden + /// + public bool CloseInsteadOfHide { get; set; } + } +} \ No newline at end of file diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 762917bc..6a73991f 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -115,6 +115,20 @@ public DockingManager() /// Event fired after a document is closed. public event EventHandler DocumentClosed; + /// Event fired when an anchorable is about to be closed. + /// Subscribers have the opportuniy to cancel the operation. + public event EventHandler AnchorableClosing; + + /// Event fired after an anchorable is closed + public event EventHandler AnchorableClosed; + + /// Event fired when an anchorable is about to be hidden. + /// Subscribers have the opportuniy to cancel the operation. + public event EventHandler AnchorableHiding; + + /// Event fired after an anchorable is hidden + public event EventHandler AnchorableHidden; + /// Event is raised when changes. /// public event EventHandler ActiveContentChanged; @@ -1922,11 +1936,36 @@ internal void ExecuteCloseAllCommand(LayoutContent contentSelected) internal void ExecuteCloseCommand(LayoutAnchorable anchorable) { if (!(anchorable is LayoutAnchorable model)) return; - model.CloseAnchorable(); - RemoveViewFromLogicalChild(anchorable); + + AnchorableClosingEventArgs closingArgs = null; + AnchorableClosing?.Invoke(this, closingArgs = new AnchorableClosingEventArgs(model)); + if (closingArgs?.Cancel == true) + return; + + if (model.CloseAnchorable()) + { + RemoveViewFromLogicalChild(model); + AnchorableClosed?.Invoke(this, new AnchorableClosedEventArgs(model)); + } } - internal void ExecuteHideCommand(LayoutAnchorable anchorable) => anchorable?.Hide(); + internal void ExecuteHideCommand(LayoutAnchorable anchorable) + { + if (!(anchorable is LayoutAnchorable model)) return; + + AnchorableHidingEventArgs hidingArgs = null; + AnchorableHiding?.Invoke(this, hidingArgs = new AnchorableHidingEventArgs(model)); + if (hidingArgs?.CloseInsteadOfHide == true) + { + ExecuteCloseCommand(model); + return; + } + if (hidingArgs?.Cancel == true) return; + + model.Hide(); + + AnchorableHidden?.Invoke(this, new AnchorableHiddenEventArgs(model)); + } internal void ExecuteAutoHideCommand(LayoutAnchorable _anchorable) => _anchorable.ToggleAutoHide(); diff --git a/source/Components/AvalonDock/Layout/LayoutAnchorable.cs b/source/Components/AvalonDock/Layout/LayoutAnchorable.cs index c098d113..4adda820 100644 --- a/source/Components/AvalonDock/Layout/LayoutAnchorable.cs +++ b/source/Components/AvalonDock/Layout/LayoutAnchorable.cs @@ -608,11 +608,12 @@ public void ToggleAutoHide() #region Internal Methods - internal void CloseAnchorable() + internal bool CloseAnchorable() { - if (!TestCanClose()) return; + if (!TestCanClose()) return false; if (IsAutoHidden) ToggleAutoHide(); CloseInternal(); + return true; } // BD: 17.08.2020 Remove that bodge and handle CanClose=false && CanHide=true in XAML From 49e7b3d146481f5b71453e8fbc96081cdfd43bde Mon Sep 17 00:00:00 2001 From: John Stewien Date: Thu, 22 Dec 2022 16:11:37 +1030 Subject: [PATCH 22/53] Added unit tests for Anchorable Hiding and Closing events --- .../AvalonDockTest/AnchorablePaneTest.cs | 230 +++++++++++++++++- 1 file changed, 229 insertions(+), 1 deletion(-) diff --git a/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs b/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs index 3f461d2f..e1eb1540 100644 --- a/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs +++ b/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs @@ -1,4 +1,4 @@ -namespace AvalonDockTest +namespace AvalonDockTest { using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.STAExtensions; @@ -6,6 +6,10 @@ using AvalonDock.Layout; using AvalonDockTest.TestHelpers; using AvalonDockTest.views; + using AvalonDock; + using System.Collections.Generic; + using System.Linq; + using AvalonDock.Controls; [STATestClass] public class AnchorablePaneTest : AutomationTestBase @@ -30,5 +34,229 @@ public void AnchorablePaneHideCloseTest() ILayoutContainer actualContainer = windows.Screen3.Parent; Assert.AreEqual(expectedContainer, actualContainer); } + + [STATestMethod] + public void AnchorablePaneHideCloseEventsFiredTest() + { + // Create the window with 2 LayoutAnchorable items + + TestHost.SwitchToAppThread(); + + Task taskResult = WindowHelpers.CreateInvisibleWindowAsync(); + + taskResult.Wait(); + + AnchorablePaneTestWindow windows = taskResult.Result; + DockingManager dockingManager = windows.dockingManager; + + // These lists hold a record of the anchorable hide and close events + + List isHidingRaised = new List(); + List isHiddenRaised = new List(); + List isClosingRaised = new List(); + List isClosedRaised = new List(); + + // Event handlers for the hide and close events + + dockingManager.AnchorableClosing += (s, e) => isClosingRaised.Add(e.Anchorable); + dockingManager.AnchorableClosed += (s, e) => isClosedRaised.Add(e.Anchorable); + dockingManager.AnchorableHiding += (s, e) => isHidingRaised.Add(e.Anchorable); + dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); + + // Get the visual items + + LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; + LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; + + Assert.IsNotNull(item2); + Assert.IsNotNull(item3); + + // Ensure the items can be hidden and closed + + item2.CanClose = true; + item3.CanClose = true; + item2.CanHide = true; + item3.CanHide = true; + + // Hide item3 + + item3.HideCommand.Execute(null); + + // Ensure only item3 is hidden, and check the correct events were fired + + Assert.IsFalse(windows.Screen2.IsHidden); + Assert.IsTrue(windows.Screen3.IsHidden); + Assert.AreEqual(1, isHidingRaised.Count); + Assert.AreEqual(1, isHiddenRaised.Count); + Assert.AreEqual(0, isClosingRaised.Count); + Assert.AreEqual(0, isClosedRaised.Count); + + Assert.AreEqual(windows.Screen3, isHidingRaised.First()); + Assert.AreEqual(windows.Screen3, isHiddenRaised.First()); + + isHidingRaised.Clear(); + isHiddenRaised.Clear(); + + // Close item2 + + item2.CloseCommand.Execute(null); + + // Check the correct events were fired + + Assert.AreEqual(0, isHidingRaised.Count); + Assert.AreEqual(0, isHiddenRaised.Count); + Assert.AreEqual(1, isClosingRaised.Count); + Assert.AreEqual(1, isClosedRaised.Count); + + Assert.AreEqual(windows.Screen2, isClosingRaised.First()); + Assert.AreEqual(windows.Screen2, isClosedRaised.First()); + } + + [STATestMethod] + public void AnchorablePaneHideCloseEventsCancelledTest() + { + // Create the window with 2 LayoutAnchorable items + + TestHost.SwitchToAppThread(); + + Task taskResult = WindowHelpers.CreateInvisibleWindowAsync(); + + taskResult.Wait(); + + AnchorablePaneTestWindow windows = taskResult.Result; + DockingManager dockingManager = windows.dockingManager; + + // These lists hold a record of the anchorable hide and close events + + List isHidingRaised = new List(); + List isHiddenRaised = new List(); + List isClosingRaised = new List(); + List isClosedRaised = new List(); + + // Event handlers for the hide and close events + + dockingManager.AnchorableClosing += (s, e) => + { + e.Cancel = true; + isClosingRaised.Add(e.Anchorable); + }; + dockingManager.AnchorableClosed += (s, e) => isClosedRaised.Add(e.Anchorable); + dockingManager.AnchorableHiding += (s, e) => + { + e.Cancel = true; + isHidingRaised.Add(e.Anchorable); + }; + dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); + + // Get the visual items + + LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; + LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; + + Assert.IsNotNull(item2); + Assert.IsNotNull(item3); + + // Ensure the items can be hidden and closed + + item2.CanClose = true; + item3.CanClose = true; + item2.CanHide = true; + item3.CanHide = true; + + // Hide item3 + + item3.HideCommand.Execute(null); + + // Ensure nothing was hidden but cancelled instead, and check the correct events were fired + + Assert.IsFalse(windows.Screen2.IsHidden); + Assert.IsFalse(windows.Screen3.IsHidden); + Assert.AreEqual(1, isHidingRaised.Count); + Assert.AreEqual(0, isHiddenRaised.Count); + Assert.AreEqual(0, isClosingRaised.Count); + Assert.AreEqual(0, isClosedRaised.Count); + + Assert.AreEqual(windows.Screen3, isHidingRaised.First()); + + isHidingRaised.Clear(); + + // Close item2 + + item2.CloseCommand.Execute(null); + + // Ensure nothing was closed, and check the correct events were fired + + Assert.AreEqual(0, isHidingRaised.Count); + Assert.AreEqual(0, isHiddenRaised.Count); + Assert.AreEqual(1, isClosingRaised.Count); + Assert.AreEqual(0, isClosedRaised.Count); + + Assert.AreEqual(windows.Screen2, isClosingRaised.First()); + } + + [STATestMethod] + public void AnchorablePaneHideEventRedirectTest() + { + // Create the window with 2 LayoutAnchorable items + + TestHost.SwitchToAppThread(); + + Task taskResult = WindowHelpers.CreateInvisibleWindowAsync(); + + taskResult.Wait(); + + AnchorablePaneTestWindow windows = taskResult.Result; + DockingManager dockingManager = windows.dockingManager; + + // These lists hold a record of the anchorable hide and close events + + List isHidingRaised = new List(); + List isHiddenRaised = new List(); + List isClosingRaised = new List(); + List isClosedRaised = new List(); + + // Event handlers for the hide and close events + + dockingManager.AnchorableClosing += (s, e) => isClosingRaised.Add(e.Anchorable); + dockingManager.AnchorableClosed += (s, e) => isClosedRaised.Add(e.Anchorable); + dockingManager.AnchorableHiding += (s, e) => + { + e.CloseInsteadOfHide = true; + isHidingRaised.Add(e.Anchorable); + }; + dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); + + // Get the visual items + + LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; + LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; + + Assert.IsNotNull(item2); + Assert.IsNotNull(item3); + + // Ensure the items can be hidden and closed + + item2.CanClose = true; + item3.CanClose = true; + item2.CanHide = true; + item3.CanHide = true; + + // Hide item3 + + item3.HideCommand.Execute(null); + + // Ensure nothing was hidden but cancelled instead, and check the correct events were fired + + Assert.IsFalse(windows.Screen2.IsHidden); + Assert.IsFalse(windows.Screen3.IsHidden); + Assert.AreEqual(1, isHidingRaised.Count); + Assert.AreEqual(0, isHiddenRaised.Count); + Assert.AreEqual(1, isClosingRaised.Count); + Assert.AreEqual(1, isClosedRaised.Count); + + Assert.AreEqual(windows.Screen3, isHidingRaised.First()); + Assert.AreEqual(windows.Screen3, isClosingRaised.First()); + Assert.AreEqual(windows.Screen3, isClosedRaised.First()); + } } } From 2dd0af09afdd90e2bc53939fb1c6e267241a0ab1 Mon Sep 17 00:00:00 2001 From: John Stewien Date: Thu, 22 Dec 2022 17:52:57 +1030 Subject: [PATCH 23/53] Redirect Anchorable Hide and Close to DockingManager. Updated unit tests. --- .../AvalonDockTest/AnchorablePaneTest.cs | 67 ++++++------------- .../Controls/LayoutAnchorableItem.cs | 2 +- .../Components/AvalonDock/DockingManager.cs | 5 +- .../AvalonDock/Layout/LayoutAnchorable.cs | 26 +++++-- .../Layout/Serialization/LayoutSerializer.cs | 4 +- 5 files changed, 46 insertions(+), 58 deletions(-) diff --git a/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs b/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs index e1eb1540..0ca635b2 100644 --- a/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs +++ b/source/AutomationTest/AvalonDockTest/AnchorablePaneTest.cs @@ -63,24 +63,15 @@ public void AnchorablePaneHideCloseEventsFiredTest() dockingManager.AnchorableHiding += (s, e) => isHidingRaised.Add(e.Anchorable); dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); - // Get the visual items - - LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; - LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; - - Assert.IsNotNull(item2); - Assert.IsNotNull(item3); - // Ensure the items can be hidden and closed - - item2.CanClose = true; - item3.CanClose = true; - item2.CanHide = true; - item3.CanHide = true; + windows.Screen2.CanHide = true; + windows.Screen3.CanHide = true; + windows.Screen2.CanClose = true; + windows.Screen3.CanClose = true; // Hide item3 - item3.HideCommand.Execute(null); + windows.Screen3.Hide(); // Ensure only item3 is hidden, and check the correct events were fired @@ -99,7 +90,7 @@ public void AnchorablePaneHideCloseEventsFiredTest() // Close item2 - item2.CloseCommand.Execute(null); + windows.Screen2.Close(); // Check the correct events were fired @@ -148,24 +139,15 @@ public void AnchorablePaneHideCloseEventsCancelledTest() }; dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); - // Get the visual items - - LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; - LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; - - Assert.IsNotNull(item2); - Assert.IsNotNull(item3); - // Ensure the items can be hidden and closed + windows.Screen2.CanHide = true; + windows.Screen3.CanHide = true; + windows.Screen2.CanClose = true; + windows.Screen3.CanClose = true; - item2.CanClose = true; - item3.CanClose = true; - item2.CanHide = true; - item3.CanHide = true; - - // Hide item3 + // Hide Screen3 - item3.HideCommand.Execute(null); + windows.Screen3.Hide(); // Ensure nothing was hidden but cancelled instead, and check the correct events were fired @@ -180,9 +162,9 @@ public void AnchorablePaneHideCloseEventsCancelledTest() isHidingRaised.Clear(); - // Close item2 + // Close Screen2 - item2.CloseCommand.Execute(null); + windows.Screen2.Close(); // Ensure nothing was closed, and check the correct events were fired @@ -226,24 +208,13 @@ public void AnchorablePaneHideEventRedirectTest() }; dockingManager.AnchorableHidden += (s, e) => isHiddenRaised.Add(e.Anchorable); - // Get the visual items - - LayoutAnchorableItem item2 = dockingManager.GetLayoutItemFromModel(windows.Screen2) as LayoutAnchorableItem; - LayoutAnchorableItem item3 = dockingManager.GetLayoutItemFromModel(windows.Screen3) as LayoutAnchorableItem; - - Assert.IsNotNull(item2); - Assert.IsNotNull(item3); + // Ensure the Screen3 can be hidden and closed + windows.Screen3.CanHide = true; + windows.Screen3.CanClose = true; - // Ensure the items can be hidden and closed - - item2.CanClose = true; - item3.CanClose = true; - item2.CanHide = true; - item3.CanHide = true; + // Hide Screen3 - // Hide item3 - - item3.HideCommand.Execute(null); + windows.Screen3.Hide(); // Ensure nothing was hidden but cancelled instead, and check the correct events were fired diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableItem.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableItem.cs index daee7dfa..67e5f0dd 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableItem.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableItem.cs @@ -276,7 +276,7 @@ protected override void OnVisibilityChanged() { switch (Visibility) { - case Visibility.Hidden: case Visibility.Collapsed: _anchorable.Hide(false); break; + case Visibility.Hidden: case Visibility.Collapsed: _anchorable.HideAnchorable(false); break; case Visibility.Visible: _anchorable.Show(); break; } } diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index 6a73991f..633b0f6d 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1962,9 +1962,8 @@ internal void ExecuteHideCommand(LayoutAnchorable anchorable) } if (hidingArgs?.Cancel == true) return; - model.Hide(); - - AnchorableHidden?.Invoke(this, new AnchorableHiddenEventArgs(model)); + if(model.HideAnchorable(true)) + AnchorableHidden?.Invoke(this, new AnchorableHiddenEventArgs(model)); } internal void ExecuteAutoHideCommand(LayoutAnchorable _anchorable) => _anchorable.ToggleAutoHide(); diff --git a/source/Components/AvalonDock/Layout/LayoutAnchorable.cs b/source/Components/AvalonDock/Layout/LayoutAnchorable.cs index 4adda820..68efffde 100644 --- a/source/Components/AvalonDock/Layout/LayoutAnchorable.cs +++ b/source/Components/AvalonDock/Layout/LayoutAnchorable.cs @@ -280,7 +280,16 @@ public override void WriteXml(System.Xml.XmlWriter writer) } /// - public override void Close() => CloseAnchorable(); + public override void Close() + { + if (Root?.Manager != null) + { + var dockingManager = Root.Manager; + dockingManager.ExecuteCloseCommand(this); + } + else + CloseAnchorable(); + } #if TRACE /// @@ -298,24 +307,31 @@ public override void ConsoleDump(int tab) #endregion Overrides #region Public Methods + public void Hide() + { + if (Root?.Manager is DockingManager dockingManager) + dockingManager.ExecuteHideCommand(this); + else + HideAnchorable(true); + } /// Hide this contents. /// Add this content to collection of parent root. /// - public void Hide(bool cancelable = true) + internal bool HideAnchorable(bool cancelable) { if (!IsVisible) { IsSelected = true; IsActive = true; - return; + return false; } if (cancelable) { var args = new CancelEventArgs(); OnHiding(args); - if (args.Cancel) return; + if (args.Cancel) return false; } RaisePropertyChanging(nameof(IsHidden)); @@ -330,6 +346,8 @@ public void Hide(bool cancelable = true) RaisePropertyChanged(nameof(IsVisible)); RaisePropertyChanged(nameof(IsHidden)); NotifyIsVisibleChanged(); + + return true; } /// Show the content. diff --git a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs index 7244045f..b455b913 100644 --- a/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs +++ b/source/Components/AvalonDock/Layout/Serialization/LayoutSerializer.cs @@ -89,10 +89,10 @@ protected virtual void FixupLayout(LayoutRoot layout) else if (args.Content != null) lcToFix.Content = args.Content; else if (args.Model.Content != null) - lcToFix.Hide(false); // hide layoutanchorable if client app supplied no content + lcToFix.HideAnchorable(false); // hide layoutanchorable if client app supplied no content } else if (previousAchorable == null) // No Callback and no provious document -> skip this - lcToFix.Hide(false); + lcToFix.HideAnchorable(false); else { // No Callback but previous anchoreable available -> load content from previous document lcToFix.Content = previousAchorable.Content; From db7d8693b4abfaf0a00f80c6b2436ab0b161e61b Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 22 Dec 2022 20:32:02 +0800 Subject: [PATCH 24/53] Activate the pane when a floating window is active/inactive - Add handling for single/multiple pane activations --- .../LayoutDocumentFloatingWindowControl.cs | 110 ++++++++++++++++-- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 13845d3c..17ea136c 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -113,23 +113,117 @@ private void Model_PropertyChanged(object sender, System.ComponentModel.Property if (e.PropertyName == nameof(LayoutDocumentFloatingWindow.RootPanel) && _model.RootPanel == null) InternalClose(); } + private void ActiveOfSinglePane(bool isActive) + { + var layoutDocumentPane = _model.Descendents().OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + + layoutDocumentPane.SelectedContent.IsActive = isActive; + } + + private static LayoutDocumentPaneControl FindDocumentPaneControlByPoint(IEnumerable areaHosts, Point point) + { + foreach (var areaHost in areaHosts) + { + var area = areaHost.GetScreenArea(); + var pos = areaHost.TransformFromDeviceDPI(point); + var b = area.Contains(pos); + + if (b) + { + return areaHost; + } + } + + return null; + } + + private void ActiveOfMultiPane(bool isActive) + { + var mousePosition = Win32Helper.GetMousePosition(); + var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; + var areaHosts = rootVisual.FindVisualChildren(); + + if (isActive) + { + var documentPane = FindDocumentPaneControlByPoint(areaHosts, mousePosition); + if (documentPane != null) + { + var model = (LayoutDocumentPane)documentPane.Model; + if (model.SelectedContent != null) + { + model.SelectedContent.IsActive = true; + return; + } + // AnchorablePane + else + { + var index = 0; + for (var i = 0; i < model.Children.Count; i++) + { + var item = model.Children[i]; + if (item.IsLastFocusedDocument) + { + index = i; + } + } + + model.SelectedContentIndex = index; + return; + } + } + else + { + // Active the Last Focus + foreach (var areaHost in areaHosts) + { + var model = (LayoutDocumentPane)areaHost.Model; + for (var i = 0; i < model.Children.Count; i++) + { + var item = model.Children[i]; + if (item.IsLastFocusedDocument) + { + item.IsActive = true; + return; + } + } + } + } + } + else + { + foreach (var areaHost in areaHosts) + { + var model = (LayoutDocumentPane)areaHost.Model; + for (var i = 0; i < model.Children.Count; i++) + { + var item = model.Children[i]; + if (item.IsActive) + { + item.IsActive = false; + } + } + } + } + } + /// protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case Win32Helper.WM_ACTIVATE: - var layoutDocumentPane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - - if (layoutDocumentPane != null) + var isInactive = ((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE; + if (_model.IsSinglePane) { - var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); - layoutDocumentPane.SelectedContent.IsActive = isActive; - - handled = true; + ActiveOfSinglePane(!isInactive); + } + else + { + ActiveOfMultiPane(!isInactive); } + handled = true; break; case Win32Helper.WM_NCRBUTTONUP: From 9054d3a93be9a189d9e3a82e02e69842923c617e Mon Sep 17 00:00:00 2001 From: Noisrev Date: Sat, 24 Dec 2022 21:30:13 +0800 Subject: [PATCH 25/53] Fixed an issue where the dragged window still appeared above the overlay window --- source/Components/AvalonDock/Controls/DragService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index 2b5d18e8..e01d5f96 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -196,6 +196,7 @@ internal void UpdateMouseLocation(Point dragPosition) if (_currentDropTarget != null) { _currentWindow.DragEnter(_currentDropTarget); + BringWindowToTop2((Window)_currentWindow); return; } }); From 2c5c80ce4ab87af1c8bd724c96b83b74506c73ed Mon Sep 17 00:00:00 2001 From: Noisrev Date: Sat, 24 Dec 2022 21:30:13 +0800 Subject: [PATCH 26/53] Fix a issue where the dragged window still appeared above the overlay window --- source/Components/AvalonDock/Controls/DragService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index 2b5d18e8..e01d5f96 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -196,6 +196,7 @@ internal void UpdateMouseLocation(Point dragPosition) if (_currentDropTarget != null) { _currentWindow.DragEnter(_currentDropTarget); + BringWindowToTop2((Window)_currentWindow); return; } }); From 6541b1e35d8a0958b4a7f40d8973d95583e95eee Mon Sep 17 00:00:00 2001 From: Noisrev Date: Sat, 24 Dec 2022 21:33:07 +0800 Subject: [PATCH 27/53] Add null check for BringWindowToTop2 --- source/Components/AvalonDock/Controls/DragService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index e01d5f96..96e6c641 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -279,6 +279,8 @@ internal void Abort() private void BringWindowToTop2(Window window) { + if (window == null) return; + Win32Helper.SetWindowPos(new WindowInteropHelper(window).Handle, IntPtr.Zero, 0, 0, 0, 0, Win32Helper.SetWindowPosFlags.IgnoreResize | Win32Helper.SetWindowPosFlags.IgnoreMove | Win32Helper.SetWindowPosFlags.DoNotActivate); } From edda01824c4942d689710573d83920f9d0444e43 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sun, 25 Dec 2022 20:17:36 +0100 Subject: [PATCH 28/53] AvalonDock Version 4.71.0 --- README.md | 20 +++++++++++++++++++ .../AvalonDock.Themes.Aero.csproj | 8 ++++---- .../AvalonDock.Themes.Expression.csproj | 10 +++++----- .../AvalonDock.Themes.Metro.csproj | 10 +++++----- .../AvalonDock.Themes.VS2010.csproj | 8 ++++---- .../AvalonDock.Themes.VS2013.csproj | 10 +++++----- .../Components/AvalonDock/AvalonDock.csproj | 8 ++++---- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 5fdeff4c..579c2d02 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,26 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History +## Fixes Added in Version 4.71.0 + +- [#399 add open/close LayoutFlayoutingWindowsControl events](https://github.com/Dirkster99/AvalonDock/pull/399) (thanx to [https://github.com/GonzRu](Denis Smirnov)) + +- [#400 set ResizeOverlay's owner always null](https://github.com/Dirkster99/AvalonDock/pull/400) (thanx to [https://github.com/GonzRu](Denis Smirnov)) + +- [#401 remove unused variable from DocumentPaneTabPanel](https://github.com/Dirkster99/AvalonDock/pull/401) (thanx to [https://github.com/GonzRu](Denis Smirnov)) + +- [#403 Add XmlSerializer cache to fix memory leaks.](https://github.com/Dirkster99/AvalonDock/pull/403) (thanx to [https://github.com/pkindruk](Pavel Kindruk)) + +- [#404 Fix deserialized layout document close.](https://github.com/Dirkster99/AvalonDock/pull/404) (thanx to [https://github.com/pkindruk](Pavel Kindruk)) + +- [#409 Restore previously activated document after closing active document](https://github.com/Dirkster99/AvalonDock/pull/409) (thanx to [https://github.com/L45eMy](L45eMy)) + +- [#410 Improved activation of floating Windows](https://github.com/Dirkster99/AvalonDock/pull/410) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#411 Add anchorable hide and close notifications to DockingManager](https://github.com/Dirkster99/AvalonDock/pull/411) (thanx to [https://github.com/stewienj](John Stewien)) + +- [#412 Fix a issue where the dragged window still appeared above the overlay window](https://github.com/Dirkster99/AvalonDock/pull/412) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + ## Fixes Added in Version 4.70.3 - [#394 Fix the get owner DockingManagerWindow and Update drag and drop](https://github.com/Dirkster99/AvalonDock/pull/394) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 812d2559..acf76cde 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index f167e031..7c18b96f 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index d64aaf06..b7b13638 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index 09db8298..3308ed84 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index 68ca2a8d..db31789a 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index 4b5de5ca..b5adbbd6 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,10 +6,10 @@ true sn.snk true - 4.70.3 - 4.70.3 - 4.70.3 - 4.70.3 + 4.71.0 + 4.71.0 + 4.71.0 + 4.71.0 Open Source Dirkster.AvalonDock 2017-2022 From 1d4d30d9969236c3717b8a6f430809a230ff671a Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sun, 25 Dec 2022 20:29:17 +0100 Subject: [PATCH 29/53] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 579c2d02..a5be5479 100644 --- a/README.md +++ b/README.md @@ -122,21 +122,21 @@ to also theme standard elements, such as, button and textblock etc. ## Fixes Added in Version 4.71.0 -- [#399 add open/close LayoutFlayoutingWindowsControl events](https://github.com/Dirkster99/AvalonDock/pull/399) (thanx to [https://github.com/GonzRu](Denis Smirnov)) +- [#399 add open/close LayoutFlayoutingWindowsControl events](https://github.com/Dirkster99/AvalonDock/pull/399) (thanx to [Denis Smirnov](https://github.com/GonzRu)) -- [#400 set ResizeOverlay's owner always null](https://github.com/Dirkster99/AvalonDock/pull/400) (thanx to [https://github.com/GonzRu](Denis Smirnov)) +- [#400 set ResizeOverlay's owner always null](https://github.com/Dirkster99/AvalonDock/pull/400) (thanx to [Denis Smirnov](https://github.com/GonzRu)) -- [#401 remove unused variable from DocumentPaneTabPanel](https://github.com/Dirkster99/AvalonDock/pull/401) (thanx to [https://github.com/GonzRu](Denis Smirnov)) +- [#401 remove unused variable from DocumentPaneTabPanel](https://github.com/Dirkster99/AvalonDock/pull/401) (thanx to [Denis Smirnov](https://github.com/GonzRu)) -- [#403 Add XmlSerializer cache to fix memory leaks.](https://github.com/Dirkster99/AvalonDock/pull/403) (thanx to [https://github.com/pkindruk](Pavel Kindruk)) +- [#403 Add XmlSerializer cache to fix memory leaks.](https://github.com/Dirkster99/AvalonDock/pull/403) (thanx to [Pavel Kindruk](https://github.com/pkindruk)) -- [#404 Fix deserialized layout document close.](https://github.com/Dirkster99/AvalonDock/pull/404) (thanx to [https://github.com/pkindruk](Pavel Kindruk)) +- [#404 Fix deserialized layout document close.](https://github.com/Dirkster99/AvalonDock/pull/404) (thanx to [Pavel Kindruk](https://github.com/pkindruk)) -- [#409 Restore previously activated document after closing active document](https://github.com/Dirkster99/AvalonDock/pull/409) (thanx to [https://github.com/L45eMy](L45eMy)) +- [#409 Restore previously activated document after closing active document](https://github.com/Dirkster99/AvalonDock/pull/409) (thanx to [L45eMy](https://github.com/L45eMy)) - [#410 Improved activation of floating Windows](https://github.com/Dirkster99/AvalonDock/pull/410) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) -- [#411 Add anchorable hide and close notifications to DockingManager](https://github.com/Dirkster99/AvalonDock/pull/411) (thanx to [https://github.com/stewienj](John Stewien)) +- [#411 Add anchorable hide and close notifications to DockingManager](https://github.com/Dirkster99/AvalonDock/pull/411) (thanx to [John Stewien](https://github.com/stewienj)) - [#412 Fix a issue where the dragged window still appeared above the overlay window](https://github.com/Dirkster99/AvalonDock/pull/412) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) From c444860cb62a6d20f6230ad16f6e29ab1fd2a2f4 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 26 Dec 2022 11:11:06 +0800 Subject: [PATCH 30/53] Improved and fix floating window activation and activation pane - Use LastActivationTimeStamp instead of IsLastFocusedDocument --- .../LayoutDocumentFloatingWindowControl.cs | 105 ++++++++++-------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 17ea136c..d3b4124e 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -118,7 +118,15 @@ private void ActiveOfSinglePane(bool isActive) var layoutDocumentPane = _model.Descendents().OfType() .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - layoutDocumentPane.SelectedContent.IsActive = isActive; + if (layoutDocumentPane != null) + { + layoutDocumentPane.SelectedContent.IsActive = isActive; + } + // + else + { + ActiveLastActivationOfItems(isActive); + } } private static LayoutDocumentPaneControl FindDocumentPaneControlByPoint(IEnumerable areaHosts, Point point) @@ -127,7 +135,7 @@ private static LayoutDocumentPaneControl FindDocumentPaneControlByPoint(IEnumera { var area = areaHost.GetScreenArea(); var pos = areaHost.TransformFromDeviceDPI(point); - var b = area.Contains(pos); + var b = area.Contains(point); if (b) { @@ -138,6 +146,53 @@ private static LayoutDocumentPaneControl FindDocumentPaneControlByPoint(IEnumera return null; } + private void ActiveLastActivationOfPane(LayoutDocumentPane model) + { + if (model.Children.Count > 0) + { + var index = 0; + if (model.Children.Count > 1) + { + var tmTimeStamp = model.Children[0].LastActivationTimeStamp; + for (var i = 1; i < model.Children.Count; i++) + { + var item = model.Children[i]; + if (item.LastActivationTimeStamp > tmTimeStamp) + { + tmTimeStamp = item.LastActivationTimeStamp; + index = i; + } + } + } + + model.SelectedContentIndex = index; + } + } + + private void ActiveLastActivationOfItems(bool isActive) + { + var items = _model.Descendents().OfType().ToList(); + if (items.Count > 0) + { + var index = 0; + if (items.Count > 1) + { + var tmpTimeStamp2 = items[0].LastActivationTimeStamp; + for (var i = 1; i < items.Count; i++) + { + var item = items[i]; + if (item.LastActivationTimeStamp > tmpTimeStamp2) + { + tmpTimeStamp2 = item.LastActivationTimeStamp; + index = i; + } + } + } + + items[index].IsActive = isActive; + } + } + private void ActiveOfMultiPane(bool isActive) { var mousePosition = Win32Helper.GetMousePosition(); @@ -155,56 +210,14 @@ private void ActiveOfMultiPane(bool isActive) model.SelectedContent.IsActive = true; return; } - // AnchorablePane else { - var index = 0; - for (var i = 0; i < model.Children.Count; i++) - { - var item = model.Children[i]; - if (item.IsLastFocusedDocument) - { - index = i; - } - } - - model.SelectedContentIndex = index; + ActiveLastActivationOfPane(model); return; } } - else - { - // Active the Last Focus - foreach (var areaHost in areaHosts) - { - var model = (LayoutDocumentPane)areaHost.Model; - for (var i = 0; i < model.Children.Count; i++) - { - var item = model.Children[i]; - if (item.IsLastFocusedDocument) - { - item.IsActive = true; - return; - } - } - } - } - } - else - { - foreach (var areaHost in areaHosts) - { - var model = (LayoutDocumentPane)areaHost.Model; - for (var i = 0; i < model.Children.Count; i++) - { - var item = model.Children[i]; - if (item.IsActive) - { - item.IsActive = false; - } - } - } } + ActiveLastActivationOfItems(isActive); } /// From f721a5dcaf576393f69701670d19735566829b75 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 26 Dec 2022 11:17:36 +0800 Subject: [PATCH 31/53] Change the scope of variables to avoid unused variables --- .../LayoutDocumentFloatingWindowControl.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index d3b4124e..5984a739 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -129,13 +129,17 @@ private void ActiveOfSinglePane(bool isActive) } } - private static LayoutDocumentPaneControl FindDocumentPaneControlByPoint(IEnumerable areaHosts, Point point) + private LayoutDocumentPaneControl FindDocumentPaneControlByMousePoint() { + var mousePosition = Win32Helper.GetMousePosition(); + var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; + var areaHosts = rootVisual.FindVisualChildren(); + foreach (var areaHost in areaHosts) { var area = areaHost.GetScreenArea(); - var pos = areaHost.TransformFromDeviceDPI(point); - var b = area.Contains(point); + var pos = areaHost.TransformFromDeviceDPI(mousePosition); + var b = area.Contains(pos); if (b) { @@ -195,13 +199,9 @@ private void ActiveLastActivationOfItems(bool isActive) private void ActiveOfMultiPane(bool isActive) { - var mousePosition = Win32Helper.GetMousePosition(); - var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; - var areaHosts = rootVisual.FindVisualChildren(); - if (isActive) { - var documentPane = FindDocumentPaneControlByPoint(areaHosts, mousePosition); + var documentPane = FindDocumentPaneControlByMousePoint(); if (documentPane != null) { var model = (LayoutDocumentPane)documentPane.Model; From cbf6f925c29a42597905a36ac8d0a8e31a193721 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 26 Dec 2022 11:26:05 +0800 Subject: [PATCH 32/53] Add code comment --- .../Controls/LayoutDocumentFloatingWindowControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 5984a739..c25e67e7 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -122,7 +122,13 @@ private void ActiveOfSinglePane(bool isActive) { layoutDocumentPane.SelectedContent.IsActive = isActive; } - // + // When the floating tool window is mixed with the floating document window + // and the document pane in the floating document window is dragged out. + + // Only the Tool panes is left in the floating document window. + // The Children Count is greater than 0 and the Selected Content is null. + + // Then we only need to activate the last active content. else { ActiveLastActivationOfItems(isActive); From d4f05a9b100f389cda49c1162fb5434d47734a7a Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 26 Dec 2022 13:54:18 +0800 Subject: [PATCH 33/53] Fix the binding error in AnchorGroupTemplate --- .../AvalonDock.Themes.Aero/Theme.xaml | 30 +++++++++++------ .../AvalonDock.Themes.Expression/Theme.xaml | 18 +++++++++-- .../AvalonDock.Themes.Metro/Theme.xaml | 18 +++++++++-- .../AvalonDock.Themes.VS2010/Theme.xaml | 18 +++++++++-- .../Themes/Generic.xaml | 32 +++++++++++++------ .../Components/AvalonDock/Themes/generic.xaml | 18 +++++++++-- 6 files changed, 103 insertions(+), 31 deletions(-) diff --git a/source/Components/AvalonDock.Themes.Aero/Theme.xaml b/source/Components/AvalonDock.Themes.Aero/Theme.xaml index b12003a0..caf5abe7 100644 --- a/source/Components/AvalonDock.Themes.Aero/Theme.xaml +++ b/source/Components/AvalonDock.Themes.Aero/Theme.xaml @@ -481,15 +481,27 @@ - - - - - - - - - + + + + + + + + + diff --git a/source/Components/AvalonDock.Themes.Expression/Theme.xaml b/source/Components/AvalonDock.Themes.Expression/Theme.xaml index b4f91e75..8222ce68 100644 --- a/source/Components/AvalonDock.Themes.Expression/Theme.xaml +++ b/source/Components/AvalonDock.Themes.Expression/Theme.xaml @@ -485,14 +485,26 @@ - - - + + + diff --git a/source/Components/AvalonDock.Themes.Metro/Theme.xaml b/source/Components/AvalonDock.Themes.Metro/Theme.xaml index 32a3a177..52021b3c 100644 --- a/source/Components/AvalonDock.Themes.Metro/Theme.xaml +++ b/source/Components/AvalonDock.Themes.Metro/Theme.xaml @@ -491,14 +491,26 @@ - - - + + + diff --git a/source/Components/AvalonDock.Themes.VS2010/Theme.xaml b/source/Components/AvalonDock.Themes.VS2010/Theme.xaml index 9508c1b5..1748242f 100644 --- a/source/Components/AvalonDock.Themes.VS2010/Theme.xaml +++ b/source/Components/AvalonDock.Themes.VS2010/Theme.xaml @@ -545,14 +545,26 @@ - - - + + + diff --git a/source/Components/AvalonDock.Themes.VS2013/Themes/Generic.xaml b/source/Components/AvalonDock.Themes.VS2013/Themes/Generic.xaml index cca18e3a..e62e831c 100644 --- a/source/Components/AvalonDock.Themes.VS2013/Themes/Generic.xaml +++ b/source/Components/AvalonDock.Themes.VS2013/Themes/Generic.xaml @@ -820,16 +820,28 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/source/Components/AvalonDock/Themes/generic.xaml b/source/Components/AvalonDock/Themes/generic.xaml index 3c7ca2ee..68df206a 100644 --- a/source/Components/AvalonDock/Themes/generic.xaml +++ b/source/Components/AvalonDock/Themes/generic.xaml @@ -390,14 +390,26 @@ - - - + + + From 11043c9ea9a28b15b720557a8bd70c431d7658f4 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Mon, 26 Dec 2022 15:11:22 +0800 Subject: [PATCH 34/53] Add a Deactivated event handler in the Navigator Window - Close the window when it is not active and activate the selected content --- .../AvalonDock/Controls/NavigatorWindow.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/Controls/NavigatorWindow.cs b/source/Components/AvalonDock/Controls/NavigatorWindow.cs index ccfa3562..7deee8ca 100644 --- a/source/Components/AvalonDock/Controls/NavigatorWindow.cs +++ b/source/Components/AvalonDock/Controls/NavigatorWindow.cs @@ -98,6 +98,7 @@ internal NavigatorWindow(DockingManager manager) DataContext = this; Loaded += OnLoaded; Unloaded += OnUnloaded; + Deactivated += OnDeactivated; UpdateThemeResources(); } @@ -380,11 +381,7 @@ protected override void OnKeyUp(KeyEventArgs e) { if (!(e.Key == Key.Tab || e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down)) { - Close(); - if (SelectedDocument != null && SelectedDocument.ActivateCommand.CanExecute(null)) - SelectedDocument.ActivateCommand.Execute(null); - if (SelectedDocument == null && SelectedAnchorable != null && SelectedAnchorable.ActivateCommand.CanExecute(null)) - SelectedAnchorable.ActivateCommand.Execute(null); + CloseAndActiveSelected(); e.Handled = true; } base.OnKeyUp(e); @@ -520,6 +517,21 @@ private void OnLoaded(object sender, RoutedEventArgs e) private void OnUnloaded(object sender, RoutedEventArgs e) => Unloaded -= OnUnloaded; + private void OnDeactivated(object sender, EventArgs e) + { + CloseAndActiveSelected(); + } + + private void CloseAndActiveSelected() + { + Deactivated -= OnDeactivated; + Close(); + if (SelectedDocument != null && SelectedDocument.ActivateCommand.CanExecute(null)) + SelectedDocument.ActivateCommand.Execute(null); + if (SelectedDocument == null && SelectedAnchorable != null && SelectedAnchorable.ActivateCommand.CanExecute(null)) + SelectedAnchorable.ActivateCommand.Execute(null); + } + private void FocusSelectedItem(ListBox list) { if (list.SelectedIndex >= 0) From 5f42e5cc0ba0a11cae1b3503d18e7f12c4d69a0f Mon Sep 17 00:00:00 2001 From: Noisrev Date: Tue, 27 Dec 2022 00:04:25 +0800 Subject: [PATCH 35/53] When apply new template, add back collection change event handler --- .../Controls/LayoutAnchorSideControl.cs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs index c955ba79..e0453eed 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -46,11 +46,6 @@ static LayoutAnchorSideControl() internal LayoutAnchorSideControl(LayoutAnchorSide model) { _model = model ?? throw new ArgumentNullException(nameof(model)); - CreateChildrenViews(); - _model.Children.CollectionChanged += OnModelChildrenCollectionChanged; - UpdateSide(); - - Unloaded += LayoutAnchorSideControl_Unloaded; } #endregion Constructors @@ -137,11 +132,27 @@ internal LayoutAnchorSideControl(LayoutAnchorSide model) #region Private Methods - /// - /// Executes when the element is removed from within an element tree of loaded elements. - /// - /// - /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + Loaded += LayoutAnchorSideControl_Loaded; + } + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + CreateChildrenViews(); + UpdateSide(); + } + + private void LayoutAnchorSideControl_Loaded(object sender, RoutedEventArgs e) + { + Loaded -= LayoutAnchorSideControl_Loaded; + Unloaded += LayoutAnchorSideControl_Unloaded; + _model.Children.CollectionChanged += OnModelChildrenCollectionChanged; + } + private void LayoutAnchorSideControl_Unloaded(object sender, RoutedEventArgs e) { _model.Children.CollectionChanged -= OnModelChildrenCollectionChanged; From 1ea019948c8e7272e2760c0731ba2921c4125c56 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Mon, 26 Dec 2022 18:46:17 +0100 Subject: [PATCH 36/53] AvalonDock Version 4.71.1 --- .../AvalonDock.Themes.Aero.csproj | 10 +++++----- .../AvalonDock.Themes.Expression.csproj | 8 ++++---- .../AvalonDock.Themes.Metro.csproj | 10 +++++----- .../AvalonDock.Themes.VS2010.csproj | 10 +++++----- .../AvalonDock.Themes.VS2013.csproj | 8 ++++---- source/Components/AvalonDock/AvalonDock.csproj | 8 ++++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index acf76cde..8d4440b2 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index 7c18b96f..5246ad64 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index b7b13638..2283819a 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index 3308ed84..c16829a1 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index db31789a..e62f5075 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index b5adbbd6..f664aef8 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,10 +6,10 @@ true sn.snk true - 4.71.0 - 4.71.0 - 4.71.0 - 4.71.0 + 4.71.1 + 4.71.1 + 4.71.1 + 4.71.1 Open Source Dirkster.AvalonDock 2017-2022 From e9b337d18774970eae75a30ed93101102f8011cb Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Mon, 26 Dec 2022 19:35:42 +0100 Subject: [PATCH 37/53] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a5be5479..64e7077a 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,14 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History +## Fixes Added in Version 4.71.1 + +- [#413 Fix the binding error in AnchorGroupTemplate](https://github.com/Dirkster99/AvalonDock/pull/413) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#414 When apply new template, add back collection change event handler](https://github.com/Dirkster99/AvalonDock/pull/414) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#415 Improved and fix floating window activation and activation pane](https://github.com/Dirkster99/AvalonDock/pull/415) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + ## Fixes Added in Version 4.71.0 - [#399 add open/close LayoutFlayoutingWindowsControl events](https://github.com/Dirkster99/AvalonDock/pull/399) (thanx to [Denis Smirnov](https://github.com/GonzRu)) From df8109c8595a3dd7788659b19b41ba5207382713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=B6ller?= Date: Wed, 4 Jan 2023 17:43:39 +0100 Subject: [PATCH 38/53] Fixed Issue 226: Owned/maximized floating windows (LayoutFloatingWindowControl) do not restore to maximized state when the owner (DockingManager) window is restored from being minimized. --- .../Controls/LayoutFloatingWindowControl.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index c1377a92..725cf6aa 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -218,7 +218,21 @@ private set protected override void OnStateChanged(EventArgs e) { if (!_isInternalChange) - UpdateMaximizedState(WindowState == WindowState.Maximized); + { + if (WindowState == WindowState.Maximized) + { + // Forward external changes to WindowState from any state to a new Maximized state + // to the LayoutFloatingWindowControl internal representation. + UpdateMaximizedState(true); + } + else if (IsMaximized && OwnedByDockingManagerWindow) + { + // Override any external changes to WindowState when owned and in Maximized state. + // This override fixes the issue of an owned LayoutFloatingWindowControl loosing + // its Maximized state when the owner window is restored from a Minimized state. + WindowState = WindowState.Maximized; + } + } base.OnStateChanged(e); } From 895646cdf2195ec7b3739a536d99cb774ae73609 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 12 Jan 2023 22:37:38 +0800 Subject: [PATCH 39/53] Improve the active item handler and add to LayoutAnchorableFloatingWindow --- .../LayoutAnchorableFloatingWindowControl.cs | 143 ++++++++++- .../LayoutDocumentFloatingWindowControl.cs | 242 +++++++++--------- 2 files changed, 255 insertions(+), 130 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 75f417da..381b755a 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -7,10 +7,6 @@ This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at https://opensource.org/licenses/MS-PL ************************************************************************/ -using AvalonDock.Commands; -using AvalonDock.Converters; -using AvalonDock.Layout; -using Microsoft.Windows.Shell; using System; using System.Collections.Generic; using System.ComponentModel; @@ -20,6 +16,12 @@ This program is provided to you under the terms of the Microsoft Public using System.Windows.Data; using System.Windows.Input; +using AvalonDock.Commands; +using AvalonDock.Converters; +using AvalonDock.Layout; + +using Microsoft.Windows.Shell; + namespace AvalonDock.Controls { /// @@ -244,17 +246,17 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int switch (msg) { case Win32Helper.WM_ACTIVATE: - var anchorablePane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - - if (anchorablePane != null) + var isInactive = ((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE; + if (_model.IsSinglePane) { - var isActive = !(((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE); - anchorablePane.SelectedContent.IsActive = isActive; - - handled = true; + ActiveItemOfSinglePane(!isInactive); + } + else + { + ActiveItemOfMultiPane(!isInactive); } + handled = true; break; case Win32Helper.WM_NCRBUTTONUP: @@ -431,6 +433,123 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion CloseWindowCommand + #region ActiveItem + + private void ActiveItemOfSinglePane(bool isActive) + { + var layoutDocumentPane = _model.Descendents().OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + + if (layoutDocumentPane != null) + { + layoutDocumentPane.SelectedContent.IsActive = isActive; + } + // When the floating tool window is mixed with the floating document window + // and the document pane in the floating document window is dragged out. + + // Only the Tool panes is left in the floating document window. + // The Children Count is greater than 0 and the Selected Content is null. + + // Then we only need to activate the last active content. + else + { + ActiveTheLastActivedItemOfItems(isActive); + } + } + + private LayoutAnchorablePaneControl FindPaneControlByMousePoint() + { + var mousePosition = Win32Helper.GetMousePosition(); + var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; + var areaHosts = rootVisual.FindVisualChildren(); + + foreach (var areaHost in areaHosts) + { + var rect = areaHost.GetScreenArea(); + var pos = areaHost.TransformFromDeviceDPI(mousePosition); + var b = rect.Contains(pos); + + if (b) + { + return areaHost; + } + } + + return null; + } + + private static void ActiveTheLastActivedItemOfPane(LayoutAnchorablePane pane) + { + if (pane.Children.Count > 0) + { + var index = 0; + if (pane.Children.Count > 1) + { + var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; + for (var i = 1; i < pane.Children.Count; i++) + { + var item = pane.Children[i]; + if (item.LastActivationTimeStamp > tmTimeStamp) + { + tmTimeStamp = item.LastActivationTimeStamp; + index = i; + } + } + } + + pane.SelectedContentIndex = index; + } + } + + private void ActiveTheLastActivedItemOfItems(bool isActive) + { + var items = _model.Descendents().OfType().ToList(); + if (items.Count > 0) + { + var index = 0; + if (items.Count > 1) + { + var tmpTimeStamp2 = items[0].LastActivationTimeStamp; + for (var i = 1; i < items.Count; i++) + { + var item = items[i]; + if (item.LastActivationTimeStamp > tmpTimeStamp2) + { + tmpTimeStamp2 = item.LastActivationTimeStamp; + index = i; + } + } + } + + items[index].IsActive = isActive; + } + } + + private void ActiveItemOfMultiPane(bool isActive) + { + if (isActive) + { + var documentPane = FindPaneControlByMousePoint(); + if (documentPane != null) + { + var model = (LayoutAnchorablePane)documentPane.Model; + if (model.SelectedContent != null) + { + model.SelectedContent.IsActive = true; + return; + } + else + { + ActiveTheLastActivedItemOfPane(model); + return; + } + } + } + ActiveTheLastActivedItemOfItems(isActive); + } + + #endregion ActiveItem + #endregion Private Methods } } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index c25e67e7..baa4ce29 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -7,9 +7,6 @@ This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at https://opensource.org/licenses/MS-PL ************************************************************************/ -using AvalonDock.Commands; -using AvalonDock.Layout; -using Microsoft.Windows.Shell; using System; using System.Collections.Generic; using System.Linq; @@ -17,6 +14,11 @@ This program is provided to you under the terms of the Microsoft Public using System.Windows.Controls.Primitives; using System.Windows.Input; +using AvalonDock.Commands; +using AvalonDock.Layout; + +using Microsoft.Windows.Shell; + namespace AvalonDock.Controls { /// @@ -113,119 +115,6 @@ private void Model_PropertyChanged(object sender, System.ComponentModel.Property if (e.PropertyName == nameof(LayoutDocumentFloatingWindow.RootPanel) && _model.RootPanel == null) InternalClose(); } - private void ActiveOfSinglePane(bool isActive) - { - var layoutDocumentPane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - - if (layoutDocumentPane != null) - { - layoutDocumentPane.SelectedContent.IsActive = isActive; - } - // When the floating tool window is mixed with the floating document window - // and the document pane in the floating document window is dragged out. - - // Only the Tool panes is left in the floating document window. - // The Children Count is greater than 0 and the Selected Content is null. - - // Then we only need to activate the last active content. - else - { - ActiveLastActivationOfItems(isActive); - } - } - - private LayoutDocumentPaneControl FindDocumentPaneControlByMousePoint() - { - var mousePosition = Win32Helper.GetMousePosition(); - var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; - var areaHosts = rootVisual.FindVisualChildren(); - - foreach (var areaHost in areaHosts) - { - var area = areaHost.GetScreenArea(); - var pos = areaHost.TransformFromDeviceDPI(mousePosition); - var b = area.Contains(pos); - - if (b) - { - return areaHost; - } - } - - return null; - } - - private void ActiveLastActivationOfPane(LayoutDocumentPane model) - { - if (model.Children.Count > 0) - { - var index = 0; - if (model.Children.Count > 1) - { - var tmTimeStamp = model.Children[0].LastActivationTimeStamp; - for (var i = 1; i < model.Children.Count; i++) - { - var item = model.Children[i]; - if (item.LastActivationTimeStamp > tmTimeStamp) - { - tmTimeStamp = item.LastActivationTimeStamp; - index = i; - } - } - } - - model.SelectedContentIndex = index; - } - } - - private void ActiveLastActivationOfItems(bool isActive) - { - var items = _model.Descendents().OfType().ToList(); - if (items.Count > 0) - { - var index = 0; - if (items.Count > 1) - { - var tmpTimeStamp2 = items[0].LastActivationTimeStamp; - for (var i = 1; i < items.Count; i++) - { - var item = items[i]; - if (item.LastActivationTimeStamp > tmpTimeStamp2) - { - tmpTimeStamp2 = item.LastActivationTimeStamp; - index = i; - } - } - } - - items[index].IsActive = isActive; - } - } - - private void ActiveOfMultiPane(bool isActive) - { - if (isActive) - { - var documentPane = FindDocumentPaneControlByMousePoint(); - if (documentPane != null) - { - var model = (LayoutDocumentPane)documentPane.Model; - if (model.SelectedContent != null) - { - model.SelectedContent.IsActive = true; - return; - } - else - { - ActiveLastActivationOfPane(model); - return; - } - } - } - ActiveLastActivationOfItems(isActive); - } - /// protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { @@ -235,11 +124,11 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int var isInactive = ((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE; if (_model.IsSinglePane) { - ActiveOfSinglePane(!isInactive); + ActiveItemOfSinglePane(!isInactive); } else { - ActiveOfMultiPane(!isInactive); + ActiveItemOfMultiPane(!isInactive); } handled = true; @@ -543,6 +432,123 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion CloseWindowCommand + #region ActiveItem + + private void ActiveItemOfSinglePane(bool isActive) + { + var layoutDocumentPane = _model.Descendents().OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + + if (layoutDocumentPane != null) + { + layoutDocumentPane.SelectedContent.IsActive = isActive; + } + // When the floating tool window is mixed with the floating document window + // and the document pane in the floating document window is dragged out. + + // Only the Tool panes is left in the floating document window. + // The Children Count is greater than 0 and the Selected Content is null. + + // Then we only need to activate the last active content. + else + { + ActiveTheLastActivedItemOfItems(isActive); + } + } + + private LayoutDocumentPaneControl FindPaneControlByMousePoint() + { + var mousePosition = Win32Helper.GetMousePosition(); + var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; + var areaHosts = rootVisual.FindVisualChildren(); + + foreach (var areaHost in areaHosts) + { + var rect = areaHost.GetScreenArea(); + var pos = areaHost.TransformFromDeviceDPI(mousePosition); + var b = rect.Contains(pos); + + if (b) + { + return areaHost; + } + } + + return null; + } + + private static void ActiveTheLastActivedItemOfPane(LayoutDocumentPane pane) + { + if (pane.Children.Count > 0) + { + var index = 0; + if (pane.Children.Count > 1) + { + var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; + for (var i = 1; i < pane.Children.Count; i++) + { + var item = pane.Children[i]; + if (item.LastActivationTimeStamp > tmTimeStamp) + { + tmTimeStamp = item.LastActivationTimeStamp; + index = i; + } + } + } + + pane.SelectedContentIndex = index; + } + } + + private void ActiveTheLastActivedItemOfItems(bool isActive) + { + var items = _model.Descendents().OfType().ToList(); + if (items.Count > 0) + { + var index = 0; + if (items.Count > 1) + { + var tmpTimeStamp2 = items[0].LastActivationTimeStamp; + for (var i = 1; i < items.Count; i++) + { + var item = items[i]; + if (item.LastActivationTimeStamp > tmpTimeStamp2) + { + tmpTimeStamp2 = item.LastActivationTimeStamp; + index = i; + } + } + } + + items[index].IsActive = isActive; + } + } + + private void ActiveItemOfMultiPane(bool isActive) + { + if (isActive) + { + var documentPane = FindPaneControlByMousePoint(); + if (documentPane != null) + { + var model = (LayoutDocumentPane)documentPane.Model; + if (model.SelectedContent != null) + { + model.SelectedContent.IsActive = true; + return; + } + else + { + ActiveTheLastActivedItemOfPane(model); + return; + } + } + } + ActiveTheLastActivedItemOfItems(isActive); + } + + #endregion + #endregion Private Methods } } From b63b9668884dc4b910f4dfd77a40e177dcfd93cc Mon Sep 17 00:00:00 2001 From: Noisrev Date: Thu, 12 Jan 2023 23:01:28 +0800 Subject: [PATCH 40/53] Fix types and variable names --- .../LayoutAnchorableFloatingWindowControl.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 381b755a..84743360 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -437,12 +437,12 @@ private void OnExecuteCloseWindowCommand(object parameter) private void ActiveItemOfSinglePane(bool isActive) { - var layoutDocumentPane = _model.Descendents().OfType() + var layoutAnchorablePane = _model.Descendents().OfType() .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - if (layoutDocumentPane != null) + if (layoutAnchorablePane != null) { - layoutDocumentPane.SelectedContent.IsActive = isActive; + layoutAnchorablePane.SelectedContent.IsActive = isActive; } // When the floating tool window is mixed with the floating document window // and the document pane in the floating document window is dragged out. @@ -529,10 +529,10 @@ private void ActiveItemOfMultiPane(bool isActive) { if (isActive) { - var documentPane = FindPaneControlByMousePoint(); - if (documentPane != null) + var anchorablePane = FindPaneControlByMousePoint(); + if (anchorablePane != null) { - var model = (LayoutAnchorablePane)documentPane.Model; + var model = (LayoutAnchorablePane)anchorablePane.Model; if (model.SelectedContent != null) { model.SelectedContent.IsActive = true; From fa58dfe01aeb1c963ee61d6f5acd82d9560d25c1 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Fri, 13 Jan 2023 00:17:23 +0800 Subject: [PATCH 41/53] Improve and optimize the active item handlers --- .../LayoutAnchorableFloatingWindowControl.cs | 86 ++------------ .../LayoutDocumentFloatingWindowControl.cs | 88 ++------------ .../Controls/LayoutFloatingWindowControl.cs | 108 +++++++++++++++--- 3 files changed, 108 insertions(+), 174 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 84743360..29ec53a9 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -435,14 +435,14 @@ private void OnExecuteCloseWindowCommand(object parameter) #region ActiveItem - private void ActiveItemOfSinglePane(bool isActive) + internal void ActiveItemOfSinglePane(bool isActive) { - var layoutAnchorablePane = _model.Descendents().OfType() + var pane = _model.Descendents().OfType() .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - if (layoutAnchorablePane != null) + if (pane != null) { - layoutAnchorablePane.SelectedContent.IsActive = isActive; + pane.SelectedContent.IsActive = isActive; } // When the floating tool window is mixed with the floating document window // and the document pane in the floating document window is dragged out. @@ -457,82 +457,14 @@ private void ActiveItemOfSinglePane(bool isActive) } } - private LayoutAnchorablePaneControl FindPaneControlByMousePoint() - { - var mousePosition = Win32Helper.GetMousePosition(); - var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; - var areaHosts = rootVisual.FindVisualChildren(); - - foreach (var areaHost in areaHosts) - { - var rect = areaHost.GetScreenArea(); - var pos = areaHost.TransformFromDeviceDPI(mousePosition); - var b = rect.Contains(pos); - - if (b) - { - return areaHost; - } - } - - return null; - } - - private static void ActiveTheLastActivedItemOfPane(LayoutAnchorablePane pane) - { - if (pane.Children.Count > 0) - { - var index = 0; - if (pane.Children.Count > 1) - { - var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; - for (var i = 1; i < pane.Children.Count; i++) - { - var item = pane.Children[i]; - if (item.LastActivationTimeStamp > tmTimeStamp) - { - tmTimeStamp = item.LastActivationTimeStamp; - index = i; - } - } - } - - pane.SelectedContentIndex = index; - } - } - - private void ActiveTheLastActivedItemOfItems(bool isActive) - { - var items = _model.Descendents().OfType().ToList(); - if (items.Count > 0) - { - var index = 0; - if (items.Count > 1) - { - var tmpTimeStamp2 = items[0].LastActivationTimeStamp; - for (var i = 1; i < items.Count; i++) - { - var item = items[i]; - if (item.LastActivationTimeStamp > tmpTimeStamp2) - { - tmpTimeStamp2 = item.LastActivationTimeStamp; - index = i; - } - } - } - - items[index].IsActive = isActive; - } - } - - private void ActiveItemOfMultiPane(bool isActive) + internal void ActiveItemOfMultiPane(bool isActive) { if (isActive) { - var anchorablePane = FindPaneControlByMousePoint(); - if (anchorablePane != null) + var paneControl = FindPaneControlByMousePoint(); + if (paneControl != null) { - var model = (LayoutAnchorablePane)anchorablePane.Model; + var model = (LayoutAnchorablePane)paneControl.Model; if (model.SelectedContent != null) { model.SelectedContent.IsActive = true; @@ -540,7 +472,7 @@ private void ActiveItemOfMultiPane(bool isActive) } else { - ActiveTheLastActivedItemOfPane(model); + ActiveTheLastActivedItemOfPane(model); return; } } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index baa4ce29..18fada33 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -434,14 +434,14 @@ private void OnExecuteCloseWindowCommand(object parameter) #region ActiveItem - private void ActiveItemOfSinglePane(bool isActive) + internal void ActiveItemOfSinglePane(bool isActive) { - var layoutDocumentPane = _model.Descendents().OfType() + var pane = _model.Descendents().OfType() .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - if (layoutDocumentPane != null) + if (pane != null) { - layoutDocumentPane.SelectedContent.IsActive = isActive; + pane.SelectedContent.IsActive = isActive; } // When the floating tool window is mixed with the floating document window // and the document pane in the floating document window is dragged out. @@ -456,82 +456,14 @@ private void ActiveItemOfSinglePane(bool isActive) } } - private LayoutDocumentPaneControl FindPaneControlByMousePoint() - { - var mousePosition = Win32Helper.GetMousePosition(); - var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; - var areaHosts = rootVisual.FindVisualChildren(); - - foreach (var areaHost in areaHosts) - { - var rect = areaHost.GetScreenArea(); - var pos = areaHost.TransformFromDeviceDPI(mousePosition); - var b = rect.Contains(pos); - - if (b) - { - return areaHost; - } - } - - return null; - } - - private static void ActiveTheLastActivedItemOfPane(LayoutDocumentPane pane) - { - if (pane.Children.Count > 0) - { - var index = 0; - if (pane.Children.Count > 1) - { - var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; - for (var i = 1; i < pane.Children.Count; i++) - { - var item = pane.Children[i]; - if (item.LastActivationTimeStamp > tmTimeStamp) - { - tmTimeStamp = item.LastActivationTimeStamp; - index = i; - } - } - } - - pane.SelectedContentIndex = index; - } - } - - private void ActiveTheLastActivedItemOfItems(bool isActive) - { - var items = _model.Descendents().OfType().ToList(); - if (items.Count > 0) - { - var index = 0; - if (items.Count > 1) - { - var tmpTimeStamp2 = items[0].LastActivationTimeStamp; - for (var i = 1; i < items.Count; i++) - { - var item = items[i]; - if (item.LastActivationTimeStamp > tmpTimeStamp2) - { - tmpTimeStamp2 = item.LastActivationTimeStamp; - index = i; - } - } - } - - items[index].IsActive = isActive; - } - } - - private void ActiveItemOfMultiPane(bool isActive) + internal void ActiveItemOfMultiPane(bool isActive) { if (isActive) { - var documentPane = FindPaneControlByMousePoint(); - if (documentPane != null) + var paneControl = FindPaneControlByMousePoint(); + if (paneControl != null) { - var model = (LayoutDocumentPane)documentPane.Model; + var model = (LayoutDocumentPane)paneControl.Model; if (model.SelectedContent != null) { model.SelectedContent.IsActive = true; @@ -539,7 +471,7 @@ private void ActiveItemOfMultiPane(bool isActive) } else { - ActiveTheLastActivedItemOfPane(model); + ActiveTheLastActivedItemOfPane(model); return; } } @@ -547,7 +479,7 @@ private void ActiveItemOfMultiPane(bool isActive) ActiveTheLastActivedItemOfItems(isActive); } - #endregion + #endregion ActiveItem #endregion Private Methods } diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index c1377a92..fdc308d8 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -7,8 +7,6 @@ This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at https://opensource.org/licenses/MS-PL ************************************************************************/ -using AvalonDock.Layout; -using AvalonDock.Themes; using System; using System.ComponentModel; using System.Linq; @@ -22,6 +20,9 @@ This program is provided to you under the terms of the Microsoft Public using System.Windows.Interop; using System.Windows.Media; +using AvalonDock.Layout; +using AvalonDock.Themes; + namespace AvalonDock.Controls { /// @@ -52,7 +53,7 @@ public abstract class LayoutFloatingWindowControl : Window, ILayoutControl /// /// private bool _isTotalMarginSet = false; - + #endregion fields #region Constructors @@ -491,6 +492,75 @@ internal void InternalClose(bool closeInitiatedByUser = false) Close(); } + internal T FindPaneControlByMousePoint() where T : FrameworkElement, ILayoutControl + { + var mousePosition = this.PointToScreenDPI(Mouse.GetPosition(this)); + var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; + var areaHosts = rootVisual.FindVisualChildren(); + + foreach (var areaHost in areaHosts) + { + var rect = areaHost.GetScreenArea(); + var b = rect.Contains(mousePosition); + + if (b) + { + return areaHost; + } + } + + return null; + } + + internal void ActiveTheLastActivedItemOfItems(bool isActive) + { + var items = _model.Descendents().OfType().ToList(); + if (items.Count > 0) + { + var index = 0; + if (items.Count > 1) + { + var tmpTimeStamp2 = items[0].LastActivationTimeStamp; + for (var i = 1; i < items.Count; i++) + { + var item = items[i]; + if (item.LastActivationTimeStamp > tmpTimeStamp2) + { + tmpTimeStamp2 = item.LastActivationTimeStamp; + index = i; + } + } + } + + items[index].IsActive = isActive; + } + } + + internal static void ActiveTheLastActivedItemOfPane(TPane pane) + where TPane : LayoutPositionableGroup, ILayoutContentSelector + where TChild : LayoutContent + { + if (pane.Children.Count > 0) + { + var index = 0; + if (pane.Children.Count > 1) + { + var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; + for (var i = 1; i < pane.Children.Count; i++) + { + var item = pane.Children[i]; + if (item.LastActivationTimeStamp > tmTimeStamp) + { + tmTimeStamp = item.LastActivationTimeStamp; + index = i; + } + } + } + + pane.SelectedContentIndex = index; + } + } + #endregion Internal Methods #region Overrides @@ -528,24 +598,24 @@ protected override void OnInitialized(EventArgs e) } protected override void OnClosing(CancelEventArgs e) - { - base.OnClosing(e); - AssureOwnerIsNotMinimized(); - } + { + base.OnClosing(e); + AssureOwnerIsNotMinimized(); + } - /// + /// /// Prevents a known bug in WPF, which wronlgy minimizes the parent window, when closing this control - /// - private void AssureOwnerIsNotMinimized() - { - try - { - Owner?.Activate(); - } - catch (Exception) - { - } - } + /// + private void AssureOwnerIsNotMinimized() + { + try + { + Owner?.Activate(); + } + catch (Exception) + { + } + } #endregion Overrides From ce7de8d94ab28e6dc364fb7717f72ca0a7cec026 Mon Sep 17 00:00:00 2001 From: Noisrev Date: Tue, 24 Jan 2023 22:02:58 +0800 Subject: [PATCH 42/53] Improve and optimize the codes of active content for floating windows * Remove the codes of active content in the floating windows * Add a common class LayoutFloatingWindowControlHelper for active/inactive the content when floating window is active or inactive --- .../LayoutAnchorableFloatingWindowControl.cs | 53 +----- .../LayoutDocumentFloatingWindowControl.cs | 53 +----- .../Controls/LayoutFloatingWindowControl.cs | 69 ------- .../LayoutFloatingWindowControlHelper.cs | 172 ++++++++++++++++++ 4 files changed, 176 insertions(+), 171 deletions(-) create mode 100644 source/Components/AvalonDock/Controls/LayoutFloatingWindowControlHelper.cs diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 29ec53a9..16551443 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -249,11 +249,11 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int var isInactive = ((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE; if (_model.IsSinglePane) { - ActiveItemOfSinglePane(!isInactive); + LayoutFloatingWindowControlHelper.ActiveTheContentOfSinglePane(this, !isInactive); } else { - ActiveItemOfMultiPane(!isInactive); + LayoutFloatingWindowControlHelper.ActiveTheContentOfMultiPane(this, !isInactive); } handled = true; @@ -433,55 +433,6 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion CloseWindowCommand - #region ActiveItem - - internal void ActiveItemOfSinglePane(bool isActive) - { - var pane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - - if (pane != null) - { - pane.SelectedContent.IsActive = isActive; - } - // When the floating tool window is mixed with the floating document window - // and the document pane in the floating document window is dragged out. - - // Only the Tool panes is left in the floating document window. - // The Children Count is greater than 0 and the Selected Content is null. - - // Then we only need to activate the last active content. - else - { - ActiveTheLastActivedItemOfItems(isActive); - } - } - - internal void ActiveItemOfMultiPane(bool isActive) - { - if (isActive) - { - var paneControl = FindPaneControlByMousePoint(); - if (paneControl != null) - { - var model = (LayoutAnchorablePane)paneControl.Model; - if (model.SelectedContent != null) - { - model.SelectedContent.IsActive = true; - return; - } - else - { - ActiveTheLastActivedItemOfPane(model); - return; - } - } - } - ActiveTheLastActivedItemOfItems(isActive); - } - - #endregion ActiveItem - #endregion Private Methods } } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 18fada33..44bb0a79 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -124,11 +124,11 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int var isInactive = ((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE; if (_model.IsSinglePane) { - ActiveItemOfSinglePane(!isInactive); + LayoutFloatingWindowControlHelper.ActiveTheContentOfSinglePane(this, !isInactive); } else { - ActiveItemOfMultiPane(!isInactive); + LayoutFloatingWindowControlHelper.ActiveTheContentOfMultiPane(this, !isInactive); } handled = true; @@ -432,55 +432,6 @@ private void OnExecuteCloseWindowCommand(object parameter) #endregion CloseWindowCommand - #region ActiveItem - - internal void ActiveItemOfSinglePane(bool isActive) - { - var pane = _model.Descendents().OfType() - .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); - - if (pane != null) - { - pane.SelectedContent.IsActive = isActive; - } - // When the floating tool window is mixed with the floating document window - // and the document pane in the floating document window is dragged out. - - // Only the Tool panes is left in the floating document window. - // The Children Count is greater than 0 and the Selected Content is null. - - // Then we only need to activate the last active content. - else - { - ActiveTheLastActivedItemOfItems(isActive); - } - } - - internal void ActiveItemOfMultiPane(bool isActive) - { - if (isActive) - { - var paneControl = FindPaneControlByMousePoint(); - if (paneControl != null) - { - var model = (LayoutDocumentPane)paneControl.Model; - if (model.SelectedContent != null) - { - model.SelectedContent.IsActive = true; - return; - } - else - { - ActiveTheLastActivedItemOfPane(model); - return; - } - } - } - ActiveTheLastActivedItemOfItems(isActive); - } - - #endregion ActiveItem - #endregion Private Methods } } diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index fdc308d8..c7af3acf 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -492,75 +492,6 @@ internal void InternalClose(bool closeInitiatedByUser = false) Close(); } - internal T FindPaneControlByMousePoint() where T : FrameworkElement, ILayoutControl - { - var mousePosition = this.PointToScreenDPI(Mouse.GetPosition(this)); - var rootVisual = ((FloatingWindowContentHost)Content).RootVisual; - var areaHosts = rootVisual.FindVisualChildren(); - - foreach (var areaHost in areaHosts) - { - var rect = areaHost.GetScreenArea(); - var b = rect.Contains(mousePosition); - - if (b) - { - return areaHost; - } - } - - return null; - } - - internal void ActiveTheLastActivedItemOfItems(bool isActive) - { - var items = _model.Descendents().OfType().ToList(); - if (items.Count > 0) - { - var index = 0; - if (items.Count > 1) - { - var tmpTimeStamp2 = items[0].LastActivationTimeStamp; - for (var i = 1; i < items.Count; i++) - { - var item = items[i]; - if (item.LastActivationTimeStamp > tmpTimeStamp2) - { - tmpTimeStamp2 = item.LastActivationTimeStamp; - index = i; - } - } - } - - items[index].IsActive = isActive; - } - } - - internal static void ActiveTheLastActivedItemOfPane(TPane pane) - where TPane : LayoutPositionableGroup, ILayoutContentSelector - where TChild : LayoutContent - { - if (pane.Children.Count > 0) - { - var index = 0; - if (pane.Children.Count > 1) - { - var tmTimeStamp = pane.Children[0].LastActivationTimeStamp; - for (var i = 1; i < pane.Children.Count; i++) - { - var item = pane.Children[i]; - if (item.LastActivationTimeStamp > tmTimeStamp) - { - tmTimeStamp = item.LastActivationTimeStamp; - index = i; - } - } - } - - pane.SelectedContentIndex = index; - } - } - #endregion Internal Methods #region Overrides diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControlHelper.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControlHelper.cs new file mode 100644 index 00000000..ca2a0c0c --- /dev/null +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControlHelper.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +using AvalonDock.Layout; + + +namespace AvalonDock.Controls +{ + internal static class LayoutFloatingWindowControlHelper + { + private const string Excp_NotSupportedFloatingWindowType = "Not Supported Floating Window Type: {0}"; + + public static void ActiveTheContentOfSinglePane(T fwc, bool isActive) where T : LayoutFloatingWindowControl + { + ILayoutContentSelector selector = null; + if (fwc is LayoutAnchorableFloatingWindowControl) + { + selector = fwc.Model + .Descendents() + .OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + } + else if (fwc is LayoutDocumentFloatingWindowControl) + { + selector = fwc.Model + .Descendents() + .OfType() + .FirstOrDefault(p => p.ChildrenCount > 0 && p.SelectedContent != null); + } + else + { + throw new NotSupportedException(string.Format(Excp_NotSupportedFloatingWindowType, fwc.GetType())); + } + + if (selector != null) + { + selector.SelectedContent.IsActive = isActive; + } + else + { + // When the floating tool window is mixed with the floating document window + // and the document pane in the floating document window is dragged out. + + // Only the Tool panes is left in the floating document window. + // The Children Count is greater than 0 and the Selected Content is null. + + // Then we only need to activate the last active content. + ActiveTheLastActivedContent(fwc, isActive); + } + } + + public static void ActiveTheContentOfMultiPane(T fwc, bool isActive) where T : LayoutFloatingWindowControl + { + if (isActive) + { + if (fwc is LayoutAnchorableFloatingWindowControl) + { + var paneControl = GetLayoutControlByMousePosition(fwc); + if (paneControl != null && paneControl.Model is LayoutAnchorablePane pane) + { + if (pane.SelectedContent != null) + pane.SelectedContent.IsActive = true; + else + ActiveTheLastActivedContentOfPane(pane); + + return; + } + } + else if (fwc is LayoutDocumentFloatingWindowControl) + { + var paneControl = GetLayoutControlByMousePosition(fwc); + if (paneControl != null && paneControl.Model is LayoutDocumentPane pane) + { + if (pane.SelectedContent != null) + pane.SelectedContent.IsActive = true; + else + ActiveTheLastActivedContentOfPane(pane); + + return; + } + } + else + { + throw new NotSupportedException(string.Format(Excp_NotSupportedFloatingWindowType, fwc.GetType())); + } + } + + ActiveTheLastActivedContent(fwc, isActive); + } + + public static void ActiveTheLastActivedContent(LayoutFloatingWindowControl fwc, bool isActive) + { + var items = fwc.Model.Descendents().OfType().ToList(); + var index = IndexOfLastActivedContent(items); + if (index != -1) + { + items[index].IsActive = isActive; + } + } + + public static void ActiveTheLastActivedContentOfPane(LayoutAnchorablePane anchorablePane) + { + var index = IndexOfLastActivedContent(anchorablePane.Children); + if (index != -1) + { + anchorablePane.SelectedContentIndex = index; + if (!anchorablePane.SelectedContent.IsActive) + { + anchorablePane.SelectedContent.IsActive = true; + } + } + } + + public static void ActiveTheLastActivedContentOfPane(LayoutDocumentPane documentPane) + { + var index = IndexOfLastActivedContent(documentPane.Children); + if (index != -1) + { + documentPane.SelectedContentIndex = index; + if (!documentPane.SelectedContent.IsActive) + { + documentPane.SelectedContent.IsActive = true; + } + } + } + + private static T GetLayoutControlByMousePosition(LayoutFloatingWindowControl fwc) where T : FrameworkElement, ILayoutControl + { + var mousePosition = fwc.PointToScreenDPI(Mouse.GetPosition(fwc)); + var rootVisual = ((LayoutFloatingWindowControl.FloatingWindowContentHost)fwc.Content).RootVisual; + + foreach (var areaHost in rootVisual.FindVisualChildren()) + { + var rect = areaHost.GetScreenArea(); + if (rect.Contains(mousePosition)) + { + return areaHost; + } + } + + return null; + } + + private static int IndexOfLastActivedContent(IList list) where T : LayoutContent + { + if (list.Count > 0) + { + var index = 0; + if (list.Count > 1) + { + var tmpTimeStamp = list[0].LastActivationTimeStamp; + for (var i = 1; i < list.Count; i++) + { + var item = list[i]; + if (item.LastActivationTimeStamp > tmpTimeStamp) + { + tmpTimeStamp = item.LastActivationTimeStamp; + index = i; + } + } + } + + return index; + } + + return -1; + } + } +} From 58ec1364f24e3d8b1e52466e25335c612f6e26cd Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sun, 29 Jan 2023 18:04:17 +0100 Subject: [PATCH 43/53] AvalonDock Version 4.71.2 --- README.md | 8 ++++++++ .../AvalonDock.Themes.Aero.csproj | 10 +++++----- .../AvalonDock.Themes.Expression.csproj | 10 +++++----- .../AvalonDock.Themes.Metro.csproj | 12 ++++++------ .../AvalonDock.Themes.VS2010.csproj | 10 +++++----- .../AvalonDock.Themes.VS2013.csproj | 12 ++++++------ source/Components/AvalonDock/AvalonDock.csproj | 10 +++++----- 7 files changed, 40 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 64e7077a..3475f69b 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,14 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History +## Fixes Added in Version 4.71.2 + +- [#416 Fix Issue #226: Restore floating windows to maximized state](https://github.com/Dirkster99/AvalonDock/pull/416) (thanx to [Michael Möller](https://github.com/moellerm)) + +- [#417 Close and active selected item when NavigatorWindow is inactive](https://github.com/Dirkster99/AvalonDock/pull/417) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + +- [#418 Add active content handler to LayoutAnchorableFloatingWindow and improve the active content handlers](https://github.com/Dirkster99/AvalonDock/pull/418) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) + ## Fixes Added in Version 4.71.1 - [#413 Fix the binding error in AnchorGroupTemplate](https://github.com/Dirkster99/AvalonDock/pull/413) (thanx to [EQOH Noisrev](https://github.com/Noisrev)) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 8d4440b2..9ecdd743 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -6,13 +6,13 @@ sn.snk true true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index 5246ad64..ecbb0b0c 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -6,13 +6,13 @@ sn.snk true true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index 2283819a..9db59365 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,13 +6,13 @@ sn.snk true true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index c16829a1..9251dd1d 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -6,13 +6,13 @@ sn.snk true true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index e62f5075..98ae0c52 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,13 +6,13 @@ sn.snk true true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index f664aef8..72fdf0bd 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,13 +6,13 @@ true sn.snk true - 4.71.1 - 4.71.1 - 4.71.1 - 4.71.1 + 4.71.2 + 4.71.2 + 4.71.2 + 4.71.2 Open Source Dirkster.AvalonDock - 2017-2022 + 2017-2023 This assembly implements a docking layout system for WPF. https://github.com/Dirkster99/AvalonDock https://github.com/Dirkster99/AvalonDock From 3ccbfe8f3bb2f1350e4fe5c3479d19562c3bca60 Mon Sep 17 00:00:00 2001 From: Mona04 Date: Sat, 4 Feb 2023 02:39:03 +0900 Subject: [PATCH 44/53] issue #422 --- source/Components/AvalonDock/Themes/generic.xaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/Components/AvalonDock/Themes/generic.xaml b/source/Components/AvalonDock/Themes/generic.xaml index 68df206a..b3588682 100644 --- a/source/Components/AvalonDock/Themes/generic.xaml +++ b/source/Components/AvalonDock/Themes/generic.xaml @@ -758,9 +758,7 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> + Content="{Binding LayoutItem.View, RelativeSource={RelativeSource TemplatedParent}}" /> From d43c7c6996af1304079352e85edc3f0d25cfd981 Mon Sep 17 00:00:00 2001 From: Khaos Date: Thu, 9 Feb 2023 10:46:54 +0100 Subject: [PATCH 45/53] Fix: potential NRE on close --- source/Components/AvalonDock/DockingManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index a6a396d5..232dca16 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1138,7 +1138,7 @@ public double GridSplitterHeight new FrameworkPropertyMetadata((Style)null)); /// - /// Gets or sets the GridSplitterVerticalStyle property. This dependency property + /// Gets or sets the GridSplitterVerticalStyle property. This dependency property /// indicates the style to apply to the LayoutGridResizerControl when displayed vertically. /// public Style GridSplitterVerticalStyle @@ -1164,7 +1164,7 @@ public Style GridSplitterVerticalStyle new FrameworkPropertyMetadata((Style)null)); /// - /// Gets or sets the GridSplitterHorizontalStyle property. This dependency property + /// Gets or sets the GridSplitterHorizontalStyle property. This dependency property /// indicates the style to apply to the LayoutGridResizerControl when displayed horizontally. /// public Style GridSplitterHorizontalStyle @@ -2292,7 +2292,7 @@ private void DocumentsSourceElementsChanged(object sender, NotifyCollectionChang foreach (var documentToRemove in documentsToRemove) { documentToRemove.Content = null; - documentToRemove.Parent.RemoveChild(documentToRemove); + documentToRemove.Parent?.RemoveChild(documentToRemove); RemoveViewFromLogicalChild(documentToRemove); } } @@ -2354,7 +2354,7 @@ private void DocumentsSourceElementsChanged(object sender, NotifyCollectionChang var documentsToRemove = GetItemsToRemoveAfterReset(DocumentsSource); foreach (var documentToRemove in documentsToRemove) { - (documentToRemove.Parent as ILayoutContainer).RemoveChild( + (documentToRemove.Parent as ILayoutContainer)?.RemoveChild( documentToRemove); RemoveViewFromLogicalChild(documentToRemove); } From e30352f789de9861a53962f1031b5f99df4d7973 Mon Sep 17 00:00:00 2001 From: Khaos Date: Thu, 9 Feb 2023 15:33:53 +0100 Subject: [PATCH 46/53] Fix floating windows still created twice --- source/Components/AvalonDock/DockingManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index a6a396d5..be5a9df1 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1138,7 +1138,7 @@ public double GridSplitterHeight new FrameworkPropertyMetadata((Style)null)); /// - /// Gets or sets the GridSplitterVerticalStyle property. This dependency property + /// Gets or sets the GridSplitterVerticalStyle property. This dependency property /// indicates the style to apply to the LayoutGridResizerControl when displayed vertically. /// public Style GridSplitterVerticalStyle @@ -1164,7 +1164,7 @@ public Style GridSplitterVerticalStyle new FrameworkPropertyMetadata((Style)null)); /// - /// Gets or sets the GridSplitterHorizontalStyle property. This dependency property + /// Gets or sets the GridSplitterHorizontalStyle property. This dependency property /// indicates the style to apply to the LayoutGridResizerControl when displayed horizontally. /// public Style GridSplitterHorizontalStyle @@ -2125,9 +2125,8 @@ private void DockingManager_Loaded(object sender, RoutedEventArgs e) } _fwHiddenList.Clear(); - // load floating windows not already loaded! (issue #59 & #254) - var items = new List(Layout.FloatingWindows.Where(fw => !_fwList.Any(fwc => fwc.Model == fw))); - foreach (var fw in items) + // load floating windows not already loaded! (issue #59 & #254 & #426) + foreach (var fw in Layout.FloatingWindows.Where(fw => !_fwList.Any(fwc => fwc.Model == fw))) CreateUIElementForModel(fw); //create the overlaywindow if it's possible From 02fa07044dfa5f474351a29930eb64ca87b53467 Mon Sep 17 00:00:00 2001 From: Calum Robinson Date: Thu, 16 Feb 2023 08:06:11 +0000 Subject: [PATCH 47/53] Add DockingManager.ShowNavigator --- source/Components/AvalonDock/DockingManager.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index a6a396d5..6d2bb7d0 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1383,6 +1383,22 @@ public bool AutoWindowSizeWhenOpened #endregion AutoWindowSizeWhenOpened + #region ShowNavigator + + /// dependency property. + public static readonly DependencyProperty ShowNavigatorProperty = DependencyProperty.Register(nameof(ShowNavigator), typeof(bool), typeof(DockingManager), + new FrameworkPropertyMetadata(true)); + + /// Gets/sets whether the navigator window should be shown when the user presses Control + Tab. + [Bindable(true), Description("Gets/sets whether floating windows should show the system menu when a custom context menu is not defined."), Category("FloatingWindow")] + public bool ShowNavigator + { + get => (bool)GetValue(ShowNavigatorProperty); + set => SetValue(ShowNavigatorProperty, value); + } + + #endregion ShowNavigator + #endregion Public Properties #region LogicalChildren @@ -1428,7 +1444,7 @@ private void ClearLogicalChildrenList() private bool IsNavigatorWindowActive => _navigatorWindow != null; - private bool CanShowNavigatorWindow => _layoutItems.Any(); + private bool CanShowNavigatorWindow => ShowNavigator && _layoutItems.Any(); #endregion Private Properties From a48e6897b4ad12ed61eeec84b8a31563806db7cc Mon Sep 17 00:00:00 2001 From: Roy Zwart Date: Fri, 17 Mar 2023 16:05:37 +0100 Subject: [PATCH 48/53] Fix unwanted group orientation change when using mixed orientation --- .../Controls/DocumentPaneDropTarget.cs | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/source/Components/AvalonDock/Controls/DocumentPaneDropTarget.cs b/source/Components/AvalonDock/Controls/DocumentPaneDropTarget.cs index 49613e77..a404196e 100644 --- a/source/Components/AvalonDock/Controls/DocumentPaneDropTarget.cs +++ b/source/Components/AvalonDock/Controls/DocumentPaneDropTarget.cs @@ -74,18 +74,21 @@ internal DocumentPaneDropTarget(LayoutDocumentPaneControl paneControl, /// protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) { - ILayoutDocumentPane targetModel = _targetPane.Model as ILayoutDocumentPane; - LayoutDocument documentActive = floatingWindow.Descendents().OfType().FirstOrDefault(); + var targetModel = (ILayoutDocumentPane)_targetPane.Model; + var documentActive = floatingWindow.Descendents().OfType().FirstOrDefault(); - // ensure paneGroup var paneGroup = targetModel.Parent as LayoutDocumentPaneGroup; + var requiredOrientation = Type == DropTargetType.DocumentPaneDockBottom || Type == DropTargetType.DocumentPaneDockTop ? + System.Windows.Controls.Orientation.Vertical : System.Windows.Controls.Orientation.Horizontal; + var allowMixedOrientation = targetModel.Root.Manager.AllowMixedOrientation; + if(paneGroup == null) { - var targetModelAsPositionableElement = targetModel as ILayoutPositionableElement; - var layoutGroup = targetModel.Parent as ILayoutGroup; - paneGroup = new LayoutDocumentPaneGroup() + var targetModelAsPositionableElement = (ILayoutPositionableElement)targetModel; + var layoutGroup = (ILayoutGroup)targetModel.Parent; + paneGroup = new LayoutDocumentPaneGroup { - Orientation = System.Windows.Controls.Orientation.Vertical, + Orientation = requiredOrientation, DockWidth = targetModelAsPositionableElement.DockWidth, DockHeight = targetModelAsPositionableElement.DockHeight, }; @@ -93,8 +96,20 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) paneGroup.Children.Add(targetModel); layoutGroup.InsertChildAt(0, paneGroup); } - var paneGroupOrientaion = paneGroup as ILayoutOrientableGroup; - + else if (allowMixedOrientation && paneGroup.Orientation != requiredOrientation && Type != DropTargetType.DocumentPaneDockInside) + { + var targetModelAsPositionableElement = (ILayoutPositionableElement)targetModel; + var newGroup = new LayoutDocumentPaneGroup + { + Orientation = requiredOrientation, + DockWidth = targetModelAsPositionableElement.DockWidth, + DockHeight = targetModelAsPositionableElement.DockHeight, + }; + + paneGroup.ReplaceChild(targetModel, newGroup); + newGroup.Children.Add(targetModel); + paneGroup = newGroup; + } switch (Type) { @@ -104,7 +119,7 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) { - if (paneGroupOrientaion.Orientation != System.Windows.Controls.Orientation.Vertical) + if (!allowMixedOrientation && paneGroup.Orientation != System.Windows.Controls.Orientation.Vertical) { paneGroup.Orientation = System.Windows.Controls.Orientation.Vertical; } @@ -132,7 +147,7 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) { - if(paneGroupOrientaion.Orientation != System.Windows.Controls.Orientation.Vertical) + if(!allowMixedOrientation && paneGroup.Orientation != System.Windows.Controls.Orientation.Vertical) { paneGroup.Orientation = System.Windows.Controls.Orientation.Vertical; } @@ -159,7 +174,7 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) #region DropTargetType.DocumentPaneDockLeft { - if (paneGroupOrientaion.Orientation != System.Windows.Controls.Orientation.Horizontal) + if (!allowMixedOrientation && paneGroup.Orientation != System.Windows.Controls.Orientation.Horizontal) { paneGroup.Orientation = System.Windows.Controls.Orientation.Horizontal; } @@ -186,7 +201,7 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) #region DropTargetType.DocumentPaneDockRight { - if (paneGroupOrientaion.Orientation != System.Windows.Controls.Orientation.Horizontal) + if (!allowMixedOrientation && paneGroup.Orientation != System.Windows.Controls.Orientation.Horizontal) { paneGroup.Orientation = System.Windows.Controls.Orientation.Horizontal; } From 982004bbc8f0be0e866d807f88f53accacc3e2ed Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Fri, 24 Mar 2023 14:29:55 +0100 Subject: [PATCH 49/53] AvalonDock Version 4.72.0 --- README.md | 12 ++++++++++++ .../AvalonDock.Themes.Aero.csproj | 10 +++++----- .../AvalonDock.Themes.Expression.csproj | 10 +++++----- .../AvalonDock.Themes.Metro.csproj | 10 +++++----- .../AvalonDock.Themes.VS2010.csproj | 10 +++++----- .../AvalonDock.Themes.VS2013.csproj | 8 ++++---- source/Components/AvalonDock/AvalonDock.csproj | 8 ++++---- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 3475f69b..171ad87f 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,18 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History +## Fixes and Features Added in Version 4.72.0 + +- [#423 issue #422 DockingManager.LayoutItemTemplateSelector is applied twice because...](https://github.com/Dirkster99/AvalonDock/pull/423) (thanx to [Mona04](https://github.com/Mona04)) + +- [#425 Fix: Potential NRE on app close](https://github.com/Dirkster99/AvalonDock/pull/425) (thanx to [Khaos66](https://github.com/Khaos66)) + +- [#427 Fix floating windows still created twice](https://github.com/Dirkster99/AvalonDock/pull/427) (thanx to [Khaos66](https://github.com/Khaos66)) + +- [Add DockingManager.ShowNavigator](https://github.com/Dirkster99/AvalonDock/pull/428) (thanx to [Calum Robinson](https://github.com/calumr)) + +- [#431 Fix unwanted group orientation change when using mixed orientation](https://github.com/Dirkster99/AvalonDock/pull/431) (thanx to [KuroiRoy](https://github.com/KuroiRoy)) + ## Fixes Added in Version 4.71.2 - [#416 Fix Issue #226: Restore floating windows to maximized state](https://github.com/Dirkster99/AvalonDock/pull/416) (thanx to [Michael Möller](https://github.com/moellerm)) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 9ecdd743..125f3ef7 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index ecbb0b0c..d6995897 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index 9db59365..70e216a4 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index 9251dd1d..3b8692e2 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -1,4 +1,4 @@ - + AvalonDock.Themes netcoreapp3.0;net5.0-windows;net40 @@ -6,10 +6,10 @@ sn.snk true true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index 98ae0c52..454ce40f 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index 72fdf0bd..362bf56e 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,10 +6,10 @@ true sn.snk true - 4.71.2 - 4.71.2 - 4.71.2 - 4.71.2 + 4.72.0 + 4.72.0 + 4.72.0 + 4.72.0 Open Source Dirkster.AvalonDock 2017-2023 From 0cc7cb5fa04d2ca0d28929f8c68a5bb1d3fbc2e6 Mon Sep 17 00:00:00 2001 From: frsa Date: Fri, 31 Mar 2023 15:48:30 +0200 Subject: [PATCH 50/53] Changed how the next active document is picked on document close. --- .../Components/AvalonDock/DockingManager.cs | 122 +++++++----------- 1 file changed, 45 insertions(+), 77 deletions(-) diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index aaeffa0e..8c7ca9d0 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -1870,6 +1870,36 @@ internal void RemoveFloatingWindow(LayoutFloatingWindowControl floatingWindow) LayoutFloatingWindowControlClosed?.Invoke(this, new LayoutFloatingWindowControlClosedEventArgs(floatingWindow)); } + + + internal void ExecuteCloseAllButThisCommand(LayoutContent contentSelected) + { + foreach (var contentToClose in Layout.Descendents().OfType().Where(d => d != contentSelected && (d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow)).ToArray()) + Close(contentToClose); + } + + internal void ExecuteCloseAllCommand(LayoutContent contentSelected) + { + foreach (var contentToClose in Layout.Descendents().OfType().Where(d => (d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow)).ToArray()) + Close(contentToClose); + } + + internal void ExecuteCloseCommand(LayoutAnchorable anchorable) + { + if (!(anchorable is LayoutAnchorable model)) return; + + AnchorableClosingEventArgs closingArgs = null; + AnchorableClosing?.Invoke(this, closingArgs = new AnchorableClosingEventArgs(model)); + if (closingArgs?.Cancel == true) + return; + + if (model.CloseAnchorable()) + { + RemoveViewFromLogicalChild(model); + AnchorableClosed?.Invoke(this, new AnchorableClosedEventArgs(model)); + } + } + internal void ExecuteCloseCommand(LayoutDocument document) { if (DocumentClosing != null) @@ -1879,10 +1909,8 @@ internal void ExecuteCloseCommand(LayoutDocument document) if (argsClosing.Cancel) return; } - // - // Determine the index of the document that will be removed. - // - int indexOfDocumentToRemove = GetIndexOfDocument(document); + // Get the document to activate after the close. + LayoutDocument documentToActivate = GetDocumentToActivate(document); if (!document.CloseDocument()) return; @@ -1894,98 +1922,38 @@ internal void ExecuteCloseCommand(LayoutDocument document) //get rid of the closed document content document.Content = null; - int indexOfDocumentToSelect = indexOfDocumentToRemove - 1; - - if (indexOfDocumentToSelect < 0) - { - indexOfDocumentToSelect = 0; - } - // - // Determine the new active document and activate it. + // Activate the document determined to be the next active document. // This doesn't only update the layout, but also all related (dependency) properties. // - LayoutDocument layoutDocument = GetDocumentOnIndex(indexOfDocumentToSelect); - - if (layoutDocument != null) + if (documentToActivate != null) { - layoutDocument.IsActive = true; + documentToActivate.IsActive = true; } } - private LayoutDocument GetDocumentOnIndex(int indexToFind) + private LayoutDocument GetDocumentToActivate(LayoutDocument previousDocument) { - if (indexToFind < 0) - { - throw new ArgumentOutOfRangeException(nameof(indexToFind)); - } + ILayoutContainer parentContainer = previousDocument.Parent; + IEnumerable siblingDocuments = parentContainer?.Children.OfType() ?? Enumerable.Empty(); - int index = 0; - - foreach (LayoutDocument layoutDocument in this.Layout.Descendents().OfType()) + foreach (var childPair in siblingDocuments.Zip(siblingDocuments.Skip(1), Tuple.Create)) { - if (index == indexToFind) + if (childPair.Item2 == previousDocument) { - return layoutDocument; + return childPair.Item1; } - - index++; } - return null; - } - - private int GetIndexOfDocument(LayoutDocument documentToFind) - { - if (documentToFind == null) - { - throw new ArgumentNullException(nameof(documentToFind)); - } - - int index = 0; - - foreach (LayoutDocument layoutDocument in this.Layout.Descendents().OfType()) + foreach (LayoutDocument document in this.Layout.Descendents().OfType()) { - if (layoutDocument == documentToFind) + if (document.IsSelected) { - return index; + return document; } - - index++; } - // - // Not found. - // - return -1; - } - - internal void ExecuteCloseAllButThisCommand(LayoutContent contentSelected) - { - foreach (var contentToClose in Layout.Descendents().OfType().Where(d => d != contentSelected && (d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow)).ToArray()) - Close(contentToClose); - } - - internal void ExecuteCloseAllCommand(LayoutContent contentSelected) - { - foreach (var contentToClose in Layout.Descendents().OfType().Where(d => (d.Parent is LayoutDocumentPane || d.Parent is LayoutDocumentFloatingWindow)).ToArray()) - Close(contentToClose); - } - - internal void ExecuteCloseCommand(LayoutAnchorable anchorable) - { - if (!(anchorable is LayoutAnchorable model)) return; - - AnchorableClosingEventArgs closingArgs = null; - AnchorableClosing?.Invoke(this, closingArgs = new AnchorableClosingEventArgs(model)); - if (closingArgs?.Cancel == true) - return; - - if (model.CloseAnchorable()) - { - RemoveViewFromLogicalChild(model); - AnchorableClosed?.Invoke(this, new AnchorableClosedEventArgs(model)); - } + return null; } internal void ExecuteHideCommand(LayoutAnchorable anchorable) From 31cf051dd808e71ec314885f35c1a88da9bc4327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benny=20B=C3=BCrger?= Date: Fri, 28 Apr 2023 15:07:18 +0200 Subject: [PATCH 51/53] NullCheck for DragPoint --- .../Controls/LayoutAnchorableFloatingWindowControl.cs | 2 ++ .../Controls/LayoutDocumentFloatingWindowControl.cs | 2 ++ .../Components/AvalonDock/Controls/TransformExtentions.cs | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs index 16551443..b9d6e0a4 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableFloatingWindowControl.cs @@ -149,6 +149,8 @@ bool IOverlayWindowHost.HitTestScreen(Point dragPoint) bool HitTest(Point dragPoint) { + if (dragPoint == default(Point)) + return false; var detectionRect = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); return detectionRect.Contains(dragPoint); } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index 44bb0a79..e75d1aa6 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -224,6 +224,8 @@ bool IOverlayWindowHost.HitTestScreen(Point dragPoint) bool HitTest(Point dragPoint) { + if (dragPoint == default(Point)) + return false; var detectionRect = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); return detectionRect.Contains(dragPoint); } diff --git a/source/Components/AvalonDock/Controls/TransformExtentions.cs b/source/Components/AvalonDock/Controls/TransformExtentions.cs index 4b576366..768601ce 100644 --- a/source/Components/AvalonDock/Controls/TransformExtentions.cs +++ b/source/Components/AvalonDock/Controls/TransformExtentions.cs @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. @@ -59,7 +59,10 @@ public static Rect GetScreenArea(this FrameworkElement element) public static Point TransformToDeviceDPI(this Visual visual, Point pt) { - Matrix m = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; + var compositionTarget = PresentationSource.FromVisual(visual).CompositionTarget; + if (compositionTarget == null) + return default; + Matrix m = compositionTarget.TransformToDevice; return new Point(pt.X / m.M11, pt.Y / m.M22); } From f80e1802f81b341bb1e023bee207c0670d4b0350 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Wed, 16 Aug 2023 12:05:22 +0200 Subject: [PATCH 52/53] AvalonDock Version 4.72.1 --- .../AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj | 8 ++++---- .../AvalonDock.Themes.Expression.csproj | 8 ++++---- .../AvalonDock.Themes.Metro.csproj | 8 ++++---- .../AvalonDock.Themes.VS2010.csproj | 8 ++++---- .../AvalonDock.Themes.VS2013.csproj | 8 ++++---- source/Components/AvalonDock/AvalonDock.csproj | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj index 125f3ef7..33b4310a 100644 --- a/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj +++ b/source/Components/AvalonDock.Themes.Aero/AvalonDock.Themes.Aero.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj index d6995897..d7ed522c 100644 --- a/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj +++ b/source/Components/AvalonDock.Themes.Expression/AvalonDock.Themes.Expression.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj index 70e216a4..34203fe8 100644 --- a/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj +++ b/source/Components/AvalonDock.Themes.Metro/AvalonDock.Themes.Metro.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj index 3b8692e2..844d5fde 100644 --- a/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj +++ b/source/Components/AvalonDock.Themes.VS2010/AvalonDock.Themes.VS2010.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj index 454ce40f..cc2bf377 100644 --- a/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj +++ b/source/Components/AvalonDock.Themes.VS2013/AvalonDock.Themes.VS2013.csproj @@ -6,10 +6,10 @@ sn.snk true true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 diff --git a/source/Components/AvalonDock/AvalonDock.csproj b/source/Components/AvalonDock/AvalonDock.csproj index 362bf56e..6986bf34 100644 --- a/source/Components/AvalonDock/AvalonDock.csproj +++ b/source/Components/AvalonDock/AvalonDock.csproj @@ -6,10 +6,10 @@ true sn.snk true - 4.72.0 - 4.72.0 - 4.72.0 - 4.72.0 + 4.72.1 + 4.72.1 + 4.72.1 + 4.72.1 Open Source Dirkster.AvalonDock 2017-2023 From 30599e401419ad238187a3594f5dd85acaa5546b Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Wed, 16 Aug 2023 12:18:05 +0200 Subject: [PATCH 53/53] Updated Readme.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 171ad87f..6f6d2157 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,12 @@ to also theme standard elements, such as, button and textblock etc. # Mile Stone History + +## Fixes and Features Added in Version 4.72.0 + +- [#436 Changed how the next active document is picked on document close.](https://github.com/Dirkster99/AvalonDock/pull/436) (thanx to [FredrikS fredriks123](https://github.com/fredriks123)) +- [#438 NullCheck for DragPoint](https://github.com/Dirkster99/AvalonDock/pull/438) (thanx to [Ben bbuerger](https://github.com/bbuerger)) + ## Fixes and Features Added in Version 4.72.0 - [#423 issue #422 DockingManager.LayoutItemTemplateSelector is applied twice because...](https://github.com/Dirkster99/AvalonDock/pull/423) (thanx to [Mona04](https://github.com/Mona04))