diff --git a/docs/dock-dockable-properties.md b/docs/dock-dockable-properties.md
index 6bef4fc1d..71755fd64 100644
--- a/docs/dock-dockable-properties.md
+++ b/docs/dock-dockable-properties.md
@@ -15,6 +15,10 @@ Dockable items such as documents, tools and docks implement the `IDockable` inte
| `IsEmpty` | Indicates a placeholder dockable with no content. |
| `IsCollapsable` | When `false`, the dock will remain even if it contains no children. |
| `Proportion` | Size ratio used by `ProportionalDock`. |
+| `MinWidth` | Optional minimum width. Overrides the current proportion if larger. |
+| `MaxWidth` | Optional maximum width. Overrides the proportion if smaller. |
+| `MinHeight` | Optional minimum height. Overrides the current proportion if larger. |
+| `MaxHeight` | Optional maximum height. Overrides the proportion if smaller. |
| `CanClose` | Whether the user can close the dockable via UI commands. |
| `CanPin` | Allows pinning and unpinning of tools. |
| `CanFloat` | Controls if the item may be detached into a floating window. |
diff --git a/docs/dock-reference.md b/docs/dock-reference.md
index 755d3dab3..93d0cd98d 100644
--- a/docs/dock-reference.md
+++ b/docs/dock-reference.md
@@ -6,7 +6,7 @@ This reference summarizes the most commonly used classes in Dock. It is based on
| Type | Description |
| --- | --- |
-| `IDockable` | Base interface for items that can be shown in a dock. Provides `Id`, `Title`, `Context` and lifecycle methods like `OnClose`. |
+| `IDockable` | Base interface for items that can be shown in a dock. Provides `Id`, `Title`, `Context`, optional size limits and lifecycle methods like `OnClose`. |
| `IDock` | Extends `IDockable` with collections of visible dockables and commands such as `GoBack`, `GoForward`, `Navigate` or `Close`. |
| `IRootDock` | The top level container. In addition to the `IDock` members it exposes pinned dock collections and commands to manage windows. |
| `IProportionalDock` | A dock that lays out its children horizontally or vertically using a `Proportion` value. |
diff --git a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
index 6525a3333..c404d7235 100644
--- a/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
+++ b/src/Dock.Avalonia/Controls/ProportionalDockControl.axaml
@@ -20,7 +20,7 @@
Classes="ProportionalStackPanel">
diff --git a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
index 7f04cd0f8..689d5e096 100644
--- a/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
+++ b/src/Dock.Avalonia/Controls/ToolChromeControl.axaml
@@ -48,6 +48,10 @@
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/ToolContentControl.axaml b/src/Dock.Avalonia/Controls/ToolContentControl.axaml
index 8d7aba83e..21c877b35 100644
--- a/src/Dock.Avalonia/Controls/ToolContentControl.axaml
+++ b/src/Dock.Avalonia/Controls/ToolContentControl.axaml
@@ -11,6 +11,10 @@
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/ToolControl.axaml b/src/Dock.Avalonia/Controls/ToolControl.axaml
index c35df65a4..b3be96132 100644
--- a/src/Dock.Avalonia/Controls/ToolControl.axaml
+++ b/src/Dock.Avalonia/Controls/ToolControl.axaml
@@ -12,6 +12,10 @@
+
+
+
+
diff --git a/src/Dock.Avalonia/Controls/ToolDockControl.axaml b/src/Dock.Avalonia/Controls/ToolDockControl.axaml
index 4276e8be5..524a71d3e 100644
--- a/src/Dock.Avalonia/Controls/ToolDockControl.axaml
+++ b/src/Dock.Avalonia/Controls/ToolDockControl.axaml
@@ -11,6 +11,10 @@
+
+
+
+
diff --git a/src/Dock.Controls.ProportionalStackPanel/ProportionalStackPanel.cs b/src/Dock.Controls.ProportionalStackPanel/ProportionalStackPanel.cs
index 798f209ef..9ec799828 100644
--- a/src/Dock.Controls.ProportionalStackPanel/ProportionalStackPanel.cs
+++ b/src/Dock.Controls.ProportionalStackPanel/ProportionalStackPanel.cs
@@ -87,12 +87,12 @@ public static void SetIsCollapsed(AvaloniaObject control, bool value)
control.SetValue(IsCollapsedProperty, value);
}
- private void AssignProportions()
+ private void AssignProportions(Size size, double splitterThickness)
{
isAssigningProportions = true;
try
{
- AssignProportionsInternal(Children);
+ AssignProportionsInternal(Children, size, splitterThickness, Orientation);
}
finally
{
@@ -100,14 +100,49 @@ private void AssignProportions()
}
}
- private static void AssignProportionsInternal(Avalonia.Controls.Controls children)
+ private static double ClampProportion(
+ Control control,
+ Orientation orientation,
+ double available,
+ double proportion)
{
+ double min = orientation == Orientation.Horizontal ? control.MinWidth : control.MinHeight;
+ double max = orientation == Orientation.Horizontal ? control.MaxWidth : control.MaxHeight;
+
+ var minProp = !double.IsNaN(min) && min > 0 ? min / available : 0.0;
+ var maxProp = !double.IsNaN(max) && !double.IsPositiveInfinity(max) ? max / available : double.PositiveInfinity;
+
+#if NETSTANDARD2_0
+ var clamped = Clamp(proportion, minProp, maxProp);
+#else
+ var clamped = Math.Clamp(proportion, minProp, maxProp);
+#endif
+ return clamped;
+
+#if NETSTANDARD2_0
+ static double Clamp(double value, double min, double max)
+ {
+ if (value < min) return min;
+ if (value > max) return max;
+ return value;
+ }
+#endif
+ }
+
+ private static void AssignProportionsInternal(
+ Avalonia.Controls.Controls children,
+ Size size,
+ double splitterThickness,
+ Orientation orientation)
+ {
+ var dimension = orientation == Orientation.Horizontal ? size.Width : size.Height;
+ var dimensionWithoutSplitters = Math.Max(1.0, dimension - splitterThickness);
+
var assignedProportion = 0.0;
var unassignedProportions = 0;
- for (var i = 0; i < children.Count; i++)
+ foreach (var control in children.OfType())
{
- var control = children[i];
var isCollapsed = GetIsCollapsed(control);
var isSplitter = ProportionalStackPanelSplitter.IsSplitter(control, out _);
@@ -126,6 +161,8 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
}
else
{
+ proportion = ClampProportion(control, orientation, dimensionWithoutSplitters, proportion);
+ SetProportion(control, proportion);
assignedProportion += proportion;
}
}
@@ -143,8 +180,9 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
if (!ProportionalStackPanelSplitter.IsSplitter(control, out _))
{
var proportion = (1.0 - toAssign) / unassignedProportions;
+ proportion = ClampProportion(control, orientation, dimensionWithoutSplitters, proportion);
SetProportion(control, proportion);
- assignedProportion += (1.0 - toAssign) / unassignedProportions;
+ assignedProportion += proportion;
}
}
}
@@ -156,9 +194,7 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
var isCollapsed = GetIsCollapsed(c);
return !ProportionalStackPanelSplitter.IsSplitter(c, out _) && !isCollapsed;
});
-
var toAdd = (1.0 - assignedProportion) / numChildren;
-
foreach (var child in children.Where(c =>
{
var isCollapsed = GetIsCollapsed(c);
@@ -176,9 +212,7 @@ private static void AssignProportionsInternal(Avalonia.Controls.Controls childre
var isCollapsed = GetIsCollapsed(c);
return !ProportionalStackPanelSplitter.IsSplitter(c, out _) && !isCollapsed;
});
-
var toRemove = (assignedProportion - 1.0) / numChildren;
-
foreach (var child in children.Where(c =>
{
var isCollapsed = GetIsCollapsed(c);
@@ -249,7 +283,7 @@ protected override Size MeasureOverride(Size constraint)
var maximumHeight = 0.0;
var splitterThickness = GetTotalSplitterThickness(Children);
- AssignProportions();
+ AssignProportions(constraint, splitterThickness);
var needsNextSplitter = false;
double sumOfFractions = 0;
@@ -372,7 +406,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
var splitterThickness = GetTotalSplitterThickness(Children);
var index = 0;
- AssignProportions();
+ AssignProportions(arrangeSize, splitterThickness);
var needsNextSplitter = false;
double sumOfFractions = 0;
diff --git a/src/Dock.Model.Avalonia/Controls/Tool.cs b/src/Dock.Model.Avalonia/Controls/Tool.cs
index 221c7c59f..86589a98f 100644
--- a/src/Dock.Model.Avalonia/Controls/Tool.cs
+++ b/src/Dock.Model.Avalonia/Controls/Tool.cs
@@ -19,39 +19,13 @@ namespace Dock.Model.Avalonia.Controls;
[DataContract(IsReference = true)]
public class Tool : DockableBase, ITool, IDocument, IToolContent, ITemplate, IRecyclingDataTemplate
{
- private double _minWidth = double.NaN;
- private double _maxWidth = double.NaN;
- private double _minHeight = double.NaN;
- private double _maxHeight = double.NaN;
///
/// Defines the property.
///
public static readonly StyledProperty