LoginSignup
15
21

More than 5 years have passed since last update.

[UWP]同じようなXAMLを書きたくない。テンプレートコントロールを使う

Posted at

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にテンプレートが追加される。このふたつがペアで機能する。

HeaderControl.cs

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として、子は、どのプロパティになるかを指定する。

Generic.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

15
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
21