- 測定段階: 各要素が必要とするサイズを測定します。
- 配置段階: 測定結果をもとに各要素が実際に配置されます。
public class BrickTile : Panel
{
private int rows;public static readonly DependencyProperty ColumnsProperty
= DependencyProperty.Register("Columns", typeof(int), typeof(BrickTile),
new FrameworkPropertyMetadata(2, FrameworkPropertyMetadataOptions.AffectsMeasure),
o => (int)o >= 2);public int Columns
{
get { return (int)base.GetValue(ColumnsProperty); }
set { base.SetValue(ColumnsProperty, value); }
}// 子要素のレイアウトに必要なサイズを測定します。
protected override Size MeasureOverride(Size availableSize)
{
ComputeRows();
Size childAvailableSize = GetChildSize(availableSize);var children = InternalChildren.Cast<UIElement>().ToArray();
foreach (var child in children)
{
child.Measure(childAvailableSize);
}
double maxWidth = children.Length == 0 ? 0 : children.Max(e => e.DesiredSize.Width);
double maxHeight = children.Length == 0 ? 0 : children.Max(e => e.DesiredSize.Height);return new Size(Columns * maxWidth, rows * maxHeight);
}// 子要素を配置します。
protected override Size ArrangeOverride(Size finalSize)
{
Size childFinalSize = GetChildSize(finalSize);int actualIndex = 0;
foreach (UIElement child in InternalChildren)
{
var position = GetPosition(actualIndex);
Point childPoint = new Point
{
X = (position.Item2 + (position.Item1 % 2 == 0 ? 0 : 0.5)) * childFinalSize.Width,
Y = position.Item1 * childFinalSize.Height,
};
Rect childRect = new Rect(childPoint, childFinalSize);child.Arrange(childRect);
if (child.Visibility != Visibility.Collapsed)
{
actualIndex++;
}
}return finalSize;
}// 行数を算出します。
private void ComputeRows()
{
int actualChildrenCount = InternalChildren.Cast<UIElement>()
.Count(e => e.Visibility != Visibility.Collapsed);if (actualChildrenCount == 0)
{
rows = 0;
}
else
{
var lastChildPosition = GetPosition(actualChildrenCount – 1);
rows = lastChildPosition.Item1 + 1;
}
}// 指定されたインデックスの子要素の (行番号, 列番号) を求めます。
private Tuple<int, int> GetPosition(int index)
{
// 2 行分を 1 グループとして、 (グループ番号, グループ内の位置) を求めます。
int groupChildrenCount = 2 * Columns – 1;
var groupedPosition = Tuple.Create(index / groupChildrenCount, index % groupChildrenCount);return Tuple.Create(2 * groupedPosition.Item1 + groupedPosition.Item2 / Columns,
groupedPosition.Item2 % Columns);
}// 子要素のサイズを求めます。
private Size GetChildSize(Size parentSize)
{
return new Size(parentSize.Width / Columns, parentSize.Height / (rows == 0 ? 1 : rows));
}
}
<Window x:Class="BrickTileTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.saka-pon.net/sample1"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type ListBox}">
<Setter Property="FontSize" Value="16"/>
<Setter Property="ItemsPanel" Value="{DynamicResource ItemsPanelTemplate1}"/>
</Style>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<s:BrickTile IsItemsHost="True" Columns="3"/>
</ItemsPanelTemplate>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="#FFC0F0B0"/>
<Setter Property="BorderBrush" Value="#FF106020"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="ContentTemplate" Value="{DynamicResource DataTemplate1}"/>
</Style>
<DataTemplate x:Key="DataTemplate1">
<TextBlock Text="{Binding Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</Window.Resources>
<ListBox>
<ListBoxItem Content="長崎"/>
<ListBoxItem Content="佐賀"/>
<ListBoxItem Content="福岡"/>
<ListBoxItem Content="熊本"/>
<ListBoxItem Content="大分"/>
<ListBoxItem Content="鹿児島"/>
<ListBoxItem Content="宮崎"/>
</ListBox>
</Window>
コメントを残す