Windows 8 アプリ入門3 【カスタムコントロールをつかう:前編】



いよいよWindows 8発売まで1ヶ月をきりました。
今回は、XAML/C#でのWindowsストアアプリ開発におけるカスタムコントロールの使用例をご紹介したいと思います。

シンプルに見えてオリジナリティを出そうとすると意外とはまるAppBarボタンを例に解説します。Segoe UI Symbolにある字体が基本となっているAppBarボタンですが、すべてそれらのデザインで賄えるということも少ないのではないでしょうか。
画像を使用したい場合に、ContentTemplateに画像を入れればOKというわけにはいきません。画像の場合、ポイントオーバー時やタップのデザインも色が正しく反転するよう設定する必要があります。1つ1つスタイルのビジュアルステートで設定するのは現実的ではありません。

上図は左端の「徒歩」をタップした状態、右端の「ヘリコプター」を無効化した状態です。アイコン画像はロイヤリティフリーで利用可能な「Syncfusion Metro Studio」を使って作成したものです。

カスタムコントロールでAppBarボタンをつくる

汎用的に使えるAppBarボタンスタイルはStandardStyle.xamlに定義されています。使用する場合は、対象のスタイルのコメントアウト(RTM版からコメントアウトされるようになりました)を外して以下のように使用することができます。

<Button Style=”{StaticResource スタイル名}” />
  • ラベルの変更は、AutomationProperties.Nameプロパティで設定します
  • アイコンデザインの変更は、「Segoe UI Symbol」に存在するテキスト等を使用する場合には、Contentプロパティに「&#x文字コード;」と指定することで簡単に設定できます(文字コード表で調べることが可能)

[左へ]25C0 [右へ]25B6

しかし、テキストによっては中央からズレた位置に表示されてしまうテキストも存在しますため注意が必要です。また、丸枠のGlyphもSegoe UI Symbolのテキストが使用されているため、枠とアイコンデザインの位置を調節することは難しいです。

  • 枠とアイコンデザインを確実に中央揃えにしたい場合
  • アイコンデザインに画像を用いたい場合

上記の場合に、カスタムコントロールを使用するとよいかと思います。例外として、アイコンデザインに画像を使用する箇所が少ない場合には、個別にスタイルを用意してもよいかと思います。

■ カスタムコントロールを定義する

Buttonを継承したカスタムコントロール「CustomAppBarButton」をつくります。
通常時の画像と、プレス時の画像を設定するプロパティを設けます。画像の場合は、型をImageSourceにします。
※PressImageは型はUriでも構いません。

public class CustomAppBarButton : Button
{
   public CustomAppBarButton()
   {
      this.DefaultStyleKey = typeof(CustomAppBarButton);
   }
   //通常時画像
   public ImageSource Image
   {
      get { return (ImageSource)GetValue(ImageProperty); }
      set { SetValue(ImageProperty, value); }
   }

   
  public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
            ”Image”, typeof(ImageSource),
            typeof(CustomAppBarButton), new PropertyMetadata(null));
    

   //プレス時画像
   public ImageSource PressImage
   {
      get { return (ImageSource)GetValue(PressImageProperty); }
      set { SetValue(PressImageProperty, value); }
   }

  
   public static readonly DependencyProperty PressImageProperty =
            DependencyProperty.Register(
                ”PressImage”, typeof(ImageSource),
                 typeof(CustomAppBarButton), new PropertyMetadata(null));
   }

■ カスタムコントロールのスタイルを定義する

Themes/Generic.xamlでスタイルを設定します。下の例では、丸枠付きの画像を用意することを前提としています。
Blendを使ってAppBarボタンのデフォルトのスタイルを編集すると簡単に作成できます。
TemplateBidingを使ってImageとTitleを受け取っています。

<Style TargetType=”local:CustomAppBarButton”>
  <Setter Property=”Template”>
    <Setter.Value>
      <ControlTemplate TargetType=”local:CustomAppBarButton”>
        <Grid x:Name=”RootGrid” Width=”100″ Background=”Transparent”>
          <StackPanel VerticalAlignment=”Top” Margin=”0,12,0,11″>
            <Grid Width=”40″ Height=”40″ Margin=”0,0,0,5″ HorizontalAlignment=”Center”>
              <Image x:Name=”ImageContent” Width=”40″ Height=”40″ VerticalAlignment=”Center” HorizontalAlignment=”Center” Source=”{TemplateBinding Image}” Stretch=”UniformToFill” Opacity=”1″/>
            </Grid>
            <TextBlock x:Name=”TextLabel” Text=”{TemplateBinding Content}” Foreground=”{StaticResource AppBarItemForegroundThemeBrush}” Margin=”0,0,2,0″ FontSize=”12″ TextAlignment=”Center” Width=”88″ MaxHeight=”32″ TextTrimming=”WordEllipsis” Style=”{StaticResource BasicTextStyle}”/>
          </StackPanel>
          //ここにビジュアルステートを書く
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

ビジュアルステートですが、ApplicationViewStatesについてはデフォルトのAppBarボタンのスタイルと同様です。
PointerOver、Pressed、Disabledの部分だけ抜粋します。

【PointerOver】ポインターオーバー時に透明度を変化させる

<VisualState x:Name=”PointerOver”>
  <Storyboard>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName=”ImageContent” Storyboard.TargetProperty=”Opacity”>
      <DiscreteObjectKeyFrame KeyTime=”0″ Value=”0.5″/>
    </ObjectAnimationUsingKeyFrames>
  </Storyboard>
</VisualState>

【Pressed】ポインタープレス時にImageContentのSourceをPressImageに変更する


TemplateBidingは使用できません。BidingでImageSource(またはUri)のプロパティPressImageを設定し、以下のように記述します。

<VisualState x:Name=”Pressed”>
  <Storyboard>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName=”ImageContent” Storyboard.TargetProperty=”Source”>
      <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{Binding PressImage, RelativeSource={RelativeSource TemplatedParent}}“/>
    </ObjectAnimationUsingKeyFrames>
  </Storyboard>
</VisualState>

【Disabled】無効化されたときに透明度を変化させる


ラベルも透過させるときは以下に加えます。

<VisualState x:Name=”Disabled”>
  <Storyboard>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName=”ImageContent” Storyboard.TargetProperty=”Opacity”>
      <DiscreteObjectKeyFrame KeyTime=”0″ Value=”0.3″/>
    </ObjectAnimationUsingKeyFrames>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName=”TextLabel” Storyboard.TargetProperty=”Opacity”>
      <DiscreteObjectKeyFrame KeyTime=”0″ Value=”0.3″/>
    </ObjectAnimationUsingKeyFrames>
  </Storyboard>
</VisualState>

■ 汎用的に使えるスタイルを定義する

アイコンデザインごとに統一させるためにカスタムコントロールの各デザインごとのスタイルを定義します。

<Style x:Key=”ViewGridCustomAppBarButtonStyle” TargetType=”local:CustomAppBarButton”>
  <Setter Property=”Image” Value=”ms-appx:///Assets/Icon/view_normal.png”/>
  <Setter Property=”PressImage” Value=”ms-appx:///Assets/Icon/view_pressed.png”/>
  <Setter Property=”Title” Value=”ラベル名”/>
</Style>

■ カスタムコントロールを使用する

通常のAppBarボタンのようにスタイルを指定することで使用できます。

<local:CustomAppBarButton Style=”{StaticResource ViewGridCustomAppBarButtonStyle}”/>

まとめ

AppBarボタンを表示するためだけにカスタムコントロールというのは面倒な気もしますが、一度スタイルを作成してしまえば使いまわせることを考えると重宝するのではと思っています。そもそもAppBarボタンに画像を使うケースを想定して設定できるようにしておいて欲しいなと思います。。。