-
Notifications
You must be signed in to change notification settings - Fork 1
/
WaterfallPanel.cs
96 lines (77 loc) · 4.05 KB
/
WaterfallPanel.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace WaterfallDemo
{
public sealed class WaterfallPanel : Panel
{
public int ColumnNum
{
get { return (int)GetValue(ColumnCountProperty); }
set { SetValue(ColumnCountProperty, value); }
}
// Using a DependencyProperty as the backing store for ColumnCount. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.Register("ColumnNum", typeof(int), typeof(WaterfallPanel), new PropertyMetadata(2));
protected override Size MeasureOverride(Size availableSize)
{
// 记录每个流的长度。因为我们用选取最短的流来添加下一个元素。
KeyValuePair<double, int>[] flowLens = new KeyValuePair<double, int>[ColumnNum];
foreach (int idx in Enumerable.Range(0, ColumnNum))
{
flowLens[idx] = new KeyValuePair<double, int>(0.0, idx);
}
// 我们就用2个纵向流来演示,获取每个流的宽度。
double flowWidth = availableSize.Width / ColumnNum;
// 为子控件提供沿着流方向上,无限大的空间
Size elemMeasureSize = new Size(flowWidth, double.PositiveInfinity);
foreach (UIElement elem in Children)
{
// 让子控件计算它的大小。
elem.Measure(elemMeasureSize);
Size elemSize = elem.DesiredSize;
double elemLen = elemSize.Height;
var pair = flowLens[0];
// 子控件添加到最短的流上,并重新计算最短流。
// 因为我们为了求得流的长度,必须在计算大小这一步时就应用一次布局。但实际的布局还是会在Arrange步骤中完成。
flowLens[0] = new KeyValuePair<double, int>(pair.Key + elemLen, pair.Value);
flowLens = flowLens.OrderBy(p => p.Key).ToArray();
}
return new Size(availableSize.Width, flowLens.Last().Key);
}
protected override Size ArrangeOverride(Size finalSize)
{
// 同样记录流的长度。
KeyValuePair<double, int>[] flowLens = new KeyValuePair<double, int>[ColumnNum];
double flowWidth = finalSize.Width / ColumnNum;
// 要用到流的横坐标了,我们用一个数组来记录(其实最初是想多加些花样,用数组来方便索引横向偏移。不过本例中就只进行简单的乘法了)
double[] xs = new double[ColumnNum];
foreach (int idx in Enumerable.Range(0, ColumnNum))
{
flowLens[idx] = new KeyValuePair<double, int>(0.0, idx);
xs[idx] = idx * flowWidth;
}
foreach (UIElement elem in Children)
{
// 直接获取子控件大小。
Size elemSize = elem.DesiredSize;
double elemLen = elemSize.Height;
var pair = flowLens[0];
double chosenFlowLen = pair.Key;
int chosenFlowIdx = pair.Value;
// 此时,我们需要设定新添加的空间的位置了,其实比measure就多了一个Point信息。接在流中上一个元素的后面。
Point pt = new Point(xs[chosenFlowIdx], chosenFlowLen);
// 调用Arrange进行子控件布局。并让子控件利用上整个流的宽度。
elem.Arrange(new Rect(pt, new Size(flowWidth, elemSize.Height)));
// 重新计算最短流。
flowLens[0] = new KeyValuePair<double, int>(chosenFlowLen + elemLen, chosenFlowIdx);
flowLens = flowLens.OrderBy(p => p.Key).ToArray();
}
// 直接返回该方法的参数。
return finalSize;
}
}
}