Skip to content

Commit abc7626

Browse files
authored
Fix bugs in StaggeredPanel (#12)
- Apply HorizontalAlignment of children - Prevent IndexOutOfRangeExceptions - Fix overlapping if the element takes all the available columns - Fix finding the column index with bigger height for wide child
1 parent 1a5fbe5 commit abc7626

File tree

1 file changed

+52
-39
lines changed

1 file changed

+52
-39
lines changed

src/OpenSilver.ControlsKit.Controls/StaggeredPanel.cs

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818

1919
using System;
2020
using System.Linq;
21-
using System.Windows.Controls;
2221
using System.Windows;
22+
using System.Windows.Controls;
2323
//using Windows.Foundation;
2424
//using Windows.UI.Xaml;
2525
//using Windows.UI.Xaml.Controls;
@@ -38,7 +38,6 @@ public class StaggeredPanel : Panel
3838
/// </summary>
3939
public StaggeredPanel()
4040
{
41-
//ProgressiveRenderingChunkSize = 1;
4241
//RegisterPropertyChangedCallback(Panel.HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
4342
}
4443

@@ -133,7 +132,7 @@ protected override Size MeasureOverride(Size availableSize)
133132
int numColumns = Math.Max(1, availableWidth == double.PositiveInfinity ? -1 : (int)Math.Floor(availableWidth / _columnWidth));
134133

135134
// adjust for column spacing on all columns expect the first
136-
double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing));
135+
double totalWidth = GetTotalWidth(numColumns);
137136
if (totalWidth > availableWidth)
138137
{
139138
numColumns--;
@@ -163,17 +162,14 @@ protected override Size MeasureOverride(Size availableSize)
163162
child.Measure(new Size(availableWidth, availableHeight));
164163
var elementSize = child.DesiredSize;
165164

166-
double newHeight;
167-
int heightDefiningColumnIndex;
168-
var columnIndex = GetPlacementInformations(columnHeights, elementSize, out newHeight, out heightDefiningColumnIndex);
169165
int elementColumnSpan = GetElementColumnSpan(elementSize.Width);
166+
var columnIndex = CalculatePlacement(columnHeights, elementSize, elementColumnSpan, out double newHeight, out _);
170167

171-
for (int k = 0; k < elementColumnSpan; ++k)
168+
for (int k = 0; k < elementColumnSpan && columnIndex + k < numColumns; ++k)
172169
{
173170
columnHeights[columnIndex + k] = newHeight + (itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0);
174171
itemsPerColumn[columnIndex + k]++;
175172
}
176-
itemsPerColumn[columnIndex]++;
177173
}
178174

179175
double desiredHeight = columnHeights.Max();
@@ -189,7 +185,7 @@ protected override Size ArrangeOverride(Size finalSize)
189185
int numColumns = Math.Max(1, (int)Math.Floor(finalSize.Width / _columnWidth));
190186

191187
// adjust for horizontal spacing on all columns expect the first
192-
double totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing));
188+
double totalWidth = GetTotalWidth(numColumns);
193189
#region Explanation for the -0.01 in the if below:
194190
// we compare to totalWidth -0.01 because such a small overflow wouldn't be visible and it is still a likely scenario:
195191
// If we are Stretched, MeasureOverride will have changed _columnWidth to availableWidth/numColumns, which can be rounded higher than the actual value.
@@ -201,7 +197,7 @@ protected override Size ArrangeOverride(Size finalSize)
201197
numColumns--;
202198

203199
// Need to recalculate the totalWidth for a correct horizontal offset
204-
totalWidth = _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing));
200+
totalWidth = GetTotalWidth(numColumns);
205201
}
206202

207203
if (HorizontalAlignment == HorizontalAlignment.Right)
@@ -218,27 +214,31 @@ protected override Size ArrangeOverride(Size finalSize)
218214

219215
for (int i = 0; i < Children.Count; i++)
220216
{
221-
222-
var child = Children[i];
217+
var child = Children[i] as FrameworkElement;
223218
var elementSize = child.DesiredSize;
224219
double elementHeight = elementSize.Height;
225220

226221
//get the element's column span:
227222
double elementWidth = elementSize.Width;
228-
int elementColumnsSpan = Math.Max(1, (int)Math.Ceiling((elementWidth - _columnWidth) / (_columnWidth + ColumnSpacing)) + 1);
229-
230-
int columnIndex, heightDefiningColumnIndex;
231-
double newHeight;
232-
233-
columnIndex = GetPlacementInformations(columnHeights, elementSize, out newHeight, out heightDefiningColumnIndex);
223+
int elementColumnSpan = GetElementColumnSpan(elementWidth);
224+
int columnIndex = CalculatePlacement(columnHeights, elementSize, elementColumnSpan, out double newHeight, out int heightDefiningColumnIndex);
234225

235226
double itemHorizontalOffset = horizontalOffset + (_columnWidth * columnIndex) + (ColumnSpacing * columnIndex);
236227
double itemVerticalOffset = columnHeights[heightDefiningColumnIndex] + verticalOffset + (RowSpacing * itemsPerColumn[heightDefiningColumnIndex]);
237228

238-
Rect bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, elementWidth, elementHeight);
229+
if (child.HorizontalAlignment == HorizontalAlignment.Right)
230+
{
231+
itemHorizontalOffset += GetTotalWidth(elementColumnSpan) - elementWidth;
232+
}
233+
else if (child.HorizontalAlignment == HorizontalAlignment.Center)
234+
{
235+
itemHorizontalOffset += (GetTotalWidth(elementColumnSpan) - elementWidth) / 2;
236+
}
237+
238+
var bounds = new Rect(itemHorizontalOffset, itemVerticalOffset, elementWidth, elementHeight);
239239
child.Arrange(bounds);
240240

241-
for (int k = 0; k < elementColumnsSpan; ++k)
241+
for (int k = 0; k < elementColumnSpan && columnIndex + k < numColumns; ++k)
242242
{
243243
columnHeights[columnIndex + k] = newHeight;
244244
itemsPerColumn[columnIndex + k]++;
@@ -248,6 +248,11 @@ protected override Size ArrangeOverride(Size finalSize)
248248
return base.ArrangeOverride(finalSize);
249249
}
250250

251+
private double GetTotalWidth(int numColumns)
252+
{
253+
return _columnWidth + ((numColumns - 1) * (_columnWidth + ColumnSpacing));
254+
}
255+
251256
private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
252257
{
253258
var panel = (StaggeredPanel)d;
@@ -270,15 +275,11 @@ private int GetElementColumnSpan(double elementWidth)
270275
return Math.Max(1, (int)Math.Ceiling((elementWidth - _columnWidth) / (_columnWidth + ColumnSpacing)) + 1);
271276
}
272277

273-
private int GetPlacementInformations(double[] columnHeights, Size elementSize, out double newHeight, out int heightDefiningColumnIndex)
278+
private int CalculatePlacement(double[] columnHeights, Size elementSize, int elementColumnSpan, out double newHeight, out int heightDefiningColumnIndex)
274279
{
275280
double elementHeight = elementSize.Height;
276281

277-
//get the element's column span:
278-
double elementWidth = elementSize.Width;
279-
int elementColumnSpan = GetElementColumnSpan(elementWidth);
280-
281-
int columnIndex = 0;
282+
int columnIndex;
282283
if (elementColumnSpan == 1)
283284
{
284285
columnIndex = GetColumnIndex(columnHeights);
@@ -313,16 +314,24 @@ private int GetColumnIndex(double[] columnHeights, int elementColumnSpan, out do
313314
{
314315
if (columnHeights.Length <= elementColumnSpan)
315316
{
316-
// there is no option on where to put the element anyway so let's just return 0.
317317
bestHeight = columnHeights[0];
318318
heightDefiningColumnIndex = 0;
319+
for (int i = 1; i < columnHeights.Length; i++)
320+
{
321+
if (columnHeights[i] > bestHeight)
322+
{
323+
heightDefiningColumnIndex = i;
324+
bestHeight = columnHeights[i];
325+
}
326+
}
319327
return 0;
320328
}
321329

322-
//initialization:
323330
int bestIndex = 0;
324331
bestHeight = double.MinValue;
325332
heightDefiningColumnIndex = 0;
333+
334+
// Initialize bestHeight using the first valid span
326335
for (int i = 0; i < elementColumnSpan; ++i)
327336
{
328337
if (bestHeight < columnHeights[i])
@@ -331,38 +340,42 @@ private int GetColumnIndex(double[] columnHeights, int elementColumnSpan, out do
331340
heightDefiningColumnIndex = i;
332341
}
333342
}
334-
int lastColumnIndex = columnHeights.Length - elementColumnSpan + 1;
343+
344+
int maxStartIndex = columnHeights.Length - elementColumnSpan;
335345

336346
//we look for the set of columns that will allow us to put the element with the smallest vertical offset:
337-
for (int j = 1; j < lastColumnIndex; j++)
347+
for (int startIndex = 1; startIndex <= maxStartIndex; ++startIndex)
338348
{
339349
//We get the height of the new column to consider:
340-
double newHeight = columnHeights[j + elementColumnSpan - 1];
341-
if (newHeight > bestHeight)
350+
double newColumnHeight = columnHeights[startIndex + elementColumnSpan - 1];
351+
352+
if (newColumnHeight > bestHeight)
342353
{
343354
//we can exclude any set of columns that include the new column since at best, it won't be as good as what we have already found:
344-
j += elementColumnSpan - 1; // -1 because the loop will also add 1.
355+
startIndex += elementColumnSpan - 1; // -1 because the loop will also add 1.
345356
continue;
346357
}
347358

348359
//Calculate the height of the current set of columns:
349360
//Note: (perf) we could also read the height on j-1 and if that column's height is < bestHeight, it means that column was not the limiting one in the previous loop so no need to recalculate.
350361
double currentHeight = double.MinValue;
351-
for (int i = 0; i < elementColumnSpan; ++i)
362+
int currentIndex = 0;
363+
for (int offset = 0; offset < elementColumnSpan; ++offset)
352364
{
353-
if (currentHeight < columnHeights[j + i])
365+
int index = startIndex + offset;
366+
if (currentHeight < columnHeights[index])
354367
{
355-
currentHeight = columnHeights[j + i];
356-
heightDefiningColumnIndex = j + i;
368+
currentHeight = columnHeights[index];
369+
currentIndex = index;
357370
}
358371
}
359372

360373
if (currentHeight < bestHeight)
361374
{
362375
bestHeight = currentHeight;
363-
bestIndex = j;
376+
heightDefiningColumnIndex = currentIndex;
377+
bestIndex = startIndex;
364378
}
365-
366379
}
367380

368381
return bestIndex;

0 commit comments

Comments
 (0)