From 83e6518c902fc8f0ea3c1c99d438429b29403d54 Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Wed, 1 Apr 2020 09:43:28 +0200 Subject: [PATCH 1/8] Auto resizing floating window on startup --- .../Controls/LayoutFloatingWindowControl.cs | 55 ++++++++++++++++++- .../AvalonDock/VisualTreeHelperExtensions.cs | 40 ++++++++++++++ source/TestApp/MainWindow.xaml | 3 +- source/TestApp/MainWindow.xaml.cs | 14 ++++- source/TestApp/TestUserControl.xaml | 1 + 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 source/Components/AvalonDock/VisualTreeHelperExtensions.cs diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 7a4af7cd..9f4d4de2 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -233,6 +233,8 @@ internal void AttachDrag(bool onActivated = true) } } + internal Thickness TotalMargin { get; private set; } + private bool _foundMargin = false; protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { handled = false; @@ -248,7 +250,9 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP handled = true; } } - break; + UpdateWindowsSizeBasedOnMinSize(); + + break; case Win32Helper.WM_EXITSIZEMOVE: UpdatePositionAndSizeOfPanes(); @@ -283,7 +287,54 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP return IntPtr.Zero; } - internal void InternalClose(bool closeInitiatedByUser = false) + private void UpdateMargins() + { + var grid = this.GetChildrenRecursive() + .OfType() + .FirstOrDefault(g => g.RowDefinitions.Count > 0); + if (grid != null) + { + _foundMargin = true; + var margin = grid.Margin; + margin.Top = grid.RowDefinitions[0].MinHeight + grid.Margin.Top; + TotalMargin = margin; + } + } + + private void UpdateWindowsSizeBasedOnMinSize() + { + if (!_foundMargin) + { + UpdateMargins(); + if (_foundMargin) + { + ContentPresenter contentControl = this.GetChildrenRecursive() + .OfType() + .FirstOrDefault(c => c.Content is LayoutContent); + if (contentControl == null) + return; + var layoutContent = (LayoutContent)contentControl.Content; + if (layoutContent.Content is FrameworkElement content) + { + var parent = VisualTreeHelper.GetParent(content) as FrameworkElement; + // StackPanels among others have an ActualHeight larger than visible, hence we check the parent control as well + if (content.ActualHeight < content.MinHeight || + parent != null && parent.ActualHeight < content.MinHeight) + { + Height = content.MinHeight + TotalMargin.Top + TotalMargin.Bottom; + } + + if (content.ActualWidth < content.MinWidth || + parent != null && parent.ActualWidth < content.MinWidth) + { + Width = content.MinWidth + TotalMargin.Left + TotalMargin.Right; + } + } + } + } + } + + internal void InternalClose(bool closeInitiatedByUser = false) { _internalCloseFlag = !closeInitiatedByUser; if (_isClosing) return; diff --git a/source/Components/AvalonDock/VisualTreeHelperExtensions.cs b/source/Components/AvalonDock/VisualTreeHelperExtensions.cs new file mode 100644 index 00000000..1a98f44c --- /dev/null +++ b/source/Components/AvalonDock/VisualTreeHelperExtensions.cs @@ -0,0 +1,40 @@ +/************************************************************************ + AvalonDock + + Copyright (C) 2020 ???? + + 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 System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace AvalonDock +{ + internal static class VisualTreeHelperExtensions + { + public static IEnumerable GetChildrenRecursive(this DependencyObject dependencyObject) + { + var children = dependencyObject.GetChildren(); + foreach (var child in children) + { + yield return child; + foreach (var c in GetChildrenRecursive(child)) + { + yield return c; + } + } + } + + public static IEnumerable GetChildren(this DependencyObject dependencyObject) + { + int n = VisualTreeHelper.GetChildrenCount(dependencyObject); + for (int i = 0; i < n; i++) + { + yield return VisualTreeHelper.GetChild(dependencyObject, i); + } + } + } +} diff --git a/source/TestApp/MainWindow.xaml b/source/TestApp/MainWindow.xaml index ac4c440c..1df19a59 100644 --- a/source/TestApp/MainWindow.xaml +++ b/source/TestApp/MainWindow.xaml @@ -54,6 +54,7 @@ + @@ -108,7 +109,7 @@ Title="Tool Window 1" ContentId="toolWindow1" Hiding="OnToolWindow1Hiding"> - + diff --git a/source/TestApp/MainWindow.xaml.cs b/source/TestApp/MainWindow.xaml.cs index a8c559cf..dc8e1b21 100644 --- a/source/TestApp/MainWindow.xaml.cs +++ b/source/TestApp/MainWindow.xaml.cs @@ -236,5 +236,17 @@ private void OnShowHeader(object sender, RoutedEventArgs e) { //// LayoutDocumentPane.ShowHeader = !LayoutDocumentPane.ShowHeader; } - } + + private void OnNewFloatingWindow(object sender, RoutedEventArgs e) + { + var view = new TestUserControl(); + var anchorable = new LayoutAnchorable() + { + Title = "New floating window", + Content = view + }; + anchorable.AddToLayout(dockManager,AnchorableShowStrategy.Most); + anchorable.Float(); + } + } } diff --git a/source/TestApp/TestUserControl.xaml b/source/TestApp/TestUserControl.xaml index b164e968..ab950fe6 100644 --- a/source/TestApp/TestUserControl.xaml +++ b/source/TestApp/TestUserControl.xaml @@ -13,6 +13,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" + MinHeight="300" MinWidth="300" Height="300" Width="300"> From 062391213eb5e39c30c726a62148ff367689582e Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Wed, 1 Apr 2020 15:00:47 +0200 Subject: [PATCH 2/8] Updated user control with some more content --- source/TestApp/TestUserControl.xaml | 8 +++++--- source/TestApp/TestUserControl.xaml.cs | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/TestApp/TestUserControl.xaml b/source/TestApp/TestUserControl.xaml index ab950fe6..cd1fe972 100644 --- a/source/TestApp/TestUserControl.xaml +++ b/source/TestApp/TestUserControl.xaml @@ -13,9 +13,11 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" - MinHeight="300" MinWidth="300" - Height="300" Width="300"> + MinHeight="300" MinWidth="300"> - + + A label: + Some input + diff --git a/source/TestApp/TestUserControl.xaml.cs b/source/TestApp/TestUserControl.xaml.cs index 6a330fac..f2122679 100644 --- a/source/TestApp/TestUserControl.xaml.cs +++ b/source/TestApp/TestUserControl.xaml.cs @@ -23,6 +23,12 @@ public TestUserControl() this.Loaded += new RoutedEventHandler(TestUserControl_Loaded); this.Unloaded += new RoutedEventHandler(TestUserControl_Unloaded); + this.SizeChanged += TestUserControl_SizeChanged; + } + + private void TestUserControl_SizeChanged(object sender, SizeChangedEventArgs e) + { + } void TestUserControl_Unloaded(object sender, RoutedEventArgs e) From 9a794e4dc6bd05b9d71d2362b2e5a13185b0f71d Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Wed, 1 Apr 2020 16:39:24 +0200 Subject: [PATCH 3/8] Founds the last 4 pixels --- .../Controls/LayoutFloatingWindowControl.cs | 52 ++++++++++++---- source/Components/AvalonDock/Extensions.cs | 60 ++++++++++++++++++- .../AvalonDock/VisualTreeHelperExtensions.cs | 40 ------------- 3 files changed, 100 insertions(+), 52 deletions(-) delete mode 100644 source/Components/AvalonDock/VisualTreeHelperExtensions.cs diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 9f4d4de2..bed2e58f 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -1,7 +1,7 @@ /************************************************************************ AvalonDock - Copyright (C) 2007-2013 Xceed Software Inc. + Copyright (C) 2007-2020 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 @@ -289,16 +289,46 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP private void UpdateMargins() { + // The grid with window bar and content var grid = this.GetChildrenRecursive() .OfType() .FirstOrDefault(g => g.RowDefinitions.Count > 0); - if (grid != null) - { - _foundMargin = true; - var margin = grid.Margin; - margin.Top = grid.RowDefinitions[0].MinHeight + grid.Margin.Top; - TotalMargin = margin; - } + ContentPresenter contentControl = this.GetChildrenRecursive() + .OfType() + .FirstOrDefault(c => c.Content is LayoutContent); + if (contentControl == null) + return; + // The content control in the grid, this has a different visual tree to walk up + var layoutContent = (LayoutContent)contentControl.Content; + if (grid != null && layoutContent.Content is FrameworkElement content) + { + var parents = content.GetParents().ToArray(); + var children = this.GetChildrenRecursive() + .TakeWhile(c => c != grid) + .ToArray(); + var borders = children + .OfType() + .Concat(parents + .OfType()) + .ToArray(); + var controls = children + .OfType() + .Concat(parents + .OfType()) + .ToArray(); + var frameworkElements = children + .OfType() + .Concat(parents + .OfType()) + .ToArray(); + var padding = controls.Sum(b => b.Padding); + var border = borders.Sum(b => b.BorderThickness); + var margin = frameworkElements.Sum(f => f.Margin); + margin = margin.Add(padding).Add(border).Add(grid.Margin); + margin.Top = grid.RowDefinitions[0].MinHeight; + TotalMargin = margin; + _foundMargin = true; + } } private void UpdateWindowsSizeBasedOnMinSize() @@ -315,8 +345,10 @@ private void UpdateWindowsSizeBasedOnMinSize() return; var layoutContent = (LayoutContent)contentControl.Content; if (layoutContent.Content is FrameworkElement content) - { - var parent = VisualTreeHelper.GetParent(content) as FrameworkElement; + { + var parent = content.GetParents() + .OfType() + .FirstOrDefault(); // StackPanels among others have an ActualHeight larger than visible, hence we check the parent control as well if (content.ActualHeight < content.MinHeight || parent != null && parent.ActualHeight < content.MinHeight) diff --git a/source/Components/AvalonDock/Extensions.cs b/source/Components/AvalonDock/Extensions.cs index a861f6d9..c0229e01 100644 --- a/source/Components/AvalonDock/Extensions.cs +++ b/source/Components/AvalonDock/Extensions.cs @@ -1,7 +1,7 @@ /************************************************************************ AvalonDock - Copyright (C) 2007-2013 Xceed Software Inc. + Copyright (C) 2007-2020 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 @@ -10,6 +10,8 @@ This program is provided to you under the terms of the Microsoft Public using System; using System.Collections.Generic; using System.Collections; +using System.Windows; +using System.Windows.Media; namespace AvalonDock { @@ -40,5 +42,59 @@ public static V GetValueOrDefault(this WeakReference wr) { return wr == null || !wr.IsAlive ? default : (V) wr.Target; } - } + + public static IEnumerable GetChildrenRecursive(this DependencyObject dependencyObject) + { + var children = dependencyObject.GetChildren(); + foreach (var child in children) + { + yield return child; + foreach (var c in GetChildrenRecursive(child)) + { + yield return c; + } + } + } + + public static IEnumerable GetChildren(this DependencyObject dependencyObject) + { + int n = VisualTreeHelper.GetChildrenCount(dependencyObject); + for (int i = 0; i < n; i++) + { + yield return VisualTreeHelper.GetChild(dependencyObject, i); + } + } + + public static IEnumerable GetParents(this DependencyObject dependencyObject) + { + while (dependencyObject != null) + { + dependencyObject = VisualTreeHelper.GetParent(dependencyObject); + if (dependencyObject != null) + yield return dependencyObject; + } + } + + public static Thickness Sum(this IEnumerable enumerable, Func func) + { + double top = 0, bottom = 0, left = 0, right = 0; + foreach (var e in enumerable) + { + var t = func(e); + left = t.Left; + top += t.Top; + right = t.Right; + bottom += t.Bottom; + } + return new Thickness(left, top, right, bottom); + } + + public static Thickness Add(this Thickness thickness, Thickness other) + { + return new Thickness(thickness.Left + other.Left, + thickness.Top + other.Top, + thickness.Right + other.Right, + thickness.Bottom + other.Bottom); + } + } } diff --git a/source/Components/AvalonDock/VisualTreeHelperExtensions.cs b/source/Components/AvalonDock/VisualTreeHelperExtensions.cs deleted file mode 100644 index 1a98f44c..00000000 --- a/source/Components/AvalonDock/VisualTreeHelperExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -/************************************************************************ - AvalonDock - - Copyright (C) 2020 ???? - - 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 System.Collections.Generic; -using System.Windows; -using System.Windows.Media; - -namespace AvalonDock -{ - internal static class VisualTreeHelperExtensions - { - public static IEnumerable GetChildrenRecursive(this DependencyObject dependencyObject) - { - var children = dependencyObject.GetChildren(); - foreach (var child in children) - { - yield return child; - foreach (var c in GetChildrenRecursive(child)) - { - yield return c; - } - } - } - - public static IEnumerable GetChildren(this DependencyObject dependencyObject) - { - int n = VisualTreeHelper.GetChildrenCount(dependencyObject); - for (int i = 0; i < n; i++) - { - yield return VisualTreeHelper.GetChild(dependencyObject, i); - } - } - } -} From 75fed45a555608cf3f0051154c864f5a89f1e9ad Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Wed, 1 Apr 2020 16:41:23 +0200 Subject: [PATCH 4/8] Removed unneeded event handler --- source/TestApp/TestUserControl.xaml.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/TestApp/TestUserControl.xaml.cs b/source/TestApp/TestUserControl.xaml.cs index f2122679..6a330fac 100644 --- a/source/TestApp/TestUserControl.xaml.cs +++ b/source/TestApp/TestUserControl.xaml.cs @@ -23,12 +23,6 @@ public TestUserControl() this.Loaded += new RoutedEventHandler(TestUserControl_Loaded); this.Unloaded += new RoutedEventHandler(TestUserControl_Unloaded); - this.SizeChanged += TestUserControl_SizeChanged; - } - - private void TestUserControl_SizeChanged(object sender, SizeChangedEventArgs e) - { - } void TestUserControl_Unloaded(object sender, RoutedEventArgs e) From 9db47c905c6b26c5d4ceac6a5e793d91745d3d59 Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Fri, 3 Apr 2020 09:09:10 +0200 Subject: [PATCH 5/8] Cleanup and rearranging after self-code review --- .../Controls/LayoutFloatingWindowControl.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index bed2e58f..af209a42 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -45,7 +45,13 @@ public abstract class LayoutFloatingWindowControl : Window, ILayoutControl private DragService _dragService = null; private bool _internalCloseFlag = false; private bool _isClosing = false; - #endregion fields + + /// + /// Margin queried from the visual tree the first time it is rendered, null until the first first FilterMessage(WM_ACTIVATE) + /// + private Thickness? _totalMargin; + + #endregion fields #region Constructors @@ -233,8 +239,6 @@ internal void AttachDrag(bool onActivated = true) } } - internal Thickness TotalMargin { get; private set; } - private bool _foundMargin = false; protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { handled = false; @@ -298,7 +302,7 @@ private void UpdateMargins() .FirstOrDefault(c => c.Content is LayoutContent); if (contentControl == null) return; - // The content control in the grid, this has a different visual tree to walk up + // The content control in the grid, this has a different tree to walk up var layoutContent = (LayoutContent)contentControl.Content; if (grid != null && layoutContent.Content is FrameworkElement content) { @@ -326,17 +330,16 @@ private void UpdateMargins() var margin = frameworkElements.Sum(f => f.Margin); margin = margin.Add(padding).Add(border).Add(grid.Margin); margin.Top = grid.RowDefinitions[0].MinHeight; - TotalMargin = margin; - _foundMargin = true; + _totalMargin = margin; } } private void UpdateWindowsSizeBasedOnMinSize() { - if (!_foundMargin) + if (!_totalMargin.HasValue) { UpdateMargins(); - if (_foundMargin) + if (_totalMargin.HasValue) { ContentPresenter contentControl = this.GetChildrenRecursive() .OfType() @@ -353,13 +356,13 @@ private void UpdateWindowsSizeBasedOnMinSize() if (content.ActualHeight < content.MinHeight || parent != null && parent.ActualHeight < content.MinHeight) { - Height = content.MinHeight + TotalMargin.Top + TotalMargin.Bottom; + Height = content.MinHeight + _totalMargin.Value.Top + _totalMargin.Value.Bottom; } if (content.ActualWidth < content.MinWidth || parent != null && parent.ActualWidth < content.MinWidth) { - Width = content.MinWidth + TotalMargin.Left + TotalMargin.Right; + Width = content.MinWidth + _totalMargin.Value.Left + _totalMargin.Value.Right; } } } From 07e0ca95313d59d34b904cbc08ef1f5701f21c27 Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Mon, 6 Apr 2020 09:00:14 +0200 Subject: [PATCH 6/8] Reverted copyright notice year (1 in https://github.com/Dirkster99/AvalonDock/pull/146#issuecomment-608575110) --- .../AvalonDock/Controls/LayoutFloatingWindowControl.cs | 2 +- source/Components/AvalonDock/Extensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index af209a42..78cd7e69 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -1,7 +1,7 @@ /************************************************************************ AvalonDock - Copyright (C) 2007-2020 Xceed Software Inc. + 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 diff --git a/source/Components/AvalonDock/Extensions.cs b/source/Components/AvalonDock/Extensions.cs index c0229e01..4dda3fa2 100644 --- a/source/Components/AvalonDock/Extensions.cs +++ b/source/Components/AvalonDock/Extensions.cs @@ -1,7 +1,7 @@ /************************************************************************ AvalonDock - Copyright (C) 2007-2020 Xceed Software Inc. + 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 From 61f0e882ab407a946f6627ecf8a24643dd0924aa Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Mon, 6 Apr 2020 09:14:08 +0200 Subject: [PATCH 7/8] Added comments to new functions 2 in https://github.com/Dirkster99/AvalonDock/pull/146#issuecomment-608575110 --- .../Controls/LayoutFloatingWindowControl.cs | 13 +++- source/Components/AvalonDock/Extensions.cs | 77 +++++++++++++------ 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 78cd7e69..31205c8a 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -291,7 +291,12 @@ protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntP return IntPtr.Zero; } - private void UpdateMargins() + /// + /// Set the margins of the window control (including the borders of the floating window and the title bar). + /// The result will be stored in _totalMargin. + /// + /// If the control is not loaded _totalMargin will not be set. + private void UpdateMargins() { // The grid with window bar and content var grid = this.GetChildrenRecursive() @@ -334,7 +339,11 @@ private void UpdateMargins() } } - private void UpdateWindowsSizeBasedOnMinSize() + /// + /// Update the floating window size based on the MinHeight and MinWidth of the content of the control. + /// + /// This will only be run once, when the window is rendered the first time and _totalMargin is identified. + private void UpdateWindowsSizeBasedOnMinSize() { if (!_totalMargin.HasValue) { diff --git a/source/Components/AvalonDock/Extensions.cs b/source/Components/AvalonDock/Extensions.cs index 4dda3fa2..b45fe5b5 100644 --- a/source/Components/AvalonDock/Extensions.cs +++ b/source/Components/AvalonDock/Extensions.cs @@ -8,41 +8,47 @@ This program is provided to you under the terms of the Microsoft Public ************************************************************************/ using System; -using System.Collections.Generic; using System.Collections; +using System.Collections.Generic; using System.Windows; using System.Windows.Media; namespace AvalonDock { - internal static class Extensions - { - public static bool Contains(this IEnumerable collection, object item) - { - foreach (var o in collection) - if (o == item) return true; - return false; - } + internal static class Extensions + { + public static bool Contains(this IEnumerable collection, object item) + { + foreach (var o in collection) + if (o == item) return true; + return false; + } - public static void ForEach(this IEnumerable collection, Action action) - { - foreach (var v in collection) action(v); - } + public static void ForEach(this IEnumerable collection, Action action) + { + foreach (var v in collection) action(v); + } - public static int IndexOf(this T[] array, T value) where T : class - { - for (var i = 0; i < array.Length; i++) - if (array[i] == value) return i; - return -1; - } + public static int IndexOf(this T[] array, T value) where T : class + { + for (var i = 0; i < array.Length; i++) + if (array[i] == value) return i; + return -1; + } - public static V GetValueOrDefault(this WeakReference wr) - { - return wr == null || !wr.IsAlive ? default : (V) wr.Target; - } + public static V GetValueOrDefault(this WeakReference wr) + { + return wr == null || !wr.IsAlive ? default : (V)wr.Target; + } + /// + /// Recursively get the visual tree children of a dependency object. + /// + /// This function is recursive and will return all children. + /// The object to find the children of + /// An enumerable with all the children of the dependency object public static IEnumerable GetChildrenRecursive(this DependencyObject dependencyObject) { var children = dependencyObject.GetChildren(); @@ -56,6 +62,12 @@ public static IEnumerable GetChildrenRecursive(this Dependency } } + /// + /// Get the visual tree children of a dependency object. + /// + /// This function is not recursive and will only return the first level of children. + /// The object to find the children of + /// An enumerable with all the first level children of the dependency object public static IEnumerable GetChildren(this DependencyObject dependencyObject) { int n = VisualTreeHelper.GetChildrenCount(dependencyObject); @@ -65,6 +77,12 @@ public static IEnumerable GetChildren(this DependencyObject de } } + /// + /// Get the visual tree parents of a dependency object, + /// + /// The object to find the parents of + /// An enumerable with the parents of the dependencyObject, the first parent returned will be + /// the direct parent of dependencyObject public static IEnumerable GetParents(this DependencyObject dependencyObject) { while (dependencyObject != null) @@ -75,6 +93,13 @@ public static IEnumerable GetParents(this DependencyObject dep } } + /// + /// Calculate the sum multiple thicknesses in an enumerable + /// + /// The type in the enumerable + /// The enumerable with thicknesses + /// A function returning the thickness from T + /// The total thickness public static Thickness Sum(this IEnumerable enumerable, Func func) { double top = 0, bottom = 0, left = 0, right = 0; @@ -89,6 +114,12 @@ public static Thickness Sum(this IEnumerable enumerable, Func + /// Add two thicknesses, each individual component will be added independently of the others. + /// + /// The first thickness + /// The second thickness + /// The total thickness public static Thickness Add(this Thickness thickness, Thickness other) { return new Thickness(thickness.Left + other.Left, From a5c663e45a47637d9d838bdcdc7ee497ec142f67 Mon Sep 17 00:00:00 2001 From: Erik Ovegard Date: Tue, 7 Apr 2020 09:26:35 +0200 Subject: [PATCH 8/8] Added dependency properties --- .../Controls/LayoutFloatingWindowControl.cs | 146 ++++++++++++++---- 1 file changed, 119 insertions(+), 27 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 31205c8a..b6805938 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -47,10 +47,10 @@ public abstract class LayoutFloatingWindowControl : Window, ILayoutControl private bool _isClosing = false; /// - /// Margin queried from the visual tree the first time it is rendered, null until the first first FilterMessage(WM_ACTIVATE) + /// Is false until the margins have been found once. /// - private Thickness? _totalMargin; - + /// + private bool _isTotalMarginSet = false; #endregion fields #region Constructors @@ -187,6 +187,90 @@ protected override void OnStateChanged(EventArgs e) #endregion IsMaximized + #region TotalMargin + + private static readonly DependencyPropertyKey TotalMarginPropertyKey = + DependencyProperty.RegisterReadOnly(nameof(TotalMargin), + typeof(Thickness), + typeof(LayoutFloatingWindowControl), + new FrameworkPropertyMetadata(default(Thickness))); + + public static readonly DependencyProperty TotalMarginProperty = TotalMarginPropertyKey.DependencyProperty; + + /// + /// The total margin (including window chrome and title bar). + /// + /// The margin is queried from the visual tree the first time it is rendered, zero until the first call of FilterMessage(WM_ACTIVATE) + /// + public Thickness TotalMargin + { + get { return (Thickness) GetValue(TotalMarginProperty); } + protected set { SetValue(TotalMarginPropertyKey, value); } + } + + #endregion + + #region ContentMinHeight + public static readonly DependencyPropertyKey ContentMinHeightPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(ContentMinHeight), typeof(double), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(0.0)); + + public static readonly DependencyProperty ContentMinHeightProperty = + ContentMinHeightPropertyKey.DependencyProperty; + + /// + /// The MinHeight of the content of the window, will be 0 until the window has been rendered, or if the MinHeight is unset for the content + /// + public double ContentMinHeight + { + get { return (double) GetValue(ContentMinHeightProperty); } + set { SetValue(ContentMinHeightPropertyKey, value); } + } + #endregion + + #region ContentMinWidth + public static readonly DependencyPropertyKey ContentMinWidthPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(ContentMinWidth), typeof(double), typeof(LayoutFloatingWindowControl), new FrameworkPropertyMetadata(0.0)); + + public static readonly DependencyProperty ContentMinWidthProperty = + ContentMinWidthPropertyKey.DependencyProperty; + + /// + /// The MinWidth ocf the content of the window, will be 0 until the window has been rendered, or if the MinWidth is unset for the content + /// + public double ContentMinWidth + { + get { return (double) GetValue(ContentMinWidthProperty); } + set { SetValue(ContentMinWidthPropertyKey, value); } + } + #endregion + + #region SetWindowSizeWhenOpened + + public static readonly DependencyProperty SetWindowSizeWhenOpenedProperty = DependencyProperty.Register( + nameof(SetWindowSizeWhenOpened), typeof(bool), typeof(LayoutFloatingWindowControl), new PropertyMetadata(false, + (sender, args) => + { + if (args.OldValue != args.NewValue && + sender is LayoutFloatingWindowControl control) + { + // This will resize the window when this property is set to true and the window is open + control._isTotalMarginSet = false; + } + })); + + /// + /// If true the MinHeight and MinWidth of the content will be used together with the margins to determine the initial size of the floating window + /// + /// + /// + /// + public bool SetWindowSizeWhenOpened + { + get { return (bool) GetValue(SetWindowSizeWhenOpenedProperty); } + set { SetValue(SetWindowSizeWhenOpenedProperty, value); } + } + + #endregion #endregion Properties #region Internal Methods @@ -335,8 +419,9 @@ private void UpdateMargins() var margin = frameworkElements.Sum(f => f.Margin); margin = margin.Add(padding).Add(border).Add(grid.Margin); margin.Top = grid.RowDefinitions[0].MinHeight; - _totalMargin = margin; - } + TotalMargin = margin; + _isTotalMarginSet = true; + } } /// @@ -345,33 +430,40 @@ private void UpdateMargins() /// This will only be run once, when the window is rendered the first time and _totalMargin is identified. private void UpdateWindowsSizeBasedOnMinSize() { - if (!_totalMargin.HasValue) + if (!_isTotalMarginSet) { UpdateMargins(); - if (_totalMargin.HasValue) + if(_isTotalMarginSet) { - ContentPresenter contentControl = this.GetChildrenRecursive() + // The LayoutAnchorableControl is bound via the ContentPresenter, hence it is best to do below in code and not in a style + // See https://github.com/Dirkster99/AvalonDock/pull/146#issuecomment-609974424 + var layoutContents = this.GetChildrenRecursive() .OfType() - .FirstOrDefault(c => c.Content is LayoutContent); - if (contentControl == null) - return; - var layoutContent = (LayoutContent)contentControl.Content; - if (layoutContent.Content is FrameworkElement content) - { - var parent = content.GetParents() - .OfType() - .FirstOrDefault(); - // StackPanels among others have an ActualHeight larger than visible, hence we check the parent control as well - if (content.ActualHeight < content.MinHeight || - parent != null && parent.ActualHeight < content.MinHeight) - { - Height = content.MinHeight + _totalMargin.Value.Top + _totalMargin.Value.Bottom; - } - - if (content.ActualWidth < content.MinWidth || - parent != null && parent.ActualWidth < content.MinWidth) + .Select(c => c.Content) + .OfType() + .Select(lc => lc.Content); + var contents = layoutContents.OfType(); + foreach (var content in contents) + { + ContentMinHeight = Math.Max(content.MinHeight, ContentMinHeight); + ContentMinWidth = Math.Max(content.MinWidth, ContentMinWidth); + if (SetWindowSizeWhenOpened) { - Width = content.MinWidth + _totalMargin.Value.Left + _totalMargin.Value.Right; + var parent = content.GetParents() + .OfType() + .FirstOrDefault(); + // StackPanels among others have an ActualHeight larger than visible, hence we check the parent control as well + if (content.ActualHeight < content.MinHeight || + parent != null && parent.ActualHeight < content.MinHeight) + { + Height = content.MinHeight + TotalMargin.Top + TotalMargin.Bottom; + } + + if (content.ActualWidth < content.MinWidth || + parent != null && parent.ActualWidth < content.MinWidth) + { + Width = content.MinWidth + TotalMargin.Left + TotalMargin.Right; + } } } }