印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
 Wicked Code: Silverlight 2 のカスタム コン...
Related Articles

Jeremy Miller が引き続き、永続化パターンの解説の一環として、Unit of Work 設計パターンを検討し、永続性の無視にまつわる問題について調べます。

Jeremy Miller

MSDN Magazine June 2009

...

Read more!

ドキュメントやマルチメディア アイテムのような大きい BLOB をデータベースとファイル システムのどちらに格納すればよいかは、常に意見の分かれるところです。SQL Server 2008 ではどちらかを選択する必要はありません。FILESTREAM 記憶域は両方の方法の長所を備えています。

Bob Beauchemin

MSDN Magazine May 2009

...

Read more!

メモリ使用量はアプリケーションの実行速度に直接影響を及ぼす可能性があるため、最適化する必要があります。この記事では、.NET プログラムを対象に、メモリの最適化の基本について説明します。

Subramanian Ramaswamy および Vance Morrison

MSDN Magazine June 2009

...

Read more!

Microsoft の製品チームに課せられたセキュリティ開発ライフサイクル (SDL) に関する主な要件の一部についての開発者とセキュリティ専門家の会話を紹介します。

Michael Howard

MSDN Magazine May 2009

...

Read more!

XNA Game Studio 3.0 を使用して Zune 用のゲームを作成するための基本事項について Mike Calligaro が説明します。

Mike Calligaro

MSDN Magazine May 2009

...

Read more!

Also by this Author

ASP.NET has become a bit of a gold standard for Web programming. The upcoming version, ASP.NET 2.0 will have even more of the kinds of features that have made it the popular framework it is today. This article takes a broad look at those features, including what's new in data source controls, themes and skins, the DataGrid and its new functionality, managing roles, and other administrative tasks.

Jeff Prosise

MSDN Magazine June 2004

...

Read more!

Now that ASP.NET 2.0 is a shipping product, it seems appropriate to revisit an issue that tops the new features wish lists of many developers: a SQL Server™ site map provider.

Jeff Prosise

MSDN Magazine February 2006

...

Read more!

Web プラットフォームとして、Silverlight は処理が高速である必要があります。ユーザーを待たせないために、これらのパフォーマンスのヒントに留意してください。

Jeff Prosise

MSDN Magazine March 2009

...

Read more!

By now, developers everywhere have had the opportunity to download the first beta of the Microsoft® . NET Framework 2. 0. ASP. NET developers who have played with it are no doubt salivating at all the cool new features.

Jeff Prosise

MSDN Magazine February 2005

...

Read more!

Silverlight は、非常にわずかなコード (そのほとんどが XAML) で Web サイトのイメージ拡大機能を簡単に構築できる強力なツールです。詳しくはこちらから

Jeff Prosise

MSDN Magazine November 2008

...

Read more!

Popular Articles

ワンタイム パスワードは、ディクショナリ攻撃、フィッシング、傍受などのさまざまなセキュリティ違反に対するソリューションを提供します。ここでは、そのしくみを説明します。

Dan Griffin

MSDN Magazine May 2008

...

Read more!

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

サイドバー ガジェットは小さいけれど強力なツールで、驚くほど簡単に作成できます。ここでは、Donavon West がその楽しさを教えてくれます。

Donavon West

MSDN Magazine August 2007

...

Read more!

Writing a Web application with ASP.NET is unbelievably easy. So many developers don't take the time to structure their applications for great performance. In this article, the author presents 10 tips for writing high-performance Web apps. The discussion is not limited to ASP.NET applications because they are just one subset of Web applications.

Rob Howard

MSDN Magazine January 2005

...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

Wicked Code
Silverlight 2 のカスタム コントロールを作成する
Jeff Prosise

コードのダウンロード : WickedCode2008_08.exe (585 KB)
オンラインでのコードの参照
この記事では、次の内容について説明します。
  • WPF コントロール モデル
  • コントロール テンプレートを作成する
  • コントロールを派生させる
  • イベントを追加する
この記事では、次のテクノロジを使用しています。
Silverlight 2
この記事は、Silverlight 2 の Beta 2 バージョンに基づいています。ここに記載されているすべての情報は、変更される場合があります。
Silverlight™ 2 と Silverlight 1.0 の多くの機能の相違点の 1 つに、コントロールのサポートがあります。Silverlight 2 は、堅牢かつ機能豊富なコントロール モデルを特徴としています。このコントロール モデルはプラットフォームに含まれるコントロールやサードパーティ製のコントロール パッケージの基礎になります。このコントロール モデルを使用して、独自のコントロールを構築することもできます。ただし、Windows® Presentation Foundation (WPF) コントロール モデルに馴染みのない開発者には、初めて Silverlight カスタム コントロールを構築する場合に困難と感じることがあります。本記事の執筆時点 (Silverlight 2 Beta 2 のリリース直前) では、利用可能なドキュメントはほとんどなく、簡単な Web 検索ではその方法を示すチュートリアルはほとんど見つかりません。この記事では Beta 2 を使用していますが、最終的なリリースまでにさらに変更の可能性があることにご注意ください。
新しいプラットフォーム用にカスタム コントロールの記述を学習するとき、筆者はしばしばボタン、リストボックスなどの組み込みのコントロールの複製をいくつか試みることから始めます。これらのコントロールはシンプルな外見ですが、常にコントロール モデルの主要な機能を明らかにし、その知識を開発者に問いかけます。そのうえ、最初に単純なプッシュ ボタンを作成できないと、非常に優れたマルチカラー、マルチスレッド、オールインワン、万能ウィジェット コントロールを作成することはできません。
Silverlight 2 のカスタム コントロールを習得する最良の方法は、着実に、1 つずつ、カスタム コントロールを作成することです。コントロールを構成するパーツだけではなく、パーツどうしがどのように組み合わされているかを学びます。以下のチュートリアルでは、組み込みの Button コントロールの外観と動作の重要な側面を再現するために SimpleButton コントロールをどのように作成するかを説明し、Silverlight スタイルのコントロール開発の概要を紹介します。

手順 1: 新規の Silverlight プロジェクトを作成する
カスタム コントロールを作成する第 1 の手順として、Visual Studio® 2008 を起動して (Silverlight プロジェクトを作成できるように、Visual Studio 用の Silverlight アドインがインストールされていることをご確認ください)、プロジェクトを作成します。通常は、コントロールが独自のアセンブリにコンパイルされ、それを使用するプロジェクトに参照として追加できるように、Silverlight クラス ライブラリ プロジェクトを作成します。ここでは多少異なる方法で、コントロールが同じプロジェクト内でビルドされ使用できるように、Silverlight アプリケーション プロジェクトを作成します。そこで、図 1 に示すように SimpleButtonDemo という新しい Silverlight アプリケーション プロジェクトを作成します。Visual Studio で、Silverlight プロジェクトを実行する Web プロジェクトを作成するかについて確認された場合は、[はい] をクリックします。
図 1 SimpleButtonDemo プロジェクトを作成する (クリックすると拡大画像が表示されます)

手順 2: Control (または ContentControl) から派生する
次の手順では、コントロールを表す C# クラスを追加します。基本的なコントロール機能を継承するためには、このコントロール クラスは最低限、Silverlight System.Windows.Controls.Control クラスから派生している必要があります。ただし、ContentControl や ItemsControl のような Control の派生クラスから派生させることもできます。組み込みのコントロールの多くは、直接または間接的に ContentControl から派生しています。ContentControl は、たとえば、プッシュ ボタンの表示コンテンツなど、コントロールのコンテンツをカスタマイズ可能にする Content プロパティを追加します。これに対し、ListBox コントロールは ItemsControl から派生しています。ItemsControl は、項目のコレクションをユーザーに提供するコントロールの基本的な動作を実装します。ここではボタンを実装するので、ContentControl から派生させます。
Visual Studio の [新しい項目の追加] を使用して、SimpleButtonDemo プロジェクトに新しい C# クラスを追加します。ファイル名を SimpleButton.cs とします。続いて SimpleButton.cs を開き、SimpleButton クラスを ContentControl から派生するように変更します。
namespace SimpleButtonDemo
{
    public class SimpleButton : ContentControl
    {
    }
}
この時点で、XAML ドキュメントの宣言でインスタンス化できる必要最低限のカスタム コントロールを実装できました。デモンストレーションを行うには、Page.xaml に次のステートメントを追加します。
<custom:SimpleButton />
この宣言が Silverlight にとって意味をなすようにするには、Page.xaml のルートの UserControl 要素に次の属性を追加する必要があります。
xmlns:custom="clr-namespace:SimpleButtonDemo; assembly=SimpleButtonDemo"
ご覧のように、clr-namespace は SimpleButton クラスが定義される名前空間を示し、assembly はそのコントロールを含むアセンブリを示しています。この例では、コントロール アセンブリとアプリケーション アセンブリは同一です。SimpleButton を MyControls.dll という別のアセンブリに実装した場合には、アセンブリに "MyControls" を設定します。図 2 内のコードは、これらの変更がなされた後の Page.xaml の内容を示しています。ここで、カスタム コントロールに対して custom をプレフィックスとして使用する必要はなく、任意の文字列や会社名を使用できます。
<UserControl x:Class="SimpleButtonDemo.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo"
    Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">
      <custom:SimpleButton />
    </Grid>

</UserControl>
Visual Studio によってソリューションに追加された SimpleButtonDemo_Web プロジェクト内のテスト ページ (SimpleButtonDemoTestPage.aspx または SimpleButtonDemoTestPage.html) のいずれかを起動することで、これまでに作成した結果を見ることができます。図 3 は SimpleButtonDemoTestPage.html のブラウザでの表示内容を示しています。ご覧のようにまだ特に何もありませんが、次の手順で変わります。
図 3 SimpleButton コントロールを表示する (クリックすると拡大画像が表示されます)

手順 3: コントロール テンプレートを作成する
前の手順でブラウザに表示がなかった理由は、SimpleButton はインスタンス化されても、UI がレンダリングされていなかったためです。これは、Page.xaml 内の SimpleButton 宣言をコントロール テンプレートを含めるように変更することで修正できます。図 4 のコードは、変更されたコントロール宣言を示しています。
<custom:SimpleButton>
  <custom:SimpleButton.Template>
    <ControlTemplate>
      <Grid x:Name="RootElement">
        <Rectangle x:Name="BodyElement" Width="200" Height="100"
          Fill="Lavender" Stroke="Purple" RadiusX="16" RadiusY="16" />
        <TextBlock Text="Click Me" HorizontalAlignment="Center"
          VerticalAlignment="Center" />
      </Grid>
    </ControlTemplate>
  </custom:SimpleButton.Template>
</custom:SimpleButton>
この宣言は、コントロールのビジュアル ツリーを定義する、コントロールの Template プロパティを初期化して、1 行、1 列のグリッド内に置かれる Rectangle および TextBlock を含めます。ブラウザで再び SimpleButtonDemoTestPage.html を起動すると、その出力は大きく異なります (図 5 を参照)。ようやく、SimpleButton が姿を見せました。
図 5 SimpleButton コントロール (クリックすると拡大画像が表示されます)

手順 4: 既定のコントロール テンプレートを作成する
カスタム コントロールを使用する開発者に、独自のコントロール テンプレートの定義を要求することは合理的ではありません。カスタム コントロールには、図 2 に示されるようなシンプルな宣言でもページに何かを表示できるように、既定のテンプレートが必要です。既定のテンプレートを提供しても、それが図 4 のようなテンプレートでオーバーライドされなくなるわけではありませんが、テンプレートの提供が必要であるという要件がなくなり、コントロールがより使いやすくなります。
カスタム コントロール用の既定テンプレートの定義に使用されるメカニズムは、WPF から借用します。コントロール プロジェクトに Generic.xaml と名付けられたファイルを追加して開始します (このファイルは Generic.xaml という名前にする必要があります。大文字と小文字は区別しませんが、この名前は必須です)。続いて、Generic.xaml 内でスタイルを定義して、プロパティ セッターを使用してコントロールの Template プロパティに値を割り当てます。Silverlight ランタイムが (リソースとして埋め込まれた) コントロール アセンブリ内で自動的に Generic.xaml を探して、コントロール インスタンスにこのスタイルを適用します。既定のテンプレートを定義するほかに、スタイルは、Width や Height などの他のコントロール プロパティに既定の値を割り当てることもできます。
これを確認するには、Visual Studio の [新しい項目の追加] を使用して、SimpleButtonDemo プロジェクトに Generic.xaml というテキスト ファイルを追加します。続いて、Generic.xaml の内容を図 6 のコードに置き換えます。これで Generic.xaml は、SimpleButton のすべてのインスタンスに適用される名前のないスタイルを含みます (TargetType 属性に注目)。このスタイルは、図 5 のコントロールに明示的に割り当てられた Template 値と同じ SimpleButton の Template プロパティ用の既定値を含みます。
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement" Width="200" Height="100"
              Fill="Lavender" Stroke="Purple" RadiusX="16" RadiusY="16" />
            <TextBlock Text="Click Me" HorizontalAlignment="Center"
              VerticalAlignment="Center" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
既定のテンプレートが準備できた状態で、SimpleButton.cs に戻り、クラス コンストラクタに以下のステートメントを追加します。
this.DefaultStyleKey = typeof(SimpleButton);
続いて、Page.xaml を開き、コントロール宣言を変更して、再び以下のようにします。
<custom:SimpleButton />
テスト ページをブラウザで起動すると、コントロールは先ほどと同じように見えます。ただし、今回は、はるかに簡単にこの表示を取得しています。

手順 5: テンプレート バインドを追加する
現時点で存在している SimpleButton の 1 つの問題点は、コントロールに割り当てられたプロパティ値が、コントロール テンプレートに従っていないことです。つまり、コントロールが次のように宣言されても、幅 200 および高さ 100 のままです。これは、これらの値がコントロール テンプレートにハードコーディングされているためです。
<custom:SimpleButton Width="250" Height="150" />
コントロール開発者の観点から見ると、Silverlight 2 の最も重要な機能の 1 つにテンプレート バインドがあります。テンプレート バインドは、コントロールに割り当てられたプロパティ値をコントロール テンプレートに流用し、{TemplateBinding} マークアップ拡張を使用して、XAML で宣言できます。ハードコーディング値を以下のように使用して、SimpleButton の本体を形成する Rectangle の Width および Height プロパティを定義する代わりに、
Width="200" Height="100"
これらを次のように定義してください。
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
これで、コントロールに割り当てられた幅と高さが、Rectangle に割り当てられた幅と高さにもなります。
図 7 に、基本クラスから継承された Width、Height、Background プロパティへ既定値を割り当て、テンプレート バインドを使用してコントロール テンプレート内のこれらのプロパティ値を参照する Generic.xaml の修正版を示します。
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <TextBlock Text="Click Me"
              HorizontalAlignment="Center"
              VerticalAlignment="Center" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
Page.xaml 内のコントロール宣言を編集して、修正したコントロール テンプレートを次のようにテストします。
<custom:SimpleButton Width="250" Height="150" Background="Yellow" />
この出力を図 8 に示します。TemplateBinding は、正しい実装のためのきわめて重要な手順です。これにより、割り当てたプロパティ値に従った SimpleButton のインスタンスが得られます。
図 8 強化された SimpleButton コントロール

手順 6: TextBlock の代わりに ContentPresenter を使用する
SimpleButton が ContentControl から派生するということは、SimpleButton は開発者がボタンの表示コンテンツをカスタマイズするために使用できる Content プロパティを、少なくとも理論上持っているということです。組み込みの Button コントロールを、図 9 の XAML を用いてカスタマイズし、図 10 に示される外観を作成することができます。
<Button Width="250" Height="150">
  <Button.Content>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Ellipse Width="75" Height="75" Margin="10">
        <Ellipse.Fill>
          <RadialGradientBrush GradientOrigin="0.25,0.25">
            <GradientStop Offset="0.25" Color="White" />
            <GradientStop Offset="1.0" Color="Red" />
          </RadialGradientBrush>
        </Ellipse.Fill>
      </Ellipse>
      <TextBlock Text="Click Me" VerticalAlignment="Center" />
    </StackPanel>
  </Button.Content>
</Button>
図 10 コンテンツがカスタマイズされたボタン
ただし、SimpleButton で同じことを行うと、コンテンツはシンプルなテキストのままであることがわかります。実際、Content="Test" を設定してボタン テキストを変更することもできません。これは、コントロール テンプレートが現在、ハードコーディングされたテキストと共にハードコーディングされた TextBlock を含んでいるためです。
この欠点は、SimpleButton の既定のコントロール テンプレート内の TextBlock を図 11 に示すような ContentPresenter に変えることで修正できます。TextBlock はテキストしかレンダリングできませんが、ContentPresenter はコントロールの Content プロパティに割り当てられたあらゆる XAML をレンダリングできます。これらの変更によって、図 12 の XAML SimpleButton 宣言から、図 13 の出力が得られます。これで、SimpleButton は、2 つのレベルのカスタマイズのサポートを行うようになりました。全体のビジュアル ツリーをカスタム テンプレートを用いて再定義するか、Content プロパティを使用してそのコンテンツだけを再定義することができます。さらに、単純な Content 属性を用いてボタン テキストを変更することもできます。SimpleButton は、ますます実際のボタン コントロールのように動作するようになってきました。
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="FontSize" Value="11" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <ContentPresenter Content="{TemplateBinding Content}"
              HorizontalAlignment="Center" VerticalAlignment="Center"
              FontSize="{TemplateBinding FontSize}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
<custom:SimpleButton Width="250" Height="150">
  <custom:SimpleButton.Content>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Ellipse Width="75" Height="75" Margin="10">
        <Ellipse.Fill>
          <RadialGradientBrush GradientOrigin="0.25,0.25">
            <GradientStop Offset="0.25" Color="White" />
            <GradientStop Offset="1.0" Color="Red" />
          </RadialGradientBrush>
        </Ellipse.Fill>
      </Ellipse>
      <TextBlock Text="Click Me" VerticalAlignment="Center" />
    </StackPanel>
  </custom:SimpleButton.Content>
</custom:SimpleButton>
図 13 コンテンツがカスタマイズされた SimpleButton

手順 7: クリック イベントを追加する
SimpleButton を実現する次の手順は、Click イベントを起動させることです。Silverlight コントロールでのイベントの実装は、一般的に、通常の Microsoft® .NET Framework クラスでのイベントの実装とあまり変わりはありません。単にコントロール クラスでイベントを宣言し、それらを起動するコードを記述するだけです。
ただし、Silverlight でわずかに異なるイベント起動の側面の 1 つには、Silverlight がルーティング イベントをサポートするということがあります。WPF では、イベント ルーティングは、イベントがビジュアル ツリーを上下に移動できることから、よりリッチであることを意味している一方、Silverlight では、イベントは上にだけ移動します ("バブリング" として知られるアクション)。ルーティング イベントは、RoutedEventHandler デリゲートによって定義され、ルーティング イベント ハンドラは、イベントを起動したオブジェクトを識別する Source プロパティを含む RoutedEventArgs オブジェクトを受け取ります (イベントがもともとビジュアル ツリーのより深いオブジェクトによって起動された場合は、イベント ハンドラに渡された sender パラメータとは異なります)。組み込みの Button コントロールの Click イベントはルーティング イベントであるため、SimpleButton もルーティング イベントの必要があります。
図 14 は、ルーティングされた Click イベントを起動するために、SimpleButton.cs のコントロール クラスに加える必要のある変更を示しています。SimpleButton のコンストラクタは、ここで MouseLeftButtonUp イベント用のハンドラを登録します。ハンドラは、登録されたリスナが少なくとも 1 つあれば、Click イベントを起動します。従来のボタン コントロールは、ボタンアップ イベントの前にボタンダウン イベントがある場合にだけ、Click イベントを起動します。ソース コードをできるだけ単純にするために、このロジックは SimpleButton では省略しました。
public class SimpleButton : ContentControl
{
    public event RoutedEventHandler Click;

    public SimpleButton()
    {
        this.DefaultStyleKey = typeof(SimpleButton);
        this.MouseLeftButtonUp += new MouseButtonEventHandler
            (SimpleButton_MouseLeftButtonUp);
    }

    void SimpleButton_MouseLeftButtonUp(object sender,
        MouseButtonEventArgs e)
    {
        if (Click != null)
            Click(this, new RoutedEventArgs());
    }
}
Click イベントをテストするには、Page.xaml 内のコントロール宣言を以下のように変更します。
<custom:SimpleButton Content="Click Me" Click="OnClick" />
次のように、Page.xaml.cs にイベント ハンドラを追加します。SimpleButton をクリックすると、"Click!" という語を含む警告ボックスが現れます。これは、イベントが正しく起動され処理された確かな証拠です。
protected void OnClick(Object sender, RoutedEventArgs e)
{
    System.Windows.Browser.HtmlPage.Window.Alert("Click!");
}

手順 8: Visual State を追加する
Silverlight コントロールの 2 つの主要なコンポーネントは、Visual State (表示状態) と Visual State Transition (表示状態の遷移) です。Visual State は、コントロールが押されたとき、マウスがコントロール内に入ったとき、コントロールが無効化されたときなどのさまざまな状態におけるコントロールの外観を定義します。Visual State Transition は、コントロールが、たとえば "通常の" 状態から "押された" 状態に移る場合に、1 つの Visual State から別の Visual State へどのように遷移するかを定義します。
Silverlight 2 Beta 2 では、Visual State Manager (VSM) として知られる新しいコンポーネントを導入して、状態および状態遷移を単純化し、より優れたツール サポートを容易にします。VSM では、状態を定義するために、Storyboard をカプセル化する VisualState オブジェクトを使用し、遷移を定義するために VisualTransition オブジェクトを使用します。次に、ユーザー操作に応じてコントロールを指定された状態へ遷移させる、VisualStateManager クラスの静的な GoToState メソッドを使用します。
図 15 は、VSM を活用するために Generic.xaml を変更する方法を示しています。SimpleButton は、Normal と MouseOver の 2 つの状態を定義します。これらは、<vsm:VisualState> 要素で定義されます。Normal 状態は、既定の状態のコントロールを表すので、Storyboard を必要としません。MouseOver 状態は、コントロールの背景色 (実際には、コントロールのボディーを表している四角形の Fill プロパティ) をピンクに変更するため ColorAnimation を使用します。ColorAnimation の Duration プロパティは、アニメーションの長さが対応する VisualTransition の Duration プロパティによって駆動されるので 0 です。VisualStateManager を宣言で使用できるようにするために、xmlns:vsm 属性が <ResourceDictionary> 要素に追加されたことに注目してください。
<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo"
  xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="FontSize" Value="11" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid>
            <vsm:VisualStateManager.VisualStateGroups>
              <vsm:VisualStateGroup x:Name="CommonStates">
                <vsm:VisualStateGroup.Transitions>
                  <vsm:VisualTransition To="Normal" Duration="0:0:0.2"/>
                  <vsm:VisualTransition To="MouseOver" Duration="0:0:0.2"/>
                </vsm:VisualStateGroup.Transitions>
                <vsm:VisualState x:Name="Normal" />
                <vsm:VisualState x:Name="MouseOver">
                  <Storyboard>
                    <ColorAnimation Storyboard.TargetName="BodyElement"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="Pink" Duration="0" />
                  </Storyboard>
                </vsm:VisualState>
              </vsm:VisualStateGroup>
            </vsm:VisualStateManager.VisualStateGroups>
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <ContentPresenter Content="{TemplateBinding Content}"
              HorizontalAlignment="Center" VerticalAlignment="Center"
              FontSize="{TemplateBinding FontSize}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
図 16 は、完成した SimpleButton クラスであり、Generic.xaml 内で宣言された状態と遷移を使用するように変更されています。このクラス自体も、コントロールがサポートする Visual State を示す TemplateVisualState 属性で修飾されています (これらの属性は必要ではありませんが、Expression Blend™ などのツールが、デザイン時の優れたエクスペリエンスを提供できるようにするためには必要です)。クラス コンストラクタは、MouseEnter および MouseLeave イベントを 1 組のハンドラに関連付け、ハンドラは VisualStateManager.GoToState を使用してコントロール状態を遷移させます。これで、マウスが入るとコントロールはピンクになり、マウスが離れると元の色に戻ります。
[TemplateVisualState(Name = "Normal", GroupName = "GroupCommon")]
[TemplateVisualState(Name = "StateMouseOver", GroupName = "GroupCommon")]
public class SimpleButton : ContentControl
{
    public event RoutedEventHandler Click;

    public SimpleButton()
    {
        DefaultStyleKey = typeof(SimpleButton);
        this.MouseLeftButtonUp +=
            new MouseButtonEventHandler(SimpleButton_MouseLeftButtonUp);
        this.MouseEnter +=
            new MouseEventHandler(SimpleButton_MouseEnter);
        this.MouseLeave +=
            new MouseEventHandler(SimpleButton_MouseLeave);
    }

    void SimpleButton_MouseLeftButtonUp(object sender,
        MouseButtonEventArgs e)
    {
        if (Click != null)
            Click(this, new RoutedEventArgs());
    }

    void SimpleButton_MouseEnter(object sender, MouseEventArgs e)
    {
        VisualStateManager.GoToState(this, "MouseOver", true);
    }

    void SimpleButton_MouseLeave(object sender, MouseEventArgs e)
    {
        VisualStateManager.GoToState(this, "Normal", true);
    }
}

完成したコントロール
SimpleButton の完成したソース コードは、この記事に付属するサンプル コードに含まれています。プロパティ、状態アニメーションをさらに実装して、コントロールをさらに調整し、UI をガラスのような外観にしあげることもできます。SimpleButton には、Silverlight コントロールのすべての主要な要素が含まれています。ここまでの説明で、皆さんも自分でコントロールを問題なく作成できるようになったに違いありません。

Jeff Prosise は、MSDN Magazine に寄稿している編集者で、『プログラミング Microsoft .NET』など数冊の著書があります。また、ソフトウェアのコンサルティングおよび .NET Framework 専門の教育機関である Wintellect (wintellect.com) の共同創立者でもあります。ご意見やご質問は、Jeff まで英語でお送りください。Jeff の連絡先は、wicked@microsoft.com です。

Page view tracker