Skip to content

Commit 8ee8084

Browse files
Add MultipleX and MultipleY to ConstrainedBox for floor snap to a multiple value.
1 parent 5283769 commit 8ee8084

File tree

2 files changed

+78
-27
lines changed

2 files changed

+78
-27
lines changed

Microsoft.Toolkit.Uwp.UI.Controls.Primitives/ConstrainedBox/ConstrainedBox.Properties.cs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,10 @@
1414
namespace Microsoft.Toolkit.Uwp.UI.Controls
1515
{
1616
/// <summary>
17-
/// The <see cref="ConstrainedBox"/> is a <see cref="FrameworkElement"/> control akin to <see cref="Viewbox"/>
18-
/// which can modify the behavior of it's child element's layout. <see cref="ConstrainedBox"/> restricts the
19-
/// available size for its content based on a scale factor and/or a specific <see cref="AspectRatio"/>.
20-
/// This is performed as a layout calculation modification.
17+
/// Dependency properties for the <see cref="ConstrainedBox"/> class.
2118
/// </summary>
22-
/// <remarks>
23-
/// Note that this class being implemented as a <see cref="ContentPresenter"/> is an implementation detail, and
24-
/// is not meant to be used as one with a template. It is recommended to avoid styling the frame of the control
25-
/// with borders and not using <see cref="ContentPresenter.ContentTemplate"/> for future compatibility of your
26-
/// code if moving to WinUI 3 in the future.
27-
/// </remarks>
2819
public partial class ConstrainedBox
2920
{
30-
/// <summary>
31-
/// Gets or sets aspect Ratio to use for the contents of the Panel (after scaling).
32-
/// </summary>
33-
public AspectRatio AspectRatio
34-
{
35-
get { return (AspectRatio)GetValue(AspectRatioProperty); }
36-
set { SetValue(AspectRatioProperty, value); }
37-
}
38-
39-
/// <summary>
40-
/// Identifies the <see cref="AspectRatio"/> property.
41-
/// </summary>
42-
public static readonly DependencyProperty AspectRatioProperty =
43-
DependencyProperty.Register(nameof(AspectRatio), typeof(AspectRatio), typeof(ConstrainedBox), new PropertyMetadata(null, ConstraintPropertyChanged));
44-
4521
/// <summary>
4622
/// Gets or sets the scale for the width of the panel. Should be a value between 0-1.0. Default is 1.0.
4723
/// </summary>
@@ -72,6 +48,51 @@ public double ScaleY
7248
public static readonly DependencyProperty ScaleYProperty =
7349
DependencyProperty.Register(nameof(ScaleY), typeof(double), typeof(ConstrainedBox), new PropertyMetadata(1.0, ConstraintPropertyChanged));
7450

51+
/// <summary>
52+
/// Gets or sets the integer multiple that the width of the panel should be floored to. Default is null (no snap).
53+
/// </summary>
54+
public int MultipleX
55+
{
56+
get { return (int)GetValue(MultipleXProperty); }
57+
set { SetValue(MultipleXProperty, value); }
58+
}
59+
60+
/// <summary>
61+
/// Identifies the <see cref="MultipleX"/> property.
62+
/// </summary>
63+
public static readonly DependencyProperty MultipleXProperty =
64+
DependencyProperty.Register(nameof(MultipleX), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null));
65+
66+
/// <summary>
67+
/// Gets or sets the integer multiple that the height of the panel should be floored to. Default is null (no snap).
68+
/// </summary>
69+
public int MultipleY
70+
{
71+
get { return (int)GetValue(MultipleYProperty); }
72+
set { SetValue(MultipleYProperty, value); }
73+
}
74+
75+
/// <summary>
76+
/// Identifies the <see cref="MultipleY"/> property.
77+
/// </summary>
78+
public static readonly DependencyProperty MultipleYProperty =
79+
DependencyProperty.Register(nameof(MultipleY), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null));
80+
81+
/// <summary>
82+
/// Gets or sets aspect Ratio to use for the contents of the Panel (after scaling).
83+
/// </summary>
84+
public AspectRatio AspectRatio
85+
{
86+
get { return (AspectRatio)GetValue(AspectRatioProperty); }
87+
set { SetValue(AspectRatioProperty, value); }
88+
}
89+
90+
/// <summary>
91+
/// Identifies the <see cref="AspectRatio"/> property.
92+
/// </summary>
93+
public static readonly DependencyProperty AspectRatioProperty =
94+
DependencyProperty.Register(nameof(AspectRatio), typeof(AspectRatio), typeof(ConstrainedBox), new PropertyMetadata(null, ConstraintPropertyChanged));
95+
7596
private static void ConstraintPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
7697
{
7798
if (d is ConstrainedBox panel)

Microsoft.Toolkit.Uwp.UI.Controls.Primitives/ConstrainedBox/ConstrainedBox.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ protected override Size ArrangeOverride(Size finalSize)
8686

8787
private void CalculateConstrainedSize(ref Size availableSize)
8888
{
89+
// 1) We check for Infinity, in the case we have no constraint from parent
90+
// we'll request the child's measurements first, so we can use that as
91+
// a starting point to constrain it's dimensions based on the criteria
92+
// set in our properties.
8993
var hasWidth = IsPositiveRealNumber(availableSize.Width);
9094
var hasHeight = IsPositiveRealNumber(availableSize.Height);
9195

@@ -106,13 +110,39 @@ private void CalculateConstrainedSize(ref Size availableSize)
106110
}
107111
}
108112

109-
// Scale size first before we constrain aspect ratio
113+
// 2) Apply Scales to constrain based on a percentage
114+
// --------------------------------------------------
110115
availableSize.Width *= ScaleX;
111116
availableSize.Height *= ScaleY;
112117

113-
// If we don't have an Aspect Ratio, just return the scaled value.
118+
// 3) Apply Multiples
119+
// ------------------
120+
// These floor the Width/Height values to the nearest multiple of the property (if set).
121+
// For instance you may have a responsive 4x4 repeated checkerboard pattern for transparency and
122+
// want to snap to the nearest interval of 4 so the checkerboard is consistency across the layout.
123+
if (hasWidth &&
124+
ReadLocalValue(MultipleXProperty) != DependencyProperty.UnsetValue &&
125+
MultipleX > 0)
126+
{
127+
availableSize.Width -= availableSize.Width % MultipleX;
128+
}
129+
130+
if (hasHeight &&
131+
ReadLocalValue(MultipleYProperty) != DependencyProperty.UnsetValue &&
132+
MultipleY > 0)
133+
{
134+
availableSize.Height -= availableSize.Height % MultipleY;
135+
}
136+
137+
// 4) Apply AspectRatio
138+
// --------------------
139+
// Finally, we apply the AspectRatio property after we've determined the general
140+
// area we have to work with based on the other constraints.
141+
// Devs should be careful if they use both a MultipleX&Y that the AspectRatio is also
142+
// within that same ratio. The Ratio will take preference here as the last step.
114143
if (ReadLocalValue(AspectRatioProperty) == DependencyProperty.UnsetValue)
115144
{
145+
// Skip as last constraint if we have nothing to do.
116146
return;
117147
}
118148

0 commit comments

Comments
 (0)