UWP、面倒です。

Problem

Hub というのがあります。
よくModernスタイルを紹介するときにグループ毎にタイルを並べて画面全体をスクロールできるやつ。そのグループがHubSection
で、そのHunSectionですが、StackPanelを中に入れてOrientationHorizontalにしても、中の子要素が横に並んでくれません。
HubSection自体のWidthを未指定だろうが、明示的に指定しようが縦になってしまいます。

理由はよくわかりませんでした。
とにかく、仕様として

  1. グループ単位で可変数の子要素を横に並べる
  2. グループ内ではスクロールバーは出さない

を満たす必要がありました。

Resolution

解決策として、Gridを可変の子要素に対応させました。
といってもGridを継承するのではなく、UserControl上いGridを載せて、それを依存関係プロパティから操作する感じですが。

Explanation

ソースは下記です。
https://github.com/takuya-takeuchi/Demo/tree/master/UWP1

まず、可変対応のGrid。

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System.Collections;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236

namespace UWP2
{
public sealed partial class VariableGrid : UserControl
{

#region イベント
#endregion

#region フィールド
#endregion

#region コンストラクタ
public VariableGrid()
{
this.InitializeComponent();
}

#endregion

#region プロパティ

public static readonly DependencyProperty GridLengthProperty = DependencyProperty.Register(
"GridLength", typeof(double), typeof(VariableGrid), new PropertyMetadata(default(double), GridLengthChanged));

public double GridLength
{
get
{
return (double)GetValue(GridLengthProperty);
}
set
{
SetValue(GridLengthProperty, value);
}
}
private static void GridLengthChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var control = dependencyObject as VariableGrid;
control?.Update();
}

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof(object), typeof(VariableGrid), new PropertyMetadata(default(object), ItemsSourceChanged));

public object ItemsSource
{
get
{
return (object)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}

private static void ItemsSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var control = dependencyObject as VariableGrid;
control?.Update();
}

public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof(DataTemplate), typeof(VariableGrid), new PropertyMetadata(default(DataTemplate), ItemTemplateChanged));

public DataTemplate ItemTemplate
{
get
{
return (DataTemplate)GetValue(ItemTemplateProperty);
}
set
{
SetValue(ItemTemplateProperty, value);
}
}

private static void ItemTemplateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var control = dependencyObject as VariableGrid;
control?.Update();
}

public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(VariableGrid), new PropertyMetadata(default(Orientation), OrientationChanged));

public Orientation Orientation
{
get
{
return (Orientation)GetValue(OrientationProperty);
}
set
{
SetValue(OrientationProperty, value);
}
}

private static void OrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var control = dependencyObject as VariableGrid;
control?.Update();
}

#endregion

#region メソッド

#region プライベート

private void Update()
{
var grid = this._Grid;

grid.Children.Clear();
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Clear();

var items = this.ItemsSource as IEnumerable;
if (items == null)
return;

var itemTemplate = this.ItemTemplate;
if (itemTemplate == null)
return;

var rowColumn = 0;
var gridLength = this.GridLength;

var children = new List<FrameworkElement>();
var orientation = this.Orientation;
switch (orientation)
{
case Orientation.Vertical:
foreach (var dataContext in items)
{
var child = itemTemplate.LoadContent() as FrameworkElement;
if (child == null)
continue;

child.DataContext = dataContext;

grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridLength) });
Grid.SetRow(child, rowColumn++);

children.Add(child);
}
break;
case Orientation.Horizontal:
foreach (var dataContext in items)
{
var child = itemTemplate.LoadContent() as FrameworkElement;
if (child == null)
continue;

child.DataContext = dataContext;

grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(gridLength) });
Grid.SetColumn(child, rowColumn++);

children.Add(child);
}
break;
}

children.ForEach(grid.Children.Add);
}

#endregion

#endregion
}
}

GridLengthで一つのセルの幅または高さを指定
OrientationでGridが並ぶ方向を指定
ItemsSourceで並べたいオブジェクトのコレクションを指定
ItemTemplateで並べたオブジェクトの見た目のテンプレートを指定

というだけです。

使い方は下記。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Hub>
<HubSection Header="1st">
<DataTemplate>
<local:VariableGrid GridLength="400"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Images}"
Orientation="Horizontal" />
</DataTemplate>
</HubSection>
<HubSection Header="2nd">
<DataTemplate>
<local:VariableGrid GridLength="400"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Images}"
Orientation="Horizontal" />
</DataTemplate>
</HubSection>
</Hub>

これを使うとこんな感じになります。

Conclusion

まだどういうクラスがあって、適切な使い方が見えてこないので、こんなことしてますが、本当は正しいやり方があるような気がします。
面倒だなぁ

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/UWP1