WPF アプリケーションを作る クッキング ガイド

第 6 回 「内側からキレイに! ~ スタイルでコードを見やすくスッキリと~」

XAML にもスタイルとテンプレートという概念があります。用途は HTML でいう CSS と同じようなもので色や形状などをスタイルに登録しておけば、そのスタイルを当てたオブジェクトの色や形状がスタイルのものになるというものです。今回の 記事ではスタイルとテンプレートについて解説します。

スタイルとテンプレートを理解する流れ

まずスタイルについては簡単なスタイルの使い方、スタイルを有効活用したアプリケーションの例といった順番にスタイルを理解していきます。
テンプレートについても同様に簡単なテンプレートの使い方、テンプレートを利用したアプリケーションの例という順番で理解していきましょう。

 

スタイルの定義

スタイルは主に <Window.Resources/> 内に定義します。 <Style/> というノードにスタイルの名前と用途、さらにノード内には <Setter/> というノードでプロパティを指定します。

<Window.Resources>
  <Style x:Key="RedRectangle" TargetType="{x:Type Rectangle}">
    <Setter Property="Width" Value="100"/>
    <Setter Property="Height" Value="80"/>
    <Setter Property="Fill" Value="#FF0000"/>
  </Style>
</Window.Resources>
<Grid>
  <Rectangle Style="{StaticResource RedRectangle}"/>
</Grid>



図 1 スタイルを当てた長方形 (Rectangle)

このサンプルコードではスタイルを幅が 100 、高さが 80 、塗りが赤という長方形 (Rectangle) を定義し、 <Grid/> 内に配置した <Rectangle/> にスタイルを指定しています。この段階ではスタイルはあまり意味がありませんがスタイルを当てるオブジェクトが増えれば増えるほど有効です。

 

スタイルが有効な場面

スタイルは便利なものと書きました。とはいえスタイルはどのように使うと有効なのかについては分からないと思います。
そのためにスタイルを有効的に使ったアプリケーションを作ってみました。

<Window.Resources>
  <Style TargetType="{x:Type Rectangle}" x:Key="OnMouseAnimation">
    <Style.Triggers>
      <EventTrigger RoutedEvent="Mouse.MouseEnter">
        <EventTrigger.Actions>
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimationUsingKeyFrames
                  BeginTime="00:00:00"
                  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)">
                <SplineDoubleKeyFrame 
                   KeySpline="0.1,0.1,0.1,1"
                   Value="3" 
                   KeyTime="00:00:02"
                />
              </DoubleAnimationUsingKeyFrames>
              <DoubleAnimationUsingKeyFrames
                  BeginTime="00:00:00" 
                  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)">
                <SplineDoubleKeyFrame 
                    KeySpline="0.1,0.1,0.1,1" 
                    Value="3" 
                    KeyTime="00:00:02"
                />
              </DoubleAnimationUsingKeyFrames>
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger.Actions>
      </EventTrigger>
      <EventTrigger RoutedEvent="Mouse.MouseLeave">
        <EventTrigger.Actions>
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimationUsingKeyFrames
                  BeginTime="00:00:00" 
                  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)">
                <SplineDoubleKeyFrame 
                    KeySpline="0.1,0.1,0.1,1" 
                    Value="1" 
                    KeyTime="00:00:02"
                />
              </DoubleAnimationUsingKeyFrames>
              <DoubleAnimationUsingKeyFrames 
                  BeginTime="00:00:00" 
                  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)">
                <SplineDoubleKeyFrame 
                    KeySpline="0.1,0.1,0.1,1" 
                Value="1" 
                    KeyTime="00:00:02"
                />
              </DoubleAnimationUsingKeyFrames>
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger.Actions>
      </EventTrigger>
    </Style.Triggers>
    <Setter Property="Width" Value="100"/>
    <Setter Property="Height" Value="100"/>
    <Setter Property="Grid.RenderTransform">
      <Setter.Value>
        <TransformGroup>
          <TranslateTransform X="0" Y="0"/>
          <ScaleTransform ScaleX="1" ScaleY="1"/>
          <SkewTransform AngleX="0" AngleY="0"/>
          <RotateTransform Angle="0"/>
          <TranslateTransform X="0" Y="0"/>
          <TranslateTransform X="0" Y="0"/>
        </TransformGroup>
      </Setter.Value>
    </Setter>
    <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
  </Style>
</Window.Resources>
<Grid>
  <Rectangle Fill="#CCCC0000" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Left" Margin="40,40,0,0" VerticalAlignment="Top"/>
  <Rectangle Fill="#CCCCCC00" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Left" Margin="182,56,0,0" VerticalAlignment="Top"/>
  <Rectangle Fill="#CCCCCCCC" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Left" Margin="288,164,0,0" VerticalAlignment="Top"/>
  <Rectangle Fill="#CC00CCCC" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Right" Margin="0,0,110,36" VerticalAlignment="Bottom"/>
  <Rectangle Fill="#CC0000CC" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Left" Margin="78,0,0,158" VerticalAlignment="Bottom"/>
  <Rectangle Fill="#CCCC00CC" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Left" Margin="216,0,0,52" VerticalAlignment="Bottom"/>
  <Rectangle Fill="#CC000000" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Right" Margin="0,0,68,198" VerticalAlignment="Bottom"/>
  <Rectangle Fill="#CC00CC00" Style="{StaticResource OnMouseAnimation}" HorizontalAlignment="Right" Margin="0,22,176,0" VerticalAlignment="Top"/>
</Grid>

アプリケーションとは言えないのですが、色の付いた長方形にマウスオーバーすると大きくなりマウスが離れると小さくなるというものです。つまりスタイルには色や大きさだけでなくアニメーションなどWPF特有の情報も定義することができるのです。

アニメーションもスタイルで定義したアプリケーションの例

図 2 アニメーションもスタイルで定義したアプリケーションの例

 

テンプレートの定義

テンプレートは外部データと連携するリストボックスなどで使います。言葉では伝わりづらいと思いますので実際にサンプルコードを見ながら解説します。

<Window.Resources>
  <XmlDataProvider x:Key="BookmarkData">
    <x:XData>
      <Favorites >
        <Bookmark>
          <Title>MSN JAPAN</Title>
          <URL>http://jp.msn.com</URL>
        </Bookmark>
        <Bookmark>
          <Title>AZEST</Title>
          <URL>http://www.azest.co.jp</URL>
        </Bookmark>
      </Favorites>
    </x:XData>
  </XmlDataProvider>
  <DataTemplate x:Key="BookmarkTemplate">
    <StackPanel>
      <TextBlock Text="{Binding Mode=OneWay, XPath=Title}"/>
    </StackPanel>
  </DataTemplate>
</Window.Resources>
<Grid>
  <Grid.DataContext>
    <Binding
       Mode="Default"
       Source="{StaticResource BookmarkData}" 
       XPath="/Favorites/Bookmark"
    />
  </Grid.DataContext>
  <ListBox
     ItemsSource="{Binding Mode=Default}" 
     ItemTemplate="{DynamicResource BookmarkTemplate}" 
     IsSynchronizedWithCurrentItem="True"
     HorizontalAlignment="Left" Width="120"
  />
  <Frame Source="{Binding Mode=OneWay, XPath=URL}" Margin="125,0,0,0"/>
</Grid>

テンプレート機能を使った簡易ブラウザ

図 3 テンプレート機能を使った簡易ブラウザ

このコード左カラムにお気に入り、右カラムにページが表示される簡易ブラウザです。
大まかにノードの解説をします。

<Window.Resources>
  <XmlDataProvider x:Key="BookmarkData">
    <x:XData>
      <Favorites >
        <!—表示したいページの名前とURLのXML -->
      </Favorites>
    </x:XData>
 </XmlDataProvider>
  <DataTemplate x:Key="BookmarkTemplate">
    <!-- 左カラムに表示するリストのテンプレート -->
  </DataTemplate>
</Window.Resources>
<Grid>
  <Grid.DataContext>
    <!-- XmlDataProviderで定義したデータをGridに関連付ける -->
  </Grid.DataContext>
  <!-- 左カラムに表示するリスト -->
  <ListBox/>
  <!-- 右カラムに表示するページ -->
  <Frame/>
</Grid>

表示するデータの定義を <XmlDataProvider/> 、表示するデータの呼び出しを <Grid.DataContext/> となっています。そして肝心のテンプレートが <DataTemplate/> で定義されています。つまりテンプレートはリストボックスなどの外部データと連携するコンポーネントの表示デザインを定義できるものです。
このサンプルではXMLデータをXAML内部に抱えていますが実用では表示するデータは外部のXMLファイルだと思います。その為にも外部ファイルを読み込む <XmlDataProvider/> の記述も載せておきます。

<XmlDataProvider
   x:Key="BookmarkData"
   Source="C:\data\BookMarkXML.xml"
/>

これは簡易ブラウザを実現していますが、RSS の XML ファイルを利用しての RSS リーダーも簡単に作ることができます。

 

RSS リーダー

ものは試しで簡単な RSS リーダーを作ってみましょう。技術的にはこれまで解説した情報があればできることです。実際に作ってみたコードが下記のコードです。

<Window.Resources>
  <XmlDataProvider
     x:Key="RSSdata"
     Source="https://www.microsoft.com/japan/msdn/rss.xml"
  />
  <DataTemplate x:Key="RSSreaderTemplate">
    <StackPanel>
      <TextBlock Text="{Binding Mode=OneWay, XPath=title}"/>
    </StackPanel>
  </DataTemplate>
</Window.Resources>
<Grid>
  <Grid.DataContext>
    <Binding
       Mode="Default"
       Source="{StaticResource RSSdata}" 
       XPath="/rss/channel/item" 
    />
  </Grid.DataContext>
  <ListBox 
     ItemsSource="{Binding Mode=Default}" 
     ItemTemplate="{DynamicResource RSSreaderTemplate}" 
     IsSynchronizedWithCurrentItem="True" 
     Margin="0,0,220,0" VerticalAlignment="Top" Height="150" 
  />
  <TextBox 
     Margin="0,0,0,0" 
     Text="{Binding Mode=OneWay, XPath=description}" 
     TextWrapping="Wrap" 
     HorizontalAlignment="Right" 
     Width="215" 
     Height="150" 
     VerticalAlignment="Top" 
  />
  <Frame 
     Source="{Binding Mode=OneWay, XPath=link}" 
     Margin="0,155,0,0" 
  />
</Grid>

テンプレート機能を使っての RSS リーダー

図 4 テンプレート機能を使っての RSS リーダー

下記のコードの部分の Source の値を読みたい RSS フィードの URL に変えるだけで自分だけの RSS リーダーの出来上がりです。

<XmlDataProvider
     x:Key="RSSdata"
     Source="http://rss.site.jp/rss.xml"
  />

あとはこれを利用してデザインをキレイにしたり、C# のコードと連携して複数の RSS フィードを読むことができるようにも作り変えることができます。

 

まとめ

以上で全 6 回のクッキングガイドは終わりです。ここまでで掲載してきた全ての解説、サンプルコードについて理解して頂けたでしょうか?もし、理解していれば WPF クリエイターとしては中級に入っています。
ただ、紹介したサンプルコードは直接は実用性がなく、デザインも非常に貧弱です。今後、 WPF のためのデザインの仕方や、さらに便利な文献やコードなどの情報が世の中に広まっていけば、もっと便利なアプリケーションや楽しいゲーム、格好いいデザイ ンなどを作れる人が増えていくと思います。
みなさん、頭の中で眠っていることを WPF で実現してみましょう。


著者情報

著者情報
坂本 龍介
株式会社アゼスト

株式会社アゼストはリッチインターネットアプリケーションの パイオニアとして、事業価値を高めるコンサルティングやサービスを提供しています。 私はインターネットアプリケーションのシステム構築や新技術を積極的に取り入れ、 使いやすく新しいシステムの開発を担当しております。