22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5+ using Microsoft . UI . Xaml . Controls ;
56using System . Data ;
67
78namespace CommunityToolkit . WinUI . Controls ;
@@ -14,15 +15,6 @@ public partial class EqualPanel : Panel
1415 private double _maxItemWidth = 0 ;
1516 private double _maxItemHeight = 0 ;
1617 private int _visibleItemsCount = 0 ;
17-
18- /// <summary>
19- /// Gets or sets the spacing between items.
20- /// </summary>
21- public double Spacing
22- {
23- get { return ( double ) GetValue ( SpacingProperty ) ; }
24- set { SetValue ( SpacingProperty , value ) ; }
25- }
2618
2719 /// <summary>
2820 /// Identifies the Spacing dependency property.
@@ -32,14 +24,42 @@ public double Spacing
3224 nameof ( Spacing ) ,
3325 typeof ( double ) ,
3426 typeof ( EqualPanel ) ,
35- new PropertyMetadata ( default ( double ) , OnSpacingChanged ) ) ;
27+ new PropertyMetadata ( default ( double ) , OnPropertyChanged ) ) ;
28+
29+ /// <summary>
30+ /// Backing <see cref="DependencyProperty"/> for the <see cref="Orientation"/> property.
31+ /// </summary>
32+ public static readonly DependencyProperty OrientationProperty = DependencyProperty . Register (
33+ nameof ( Orientation ) ,
34+ typeof ( Orientation ) ,
35+ typeof ( EqualPanel ) ,
36+ new PropertyMetadata ( default ( Orientation ) , OnPropertyChanged ) ) ;
37+
38+ /// <summary>
39+ /// Gets or sets the spacing between items.
40+ /// </summary>
41+ public double Spacing
42+ {
43+ get => ( double ) GetValue ( SpacingProperty ) ;
44+ set => SetValue ( SpacingProperty , value ) ;
45+ }
46+
47+ /// <summary>
48+ /// Gets or sets the panel orientation.
49+ /// </summary>
50+ public Orientation Orientation
51+ {
52+ get => ( Orientation ) GetValue ( OrientationProperty ) ;
53+ set => SetValue ( OrientationProperty , value ) ;
54+ }
3655
3756 /// <summary>
3857 /// Creates a new instance of the <see cref="EqualPanel"/> class.
3958 /// </summary>
4059 public EqualPanel ( )
4160 {
42- RegisterPropertyChangedCallback ( HorizontalAlignmentProperty , OnHorizontalAlignmentChanged ) ;
61+ RegisterPropertyChangedCallback ( HorizontalAlignmentProperty , OnAlignmentChanged ) ;
62+ RegisterPropertyChangedCallback ( VerticalAlignmentProperty , OnAlignmentChanged ) ;
4363 }
4464
4565 /// <inheritdoc/>
@@ -60,19 +80,39 @@ protected override Size MeasureOverride(Size availableSize)
6080
6181 if ( _visibleItemsCount > 0 )
6282 {
63- // Return equal widths based on the widest item
64- // In very specific edge cases the AvailableWidth might be infinite resulting in a crash.
65- if ( HorizontalAlignment != HorizontalAlignment . Stretch || double . IsInfinity ( availableSize . Width ) )
83+ bool stretch = Orientation switch
84+ {
85+ Orientation . Horizontal => HorizontalAlignment is HorizontalAlignment . Stretch && ! double . IsInfinity ( availableSize . Width ) ,
86+ Orientation . Vertical or _ => VerticalAlignment is VerticalAlignment . Stretch && ! double . IsInfinity ( availableSize . Height ) ,
87+ } ;
88+
89+ // Define XY coords
90+ double xSize = 0 , ySize = 0 ;
91+
92+ // Define UV coords for orientation agnostic XY manipulation
93+ ref double uSize = ref SelectAxis ( Orientation , ref xSize , ref ySize , true ) ;
94+ ref double vSize = ref SelectAxis ( Orientation , ref xSize , ref ySize , false ) ;
95+ ref double maxItemU = ref SelectAxis ( Orientation , ref _maxItemWidth , ref _maxItemHeight , true ) ;
96+ ref double maxItemV = ref SelectAxis ( Orientation , ref _maxItemWidth , ref _maxItemHeight , false ) ;
97+ double availableU = Orientation is Orientation . Horizontal ? availableSize . Width : availableSize . Height ;
98+
99+ if ( stretch )
66100 {
67- return new Size ( ( _maxItemWidth * _visibleItemsCount ) + ( Spacing * ( _visibleItemsCount - 1 ) ) , _maxItemHeight ) ;
101+ // Adjust maxItemU to form equal rows/columns by available U space (adjust for spacing)
102+ double totalU = availableU - ( Spacing * ( _visibleItemsCount - 1 ) ) ;
103+ maxItemU = totalU / _visibleItemsCount ;
104+
105+ // Set uSize/vSize for XY result contstruction
106+ uSize = availableU ;
107+ vSize = maxItemV ;
68108 }
69109 else
70110 {
71- // Equal columns based on the available width, adjust for spacing
72- double totalWidth = availableSize . Width - ( Spacing * ( _visibleItemsCount - 1 ) ) ;
73- _maxItemWidth = totalWidth / _visibleItemsCount ;
74- return new Size ( availableSize . Width , _maxItemHeight ) ;
111+ uSize = ( maxItemU * _visibleItemsCount ) + ( Spacing * ( _visibleItemsCount - 1 ) ) ;
112+ vSize = maxItemV ;
75113 }
114+
115+ return new Size ( xSize , ySize ) ;
76116 }
77117 else
78118 {
@@ -83,31 +123,53 @@ protected override Size MeasureOverride(Size availableSize)
83123 /// <inheritdoc/>
84124 protected override Size ArrangeOverride ( Size finalSize )
85125 {
126+ // Define X and Y
86127 double x = 0 ;
128+ double y = 0 ;
87129
130+ // Define UV axis
131+ ref double u = ref x ;
132+ ref double maxItemU = ref _maxItemWidth ;
133+ double finalSizeU = finalSize . Width ;
134+ if ( Orientation is Orientation . Vertical )
135+ {
136+ u = ref y ;
137+ maxItemU = ref _maxItemHeight ;
138+ finalSizeU = finalSize . Height ;
139+ }
140+
88141 // Check if there's more (little) width available - if so, set max item width to the maximum possible as we have an almost perfect height.
89- if ( finalSize . Width > _visibleItemsCount * _maxItemWidth + ( Spacing * ( _visibleItemsCount - 1 ) ) )
142+ if ( finalSizeU > _visibleItemsCount * maxItemU + ( Spacing * ( _visibleItemsCount - 1 ) ) )
90143 {
91- _maxItemWidth = ( finalSize . Width - ( Spacing * ( _visibleItemsCount - 1 ) ) ) / _visibleItemsCount ;
144+ maxItemU = ( finalSizeU - ( Spacing * ( _visibleItemsCount - 1 ) ) ) / _visibleItemsCount ;
92145 }
93146
94147 var elements = Children . Where ( static e => e . Visibility == Visibility . Visible ) ;
95148 foreach ( var child in elements )
96149 {
97- child . Arrange ( new Rect ( x , 0 , _maxItemWidth , _maxItemHeight ) ) ;
98- x += _maxItemWidth + Spacing ;
150+ // NOTE: The arrange method is still in X/Y coordinate system
151+ child . Arrange ( new Rect ( x , y , _maxItemWidth , _maxItemHeight ) ) ;
152+ u += maxItemU + Spacing ;
99153 }
100154 return finalSize ;
101155 }
102156
103- private void OnHorizontalAlignmentChanged ( DependencyObject sender , DependencyProperty dp )
157+ private void OnAlignmentChanged ( DependencyObject sender , DependencyProperty dp )
104158 {
105159 InvalidateMeasure ( ) ;
106160 }
107161
108- private static void OnSpacingChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
162+ private static void OnPropertyChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
109163 {
110164 var panel = ( EqualPanel ) d ;
111165 panel . InvalidateMeasure ( ) ;
112166 }
167+
168+ private static ref double SelectAxis ( Orientation orientation , ref double x , ref double y , bool u )
169+ {
170+ if ( ( orientation is Orientation . Horizontal && u ) || ( orientation is Orientation . Vertical && ! u ) )
171+ return ref x ;
172+ else
173+ return ref y ;
174+ }
113175}
0 commit comments