33// See the LICENSE file in the project root for more information.
44
55using System ;
6+ using System . Collections . Generic ;
7+ using System . Linq ;
68using Windows . Foundation ;
79using Windows . UI . Xaml ;
810using Windows . UI . Xaml . Controls ;
@@ -54,6 +56,24 @@ public double VerticalSpacing
5456 typeof ( WrapPanel ) ,
5557 new PropertyMetadata ( 0d , LayoutPropertyChanged ) ) ;
5658
59+ /// <summary>
60+ /// Gets or sets the vertical alignment to use when the control <see cref="Orientation"/> is set to <see cref="Orientation.Horizontal"/>.
61+ /// </summary>
62+ public VerticalAlignment VerticalContentAlignment
63+ {
64+ get => ( VerticalAlignment ) GetValue ( VerticalContentAlignmentProperty ) ;
65+ set => SetValue ( VerticalContentAlignmentProperty , value ) ;
66+ }
67+
68+ /// <summary>
69+ /// The DP to store the <see cref="VerticalContentAlignment"/> property value.
70+ /// </summary>
71+ public static readonly DependencyProperty VerticalContentAlignmentProperty = DependencyProperty . Register (
72+ nameof ( VerticalContentAlignment ) ,
73+ typeof ( VerticalAlignment ) ,
74+ typeof ( WrapPanel ) ,
75+ new PropertyMetadata ( VerticalAlignment . Top ) ) ;
76+
5777 /// <summary>
5878 /// Gets or sets the orientation of the WrapPanel.
5979 /// Horizontal means that child controls will be added horizontally until the width of the panel is reached, then a new row is added to add new child controls.
@@ -141,7 +161,7 @@ protected override Size MeasureOverride(Size availableSize)
141161 foreach ( var child in Children )
142162 {
143163 child . Measure ( availableSize ) ;
144- var currentMeasure = new UvMeasure ( Orientation , child . DesiredSize . Width , child . DesiredSize . Height ) ;
164+ var currentMeasure = new UvMeasure ( Orientation , child . DesiredSize ) ;
145165 if ( currentMeasure . U == 0 )
146166 {
147167 continue ; // ignore collapsed items
@@ -207,10 +227,14 @@ protected override Size ArrangeOverride(Size finalSize)
207227 var paddingEnd = new UvMeasure ( Orientation , Padding . Right , Padding . Bottom ) ;
208228 var position = new UvMeasure ( Orientation , Padding . Left , Padding . Top ) ;
209229
210- double currentV = 0 ;
211- void Arrange ( UIElement child , bool isLast = false )
230+ var allMeasures = Children . Select ( c => new UvMeasure ( Orientation , c . DesiredSize ) ) . ToList ( ) ;
231+ var allData = new List < ( UvMeasure position , UvMeasure size ) > ( ) ;
232+
233+ var currentV = 0.0 ;
234+ void Arrange ( int childIndex , bool isLast = false )
212235 {
213- var desiredMeasure = new UvMeasure ( Orientation , child . DesiredSize . Width , child . DesiredSize . Height ) ;
236+ var child = Children [ childIndex ] ;
237+ var desiredMeasure = allMeasures [ childIndex ] ;
214238 if ( desiredMeasure . U == 0 )
215239 {
216240 return ; // if an item is collapsed, avoid adding the spacing
@@ -230,15 +254,7 @@ void Arrange(UIElement child, bool isLast = false)
230254 desiredMeasure . U = parentMeasure . U - position . U ;
231255 }
232256
233- // place the item
234- if ( Orientation == Orientation . Horizontal )
235- {
236- child . Arrange ( new Rect ( position . U , position . V , desiredMeasure . U , desiredMeasure . V ) ) ;
237- }
238- else
239- {
240- child . Arrange ( new Rect ( position . V , position . U , desiredMeasure . V , desiredMeasure . U ) ) ;
241- }
257+ allData . Add ( ( position , desiredMeasure ) ) ;
242258
243259 // adjust the location for the next items
244260 position . U += desiredMeasure . U + spacingMeasure . U ;
@@ -248,10 +264,61 @@ void Arrange(UIElement child, bool isLast = false)
248264 var lastIndex = Children . Count - 1 ;
249265 for ( var i = 0 ; i < lastIndex ; i ++ )
250266 {
251- Arrange ( Children [ i ] ) ;
267+ Arrange ( i ) ;
252268 }
253269
254- Arrange ( Children [ lastIndex ] , StretchChild == StretchChild . Last ) ;
270+ Arrange ( lastIndex , StretchChild == StretchChild . Last ) ;
271+
272+ // Now that we have all the data, we do the actual arrange pass
273+ var lastArrangeV = - 1.0 ;
274+ var maxRowHeight = 0.0 ;
275+ for ( var i = 0 ; i < Children . Count ; i ++ )
276+ {
277+ // place the item
278+ var child = Children [ i ] ;
279+ ( UvMeasure arrangePosition , UvMeasure arrangeMeasure ) = allData [ i ] ;
280+
281+ if ( lastArrangeV != arrangePosition . V )
282+ {
283+ // We are on a new row, we scan all the items to get the max row height
284+ maxRowHeight = allData
285+ . Skip ( i ) // ignore what has already being processed
286+ . TakeWhile ( d => d . position . V == arrangePosition . V ) // we are still on the same row
287+ . Max ( d => d . size . V ) ; // We want the max.
288+ }
289+
290+ if ( Orientation == Orientation . Horizontal )
291+ {
292+ switch ( VerticalContentAlignment )
293+ {
294+ case VerticalAlignment . Center :
295+ {
296+ var vOffset = Math . Max ( ( maxRowHeight - arrangeMeasure . V ) / 2.0 , 0.0 ) ;
297+ child . Arrange ( new Rect ( arrangePosition . U , arrangePosition . V + vOffset , arrangeMeasure . U , arrangeMeasure . V ) ) ;
298+ break ;
299+ }
300+
301+ case VerticalAlignment . Bottom :
302+ {
303+ var vOffset = Math . Max ( maxRowHeight - arrangeMeasure . V , 0.0 ) ;
304+ child . Arrange ( new Rect ( arrangePosition . U , arrangePosition . V + vOffset , arrangeMeasure . U , arrangeMeasure . V ) ) ;
305+ break ;
306+ }
307+
308+ case VerticalAlignment . Stretch :
309+ child . Arrange ( new Rect ( arrangePosition . U , arrangePosition . V , arrangeMeasure . U , maxRowHeight ) ) ;
310+ break ;
311+ case VerticalAlignment . Top :
312+ default :
313+ child . Arrange ( new Rect ( arrangePosition . U , arrangePosition . V , arrangeMeasure . U , arrangeMeasure . V ) ) ;
314+ break ;
315+ }
316+ }
317+ else
318+ {
319+ child . Arrange ( new Rect ( arrangePosition . V , arrangePosition . U , arrangeMeasure . V , arrangeMeasure . U ) ) ;
320+ }
321+ }
255322 }
256323
257324 return finalSize ;
0 commit comments