UWPの基本パーツは、プリミティブなものばかりで、それらを組み合わせていかないといけない。
一回だけならまだしも、何回も繰り返すと、流石に嫌になる。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="Header"></TextBlock>
<TextBlock Grid.Row="1" Text="Content"></TextBlock>
</Grid>
上の例では、ContentにHeaderを付けたいだけだが、XAMLとしては、6行を消費している。レイアウト情報が多いため、記述は冗長であり、意味的なものを把握しにくいものになっている。何度も書くものではない。
このようなときには、「テンプレートコントロール」を使う。
「テンプレートコントロール」は、csファイルと Generic.xamlにテンプレートが追加される。このふたつがペアで機能する。
namespace MyUwpLib.Controls
{
[ContentProperty(Name = "Content")]
public sealed class HeaderControl : Control
{
public HeaderControl()
{
this.DefaultStyleKey = typeof(HeaderControl);
}
public FrameworkElement Header
{
get { return (FrameworkElement)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(FrameworkElement), typeof(HeaderControl), new PropertyMetadata(null));
public FrameworkElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(FrameworkElement), typeof(HeaderControl), new PropertyMetadata(null));
}
}
ポイントは、DependencyPropertyでプロパティを作る。
[ContentProperty(Name = "Content")] でXAMLとして、子は、どのプロパティになるかを指定する。
<Style TargetType="local2:HeaderControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:HeaderControl">
<Grid Background="{TemplateBinding Background}" >
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding Header}"></ContentPresenter>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"></ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Bindingは、「TemplateBinding」を使う。Contentの表示には、「ContentPresenter」を使う。
このようなもの作ることで、使うときは次のように使う。
<myControls:HeaderControl>
<myControls:HeaderControl.Header>
<TextBlock Text="Header"></TextBlock>
</myControls:HeaderControl.Header>
<TextBlock Text="Content"></TextBlock>
</myControls:HeaderControl>
これだけの記述で、初めのXAMLと同じことが実現できる。レイアウトのために使う行数は、4行と短くなっており、Header部分、とContent部分がわかりやすいものとなり、捗る。今のところ、<myControls:HeaderControl.Header>とやっているところが冗長なので、Header部分をテキストだと割り切る形で作れば、2行で済む。
テンプレートコントロールを使い、共通部品化することで、変更や機能追加もやりやすくなる。テンプレートコントロールを使った事例には、アニメーションが多い。
以上は、シンプルな例で、もうちょっと複雑なことは、このソースコードが参考になる。
http://deanchalk.com/a-xaml-uwp-custom-control-the-expander/
https://github.com/deanchalk/ExpanderUWP