コード ネーム "Avalon"
新しいコードとマークアップ モデルを使用して実際のアプリケーションを作成する
Charles Petzold
この資料は、Microsoft Windows コード ネーム "Longhorn" の Pre-PDC ビルドを基に執筆されており、ここに記載されている情報は変更になることがあります。
Download the code for this article: Avalon.exe (157KB)
翻訳元: Create Real Apps Using New Code and Markup Model (英語)
注: このドキュメントは製品の出荷前に執筆されたもので、記載されている内容が出荷される製品の詳細と同一であることについて、いかなる保障もいたしません。記載されている情報はこのドキュメントが作成された時点での製品について言及しているものであり、計画用としてのみ使用してください。情報は、事前の予告なしに変更されることがあります。
要約
"Longhorn" というコード ネームが付けられた次期バージョンの Windows の新しいプレゼンテーション サブシステムでは、開発者に新しい優れた機能が提供されます。開発者は、"Avalon" というコード ネームが付けられたこの新しいサブシステムにより、Extensible Application Markup Language (コード ネーム"XAML") という言語、または C# などの最新のオブジェクト指向の言語を使用して、"Avalon" の機能を利用できます。"Avalon" 向けに記述される多くのアプリケーションでは、XAML とプログラミング コードの両方が使用されることが予想されます。この資料では、このことを踏まえて、ページ レイアウトの制御に使用する XAML タグについて説明し、イベントに対応する手続きコードについても解説します。
目次
- インターフェイスとの決闘
- レイアウトのオプション
- イベント処理
- 要素とオブジェクト
- アニメーション
- スタイル
- "Avalon" バージョンの ColorScroll
- まとめ
何千年もの間、哲学者や科学者たちは、2 つの相反し、共存する原理の秩序を説明しようとしてきました。善悪、陰陽、物質とエネルギー、心と身体、波と粒子、そして、もちろんプログラミング コードとマークアップ言語です。
現在、プログラミング コードとマークアップ言語は、不安定な休戦状態にあります。理論上、プログラミング言語は、コンピュータが行えるあらゆる処理を実行できますが、プログラミング言語を使用すると、シンプルなビジュアル インターフェイスにテキスト、画像、およびコントロールのレイアウトをうまく設定できないことがあります。マークアップ言語は、サイズが異なる画面や異なる環境に適応可能なテキストと画像が複雑に含まれているページを定義することに秀でています。ただし、ユーザーとの間で行われる対話が重要な場合には不適切な言語です。
Windows ベースのクライアント アプリケーションをビルドするための新しいプログラミング インターフェイスを作成するにあたり、Microsoft の開発者は、この二元論を否定するのではなく、受け入れて称賛することにしました。プログラミング コードとマークアップ言語が互いをサポートする役割を担う強力かつ複雑に調和された環境を作成したのです。その結果が "Avalon" というコード ネームが付けられたプレゼンテーション サブシステムです。このサブシステムは、アダムとイブ以来、相乗的な二元論における最大の試みになるかもしれません。Vive la diffrence! (違いを受け入れましょう!)
1. インターフェイスとの決闘
Windows の次期バージョン "Longhorn" の一部である "Avalon" は、主に .NET Framework に追加される新しいクラスのコレクションで構成されます。"Avalon" のプログラミングにおいて最も重要かつ新しい名前空間には、現在 MSAvalon.Windows、MSAvalon.Windows.Controls、および MSAvalon.Windows.Media という名前が付けられています (ただし、これらの名前空間は製品版がリリースされる前に変更される予定です)。"Avalon" では、C#、Visual Basic .NET、または .NET 共通言語仕様 (CLS: Common Language Specification) でサポートされている他の任意の言語を使用して、アプリケーションを記述できます。これらのプログラムは、今日記述している Windows フォーム アプリケーションと非常によく似ています。Windows フォーム アプリケーションと似ているのは、"Avalon" の「通常」の部分です。
また、"Avalon" では、"Longhorn" で使用できる "XAML" (Extensible Application Markup Language) というコード ネームが付けられた新しいマークアップ言語が定義されます (これは、"ザム[メ]ル" と発音します)。XAML を使えば、HTML と同様の手順でテキスト、画像、およびコントロールのレイアウトを定義できます。XAML は XML ベースの言語なので、HTML よりも構文が厳密で曖昧さがありません。ほとんどの場合、XAML は、ビジュアル デザイン ツールが自動生成することになると思われます。ただし、(初期の段階で) XAML コードを手作業で記述することは、良い学習経験になるでしょう。
"Avalon" 用に記述される多くのアプリケーションでは、プログラミング コードと XAML の両方が含まれることになるでしょう。アプリケーションで最初に表示されるビジュアル インターフェイスの定義には XAML を使用し、その他の処理にはプログラミング コードを記述することになります。プログラミング コードは、XAML に直接埋め込むか、または別のファイルに保持できます。XAML を使用して行えるすべての処理は、プログラミング コードで行えます。ですから、XAML を一切使用せずにプログラムを記述することも可能です。ただし、逆は真ではありません。プログラミング コードでしか行えない処理が多数あり、XAML のみで構成されたアプリケーションは非常に簡単なアプリケーションになります。次に XAML コードの一部を示します。
<Button Background="LightSeaGreen" FontSize="24pt">
Calculate
</Button>
このコードは 1 つの XML 要素です。XML 要素には、開始タグと終了タグがあり、これらのタグの間に内容が含まれています。この要素の型は、Button です。開始タグには 2 つの属性が指定されています。各属性の名前は、Background と FontSize です。これらの属性には、属性値が設定されています。XML の必要条件により、属性値は単一引用符または二重引用符で囲まれている必要があります。開始タグと終了タグの間には、要素の内容が含まれています。この場合の内容は、ボタンに表示されるテキストです。
この Button 要素には、幅や高さが設定されていません。通常、"Avalon" では、ボタンや他のコントロールのサイズは、コンテンツに合わせて自動調整されます (もちろん、この動作をオーバーライドすることもできます)。この例では、Button 要素は 24 ポイントのフォントで文字列 "Calculate" を表示できるサイズに調整されます。また、Button 要素には座標位置が設定されていません。通常、コントロールは、ウィンドウやコントロールのサイズに基づいて実行時に動的に配置されます。
XAML は、"Avalon" のクラス ライブラリと密接な関係があります。実際のところ、XAML で使用できるすべての要素の型はクラスです。具体的には、MSAvalon.Windows 名前空間で定義された UIElement クラスまたは ContentElement クラスの子孫になります。UIElement クラスの子孫には Control クラスがあります。ボタン、スクロール バー、リスト ボックス、テキスト ボックスなどすべての一般的な ユーザー インターフェイス コントロールは、Control クラスの子孫になります。ContentElement クラスから派生されるクラスには、Bold や Italic があります。
XAML の開始タグで指定する属性名は、実際には、これらのクラスのプロパティになります。プロパティは、これまでの .NET Framework においても重要な役割を果たしてきましたが、XAML では、その役割がさらに重要なものになります。今までに .NET Framework プログラミングを行ったことがない方は、プロパティが、メソッドやフィールドと同様に、クラスのメンバであることに注意してください。プロパティは、使用時にはフィールドのように見えますが、どちらかというとメソッドのように実装されます。フィールドとは異なり、プロパティにはコードが含まれます。プロパティに対しては、読み取り、書き込み、または読み取りと書き込みの両方を行えます。読み取り/書き込みを行える Background という名前のプロパティは、基本的には、set_Background と get_Background という名前の対になった 1 組のメソッドに相当します。
もちろん、先駆的なプログラマは、独自のクラスを XAML 要素として使用できるかどうか気になると思います。独自のクラスを XAML 要素として使用することは、もちろん可能です。XAML が Extensible Application Markup Language と呼ばれるのには理由があります。引数をとらないパブリック コンストラクタと設定可能な (読み取り専用でない) プロパティを持つクラスは、XAML で使用できます。
上記の例で示した Button 要素は、Button 型のオブジェクトを作成して、Background、FontSize、および Content プロパティを割り当てるコードと同等です。同等の C# コードは、次のようになります。
Button btn = new Button();
btn.Background = Brushes.LightSeaGreen;
btn.FontSize = new FontSize(24, FontSizeType.Point);
btn.Content = "Calculate";
おわかりいただけるように、XAML でオブジェクトを定義すると、これらの 3 つのプロパティを非常に簡単に割り当てられます。Brushes クラスには、定義済みの色がすべて含まれている静的なプロパティが定義されています (これらは、一般的に HTML でサポートされている色です)。FontSize は、単位に依存しない測定方法をカプセル化した構造体です。FontSizeType は列挙型です。プロパティが文字列型、整数型、浮動小数点型、ブール型、列挙型、またはその他の型など、どんな型として定義されていても、同等の内容を XAML では属性に対するテキスト文字列として割り当てることができます。
Content プロパティは、ボタンに表示されるコンテンツを表し、XAML の Button 要素の内容に対応します。つまり、Button 要素は次のように記述することもできます。
<Button Background="LightSeaGreen" FontSize="24pt" Content="Calculate">
</Button>
Button 要素で Content 属性を設定し、開始タグと終了タグの間に内容を含める必要がないので、次のように空の要素タグとして記述することもできます。
<Button Background="LightSeaGreen" FontSize="24pt" Content="Calculate" />
要素の末尾に終了スラッシュ (/) があることに注意してください。
この例の Content プロパティは、文字列型のプロパティに見えますが、そんなに単純なものではありません。Content プロパティは、Object 型として定義されています。つまり、単純なテキスト文字列以外のものをボタンのコンテンツとして設定できることを示しています。たとえば、次に示すように Image 要素を設定することもできます。
<Button> <Image Source="Calc.jpg"/> </Button>
ボタンにテキストと画像の両方を表示させる場合は、次のようなコードを記述できます。
<Button>
Calculate
<Image Source="Calc.jpg"/>
</Button>
Text 要素と Image 要素は並んで表示されます (これらの要素は、親である Button 要素の子要素と呼ばれることがあります)。Button 要素では、高度なレイアウトをサポートしていませんが、必要に応じて、より高度なレイアウトを実現できます。より高度なレイアウトを実現するには、Button 要素のコンテンツに柔軟性のあるレイアウト オプションをサポートしている Text などの XAML 要素を設定します。以下に、コンテンツが 1 つの Text 要素であるボタンのコードを示します。この Text 要素には、画像、改行、およびテキスト文字列が含まれています。
<Button FontSize="24pt">
<Text>
<Image Source="Calc.jpg"/>
<LineBreak/>
Calculate
</Text>
</Button>
LineBreak 要素は、HTML の <br> に相当します。画像の下にテキストを表示するようなボタンは、ツール バーなどで利用するのが適切かもしれません。次のように、テキストの書式を斜体にして、目立たせることもできます。
<Button FontSize="24pt">
<Text>
<Image Source="Calc.jpg"/>
<LineBreak/>
Calculate <Italic>this</Italic>
</Text>
</Button>
これは、マークアップ言語を使用すると自然で、プログラミング コードを使用すると奇妙に見える処理の一例です。実際、これらのすべての要素はプログラミング コードで指定できますが、そのような処理を行いたいとは思わないでしょう。
XAML について少し説明をしたので、今度は、いくつか完成したプログラムを見てみましょう。図 1 は、単純な XAML を実装した典型的な Hello World プログラムです。XML ではルート要素は 1 つしか指定できません。XAML では、ルート要素は通常、出力サーフェスになります。TextPanel 要素は、HTML のような自動レイアウト機能が備わった出力サーフェスです。通常、アプリケーションのページごとに (もしアプリケーションに複数のページがあればですが) 1 つの XAML ファイルが存在し、ダイアログ ボックスごとに 1 つの XAML ファイルが存在します。Style と呼ばれる機能を使用すると、プログラム内で視覚的な統一性を持たせることができます。この機能については、この資料の後半で説明します。
図 1 Hello World
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml"
Background="BlanchedAlmond"
FontFamily="Comic sans MS"
FontSize="36pt"
HorizontalAlignment="Center">
Hello, world!
</TextPanel>
TextPanel 要素の xmlns (XML 名前空間) 属性は、C# の using ディレクティブと同じ機能を果たします。XAML ファイルのすべての要素は、Microsoft .NET Framework の対応するクラスにマップされるので、xmlns 属性には、対応するクラスが宣言されている .NET 名前空間の名前を保持するファイルを指す URI が提供されます。
Hello World プログラムを、より興味深いものにするために、TextPanel 要素にいくつかの属性を追加して、色やフォントを設定し、TextPanel 要素の子要素がパネル内で水平方向に中央揃えになるようにしました。TextPanel 要素の内容は、文字データ "Hello, world!" です。コンテンツには、画像、コントロール、長いテキストなどを設定することもできます。
HelloWorld.xaml ファイルにはコードが含まれていないので、このファイルを "Longhorn" バージョンの Microsoft Internet Explorer に直接読み込むことができます。このファイルを読み込むと、Web ページのような外観のプログラムが表示されます。また、現在 MSBuild と呼ばれているプログラムを使用して、HelloWorld.xaml をコンパイルすることもできます。このコンパイル処理では、その他に小さいファイルがあと2つ必要になります (ただし、ここでは、これらのファイルの内容は示していません)。ファイルの拡張子が PROJ または MSPROJ に設定されているファイルには、プログラムに関する情報を指定し、必要なすべてのソース ファイル (XAML またはその他のファイル) を記述します。もう 1 つの小さな XAMLファイルは、プログラムの初回実行時に表示する XAML ページを指定するのに必要です。Hello World プログラムを実行すると、Windows プログラムのようなプログラムが表示されます。図 2 では、両方のバージョンのプログラムを示します。
図 2 Hello World プログラム - ページとプログラム
.gif)
このプログラムと、ほぼ同等の C# バージョンのプログラムを図 3 に示します (ここで、「ほぼ同等」という表現を使用したのは、MSBuild では C# コードを生成することが可能ですが、このコードは、実際に生成される C# コードとは異なるためです。ここでお見せしているバージョンは、大幅に簡略化したものです)。このプログラムは、次のコマンドを使用して、コマンド ラインからコンパイルできます。
csc_/r:WindowsBase.dll;PresentationCore.dll;PresentationFramework.dll
Windows フォームを使用したことがある方なら、このプログラムの構造と外観は見慣れていると思います。このプログラムでは、TextPanel オブジェクトを作成する前に、明示的に application オブジェクトと window オブジェクトを作成する必要があります。これらの準備段階の処理は、このプログラムの XAML バージョンに含まれています。
図 3 C# バージョンの Hello World アプリケーション
using System;
using MSAvalon.Windows;
using MSAvalon.Windows.Controls;
using MSAvalon.Windows.Media;
class HelloWorldApp: Application
{
[STAThread]
static void Main()
{
HelloWorldApp app = new HelloWorldApp();
app.Run();
}
protected override void OnStartingUp(StartingUpCancelEventArgs args)
{
Window win = new Window();
TextPanel tp = new TextPanel();
tp.Background = Brushes.BlanchedAlmond;
tp.FontFamily = "Comic sans MS";
tp.FontSize = new FontSize(72f, FontSizeType.Point);
tp.HorizontalAlignment =
MSAvalon.Windows.HorizontalAlignment.Center;
tp.TextRange.Text = "Hello, world";
win.Children.Add(tp);
win.Show();
}
}
この C# コードの変わっている点は、ウィンドウに表示されるテキスト文字列に関する部分です。通常、Windows プログラムでは、テキストを表示する座標位置を指定しますが、TextPanel オブジェクトにはウィンドウやコンテンツのサイズに合わせてパネルのコンテンツの位置やサイズを調整する自動レイアウト機能が含まれています。これは、HTML がクライアント アプリケーションのプログラミングに与えた影響の一例です。"Avalon" では、プログラミング コードで自動レイアウト機能を利用することができます。
ページのトップへ
2. レイアウトのオプション
通常、XAML ファイルのルート要素は、他の要素の表示サーフェスになります。そのため、"Avalon" には Panel クラスといくつかの Panel クラスの子孫 (たとえば、HelloWorld プログラムで使用した TextPanel 要素) が含まれています。
FlowPanel 要素は TextPanel 要素に似ています。HTML と同様に、これらの要素では要素のサイズとウィンドウのサイズに基づいて自動レイアウトが行われます。TextPanel 要素には、より高度なテキストの書式設定機能が用意されており、テキストを複数の列に配置することもできます。高度なテキストの書式設定が必要でない場合は、FlowPanel 要素を使用することをお勧めします。
HelloWorld プログラムで FlowPanel 要素を使用するには、パネル上に表示されるテキストの指定方法を変更する必要があります。TextPanel 要素と異なり、FlowPanel 要素にはテキスト コンテンツを設定できません。ただし、FlowPanel 要素には Text 要素で構成されるコンテンツを設定できます。Text 要素を使用し、FontFamily 属性と FontSize 属性を Text 要素に含める必要があります。
<Text FontFamily="Comic Sans MS" FontSize=36pt>Hello, world!</Text>
別のパネル オプションとして、DockPanel 要素を使用できます。この要素は、子要素を並べて表示し、パネルの隅に固定します。Windows エクスプローラは、ドッキングを使用している典型的なアプリケーションです。上部にツール バー、下部にステータス バー、左側にディレクトリ ツリー、そして右側にディレクトリのコンテンツが表示されます。
GridPanel 要素では、行と列を使用して子要素を調整します。また、"Avalon" では、HTML の Table タグと同様の追加機能を提供する Table コントロールが用意されています。
デカルト座標系の愛好家には倦厭されていますが、3 つ目のレイアウトのオプションとして Canvas 要素が挙げられます。要素の位置を示すためのプロパティを使用して、Canvas パネル上に要素を明示的に配置する必要があります。
1 ページで使用できるパネルの数に制限はありません。XAML に含まれる要素は、すべて階層構造になっています。たとえば、次に示すように DockPanel 要素を使用してページを 3 つの領域に分割し、その後、各領域に FlowPanel 要素、Canvas 要素、および GridPanel 要素を使用することができます。
<DockPanel>
<FlowPanel DockPanel.Dock="Left">
</FlowPanel>
<Canvas DockPanel.Dock="Top">
</Canvas>
<GridPanel DockPanel.Dock="Fill">
</GridPanel>
</DockPanel>
ビジュアル インターフェイスの定義には、プログラミング コードよりもマークアップ言語が適している理由の 1 つとして、XAML の階層構造という特質が挙げられます。マークアップ言語では、ネストとインデントを使用して階層を模倣できます。実際、Windows 1.0 がリリースされた 1985 年の時点では、プログラマはメニューやダイアログ ボックスの階層構造を定義するのにテキスト リソース スクリプトを使用していました。当時、階層は 1 階層に限定されていましたが、それが出発点でした。XAML ファイルは、単純なリソース スクリプトのようなものです。
Button 要素のコンテンツには、テキスト、画像、またはネストした Text 要素のテキストと画像の組み合わせを設定できます。Button 要素のコンテンツには、他の要素を配置するパネルを設定することもできます。図 4 は、この処理を忠実に行う SmileyButton という名前のプログラムのコードです。このボタンのコンテンツは、Canvas 要素です。このプログラムは、Canvas 要素に MSAvalon.Windows.Shapes 名前空間からいくつかの要素を配置します。図 5 は、実行中のプログラムを示しています。
図 4 SmileyButton
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml"
Background="Cyan" HorizontalAlignment="Center">
<Button Width="150" Height="140">
<Canvas Width="120" Height="120">
<Ellipse CenterX="60" CenterY="60" RadiusX="50"
RadiusY="50" Fill="Gray" Stroke="Black"/>
<Ellipse CenterX="40" CenterY="40" RadiusX="10"
RadiusY="10" Fill="Blue"/>
<Ellipse CenterX="80" CenterY="40" RadiusX="10"
RadiusY="10" Fill="Blue"/>
<Polyline Points="30, 70, 60, 80, 90, 70"
StrokeThickness="10" Stroke="Red"/>
</Canvas>
</Button>
</TextPanel>
図 5 実行中の SmileyButton
.gif)
Shapes 名前空間は、Shape という名前の抽象クラスと 7 つの子孫 (Line、Polyline、Rectangle、Ellipse、Polygon、Glyphs、および Path) で構成されています。残念ながら、Shapes 名前空間には Arc クラスや ベジエ クラスがありません。そのため、ボタンに表示される顔の口には 2 つのセグメントで構成される Polyline を使用しました。Path 要素でベジエ曲線を使用して弧を模倣することもできましたが、見えを張るようなのでやめておきました。XAML ページにアタッチしたプログラミング コードでは、高度なグラフィックを実現できます。
ページのトップへ
3. イベント処理
Windows ベースのアプリケーションは、単に Web ページにあこがれているだけではありません。プログラムは、それぞれ独自の方法でユーザー インターフェイス イベントに応答する必要があります。このような場合、プログラミング コードがXAML を補完することになります。プログラミング コードは、別のファイルとして保持することも、XAML に直接埋め込むこともできます。この資料では、説明を簡単にするために、後者のアプローチについて説明します。
.NET Framework と C# では、イベントと呼ばれる汎用化されたメカニズムを利用して、クラスが相互に通知を行えるようになっています。イベントは、メソッド、フィールド、およびプロパティなどと同様にクラスのメンバです。あるクラスがイベントを宣言します。別のクラスでは、宣言されたイベントのイベント ハンドラを定義するかどうかを選択します (イベント ハンドラは、特定のシグニチャと戻り値の型が指定されたメソッドです)。1 つ目のクラスからそのメソッドを呼び出すことによって、2 つ目のクラスにイベントを通知できます。イベントは、常にデリゲートと関連付けられています。デリゲートは、基本的にはメソッドのプロトタイプです。イベント ハンドラは、このデリゲートに従って宣言する必要があります。
典型的なユーザー インターフェイス イベントは、ボタンの Click イベントです。Button クラスには、ClickEventHandler デリゲートと関連付けられている Click という名前のイベントがあります。このデリゲートによると、Click イベントのイベント ハンドラは次のように宣言しなければなりません。
void ButtonClick(object el, ClickEventArgs args)
{
MessageBox.Show("The button has been clicked");
}
1 つ目のパラメータには、それが要素であることがわかるように el という名前を付けました。このイベント ハンドラの名前は、XAML コードでは、次のように Button 要素の属性として指定できます。
<Button Click="ButtonClick" ...>
プログラミング コードを XAML ファイルに埋め込むには、特別な構文を使用する必要があります。プログラミング コードでは、< や & など、XML コードにおいて特別な意味を持つ記号を使用することがあります。XML 仕様には、XML ファイルで任意の文字データを使用するために用意された CDATA セクションという名前の機能があります。XAML ファイルに埋め込むプログラミング コードは、CDATA セクションで囲まれている必要があります。CDATA セクションは、"<![CDATA[" で始まり、"]]>" で終わります。CDATA セクションでは、どのような状況においても "]]>" を使用することはできません。この制限は、
if (arr1[arr2[I]]>5)
のような C# コードを記述する場合に問題となることがあります。偶発的な CDATA の区切り記号 ("]]>") がある場合は、どこかに空白を挿入することで、問題を解決できます。
XAML では、CDATA セクションは、def:Code 要素で囲まれている必要があります。また、別の要素で、使用しているプログラミング言語を明示する必要があります (現在、C#、Visual Basic .NET、および JScript .NET を使用できます)。図 6 では、埋め込みの C# コードで Click イベントに対処するボタンを持つ完全な XAML ファイルを示しています。"Longhorn" バージョンの Internet Explorer では、C# コードは変換されないので、このファイルをコンパイルする必要があります。ボタンをクリックすると、メッセージ ボックスが表示されます。
図 6 XAML と C# のイベント ハンドラ
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition" def:Language="C#"
HorizontalAlignment="Center"
Background="LightCyan">
<Button Click="ButtonClick" FontSize="72">
Push Me!
</Button>
<def:Code>
<![CDATA[
void ButtonClick(object el, ClickEventArgs args)
{
MessageBox.Show("The button has been clicked.");
}
] ]>
</def:Code>
</TextPanel>
ページのトップへ
4. 要素とオブジェクト
ページに複数のボタンがある場合、ボタンごとに別々のイベント ハンドラを使用できます。これを行うには、イベント ハンドラに別の名前を付けます。また、1 つのイベント ハンドラで 1 つ以上のボタンに対処することもできます。通常、ボタンごとに別のイベント ハンドラを使用する場合は、どのボタンがクリックされたかを明確に判断できる必要があります。この問題には 2 つの解決方法があります。
まず、Click イベントのイベント ハンドラに 2 つのパラメータがあることに注目します。1 つ目のパラメータが、イベントを生成したオブジェクトです。この場合は、ユーザーがクリックした Button オブジェクトです。イベント ハンドラ内では、パラメータを Button 型のオブジェクトにキャストできます。
Button btn = el as Button;
その後、Button クラスのすべてのメンバにアクセスできるようになります。ボタンの色を変更したり、テキストを変更したり、ボタンをクリックできないようにしたり、または別のイベント ハンドラを設定することもできます。
FrameworkElement クラスには、ID という名前の文字列型の特別なプロパティが含まれています (すべてのコントロールとパネルは、このプロパティから派生します)。このプロパティには、ボタンを識別するテキスト文字列を設定できます。MSBuild の仕様により、同じページ内の ID は一意にする必要があります。以下に、同じイベント ハンドラを使用し、一意な ID を含む 2 つの Button 要素を示します。
<Button ID="OkButton" Click="ButtonClick">OK</Button>
<Button ID="CancelButton" Click="ButtonClick">Cancel</Button>
イベント ハンドラ内では、ID プロパティを使用して、ボタンを識別できます。たとえば、次に示すように switch ステートメントでボタンを識別できます。
switch(el.ID)
{
case "OkButton":
break;
case "CancelButton":
break;
}
これは、ID プロパティを使用する方法の一例です。ID プロパティは、「特別なプロパティ」であると説明しましたが、ID プロパティは、本当に特別なプロパティです。
もう一度、以下の 2 つの XAML 要素を見てみましょう。
<Button ID="OkButton" Click="ButtonClick">OK</Button>
<Button ID="CancelButton" Click="ButtonClick">Cancel</Button>
MSBuild では、コンパイルの準備処理として、この XAML を C# コードに変換するときに、OkButton と CancelButton という名前の Button 型の 2 つのフィールドが作成されます。これらのフィールドは、作成された Button オブジェクトと同じ値が設定されます。イベント ハンドラ内では、これらの ID 名を (実際はオブジェクトではありませんが) Button オブジェクトとして使用することができます。次に例を示します。
if (el == OkButton)
{
}
else if (el == CancelButton)
{
}
switch ステートメントでは整数式と文字列しか使用できないので、ここでは switch ステートメントではなく if ステートメントを使用しています。
これらの ID 名を使用するとオブジェクトを識別できるだけでなく、明示的にページ上にある他のすべての要素にアクセスできるようになります (少なくとも、ID 値を割り当てたページ上にあるすべての要素には、明示的にアクセスできるようになります)。たとえば、次の単純なコードを使用すると、一部のコントロールでブール値の値によって OK ボタンを有効または無効にすることができます。
OkButton.IsEnabled = bEnable;
実際のところ、すべての要素は要素ツリーでリンクされているので、イベント ハンドラは ID 属性を持たない要素を含むページ上にある任意の要素にアクセスできます。要素で子要素がどのようにサポートされているかについては、この資料の前半で説明しました。ILogicalTreeNode インターフェイスでは、Parent と Children という名前の 2 つのプロパティが定義されています (このインターフェイスは、FrameworkElement クラスにより実装されており、要素として機能している多くのクラスでも実装されています)。これらの 2 つのプロパティを使用すると、要素ツリー全体にアクセスできます。そのため、要素ツリーでは、要素を動的に追加、削除、または変更できます。
図 7 は、MoveButtons という名前のプログラムのコードです。このプログラムには、"Tom"、"Dick"、および "Harry" という 3 つのラベルが付けられたボタンがあります。どのボタンをクリックした場合にも、クリックしたボタンのテキストが他の 2 のボタンのどちらかに移動されます。この処理は、単にパネルの Children プロパティの要素を移動しているだけです。このプログラムと同等の機能を持つプログラムを簡単な Win32 プログラム、MFC プログラム、または Windows フォーム アプリケーションで記述するどどうなるか想像が付きますか? おそらく、もっと複雑なものになるでしょう。
図 7 MoveButtons
<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition" def:Language="C#"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Click="ButtonClick" FontSize="72">Tom</Button>
<Button Click="ButtonClick" FontSize="72">Dick</Button>
<Button Click="ButtonClick" FontSize="72">Harry</Button>
<def:Code>
<![CDATA[
void ButtonClick(object el, ClickEventArgs cea)
{
Button btn = (Button) el;
FlowPanel parent = (FlowPanel) btn.Parent;
parent.Children.Remove(btn);
parent.Children.Insert(1, btn);
}
] ]>
</def:Code>
</FlowPanel>
ページのトップへ
5. アニメーション
アニメーション GIF ファイルを除き、ほとんどのグラフィック アニメーションにはコードが伴います。XAML に関する驚くべき点の 1 つには、このマークアップ言語全体で使用できるアニメーション機能です。良くも悪くも、アニメーション効果は簡単に XAML ページに挿入できます。
この機能の使用方法を理解するには、開始タグの要素属性を設定する別の方法があることを知る必要があります。
<Button Background="Red">
Cancel
</Button>
上記の要素は、下記のように記述できます。
<Button>
Cancel
<Button.Background>
Red
</Button.Background>
</Button>
これで、Background プロパティは、Button 要素内の別のタグになりました。Background プロパティの値は、Button.Background タグのコンテンツに指定されています。このプロパティをアニメーション化します。このコンテンツを次のように ColorAnimation 要素のコンテンツに置き換えます。
<Button>
Cancel
<Button.Background>
<ColorAnimation From="Red" To="Blue"
Duration="10" Fill="Hold"/>
</Button.Background>
</Button>
これによって、ボタンの初回表示時には、10 秒間で色が赤から青に変化します...。実は、事はそれほど単純ではありません。Background プロパティは、Color オブジェクトではなく Brush オブジェクトとして定義されているので、Button.Background 要素の子として SolidColorBrush 要素を、SolidColorBrush 要素の子として SolidColorBrush.ColorAnimations 要素を含める必要があります。これらの子要素を含めると、ColorAnimation 要素は、次に示すように SolidColorBrush.ColorAnimations の子要素になります。
<Button>
Cancel
<Button.Background>
<SolidColorBrush>
<SolidColorBrush.ColorAnimations>
<ColorAnimation From="Red" To="Blue"
Duration="10" Fill="Hold"/>
</SolidColorBrush.ColorAnimations>
</SolidColorBrush>
</Button.Background>
</Button>
このアプローチは、大掛かりなものですが、グラデーションや他の複雑なブラシ パターンに柔軟に対処できます。Brush オブジェクトの Background プロパティを使用すると、画像やビデオなどより鮮やかな背景を挿入することもできます。
もちろん、他にも多数のアニメーション オプションがあります。アニメーションの繰り返し方法を回数や時間で指定したり、繰り返し時にアニメーションを逆転させるかどうかを指定できます。加速や減速も指定できます。より厳密にアニメーションを制御するために、一連の主要な値と時間、および補間方法を指定することもできます。
また、浮動小数点の値、座標点、サイズ、および長さをアニメーション化することもできます。図 8 に示す AnimationButton プログラムは、左眼の 1 つの軸をアニメーション化してウィンクしているように見せている点を除き、SmileyButton と同様です。
図 8 ウィンクしている SmileyButton
<?Mapping XmlNamespace="anim"
ClrNamespace="MSAvalon.Windows.Media.Animation"
Assembly="PresentationFramework" ?>
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml"
Background="Cyan" HorizontalAlignment="Center">
<Button Width="150" Height="140">
<Canvas Width="120" Height="120">
<Ellipse CenterX="60" CenterY="60" RadiusX="50"
RadiusY="50" Fill="Gray" Stroke="Black"/>
<Ellipse CenterX="40" CenterY="40" RadiusX="10"
RadiusY="10" Fill="Blue"/>
<Ellipse CenterX="80" CenterY="40" RadiusX="10"
RadiusY="10" Fill="Blue">
<Ellipse.RadiusY>
<LengthAnimationCollection xmlns="anim">
<LengthAnimation From="10" To="1"
Duration="0.5" AutoReverse="True"
RepeatDuration="Indefinite"/>
</LengthAnimationCollection>
</Ellipse.RadiusY>
</Ellipse>
<Polyline Points="30, 70, 60, 80, 90, 70"
StrokeThickness="10" Stroke="Red"/>
</Canvas>
</Button>
</TextPanel>
ページのトップへ
6. スタイル
関連のあるコントロールのグループに、同じプロパティを何度も設定したことがあると思います。HTML を使用している場合は、CSS (カスケード スタイル シート) を使用して作業を簡略化できます。"Avalon" では、Style 要素を使用して同様の処理を行います。たとえば、FlowPanel 要素のすべてのボタンの背景色をシアン、フォント サイズを 14 ポイントに設定する場合は、次のような Style タグを使用できます。
<FlowPanel.Resources>
<Style>
<Button Background="Cyan" FontSize="14pt"/>
</Style>
</FlowPanel.Resources>
各要素の型ごとに 1 つの Sytle 要素を使用して、同じ Resources セクションで複数の要素の型を指定できます。また、特定の要素から派生するクラス、特定の ID 値を持つクラス、または特定の ID 値を持たないクラスにスタイルを適用することもできます。
さらに、特定のスタイルに名前を指定して、指定した要素にのみスタイルを適用することもできます。たとえば、ダイアログ ボックスに複数のオプション ボタンがあると仮定します。これらのオプション ボタンの一部に別のサイズまたは色を設定する場合、これらのオプション ボタンには、名前を指定した Style タグを割り当てられます。
<RadioButton Style="{Special}" >
次の Style の宣言では、"{Special}" に該当する Style プロパティを指定しているオプション ボタンに 1 つ目のスタイルが適用され、Style プロパティが設定されていないオプション ボタンには 2 つ目のスタイルが適用されます。
<FlowPanel.Resources>
<Style def:Name="Special">
<RadioButton />
</Style>
<Style>
<RadioButton />
</Style>
</FlowPanel.Resources>
また、既に他のプロパティが設定されている要素にのみ適用するプロパティを指定するプロパティ シートを作成する方法もあります。
ページのトップへ
7. "Avalon" バージョンの ColorScroll
図 9 は、この資料で説明した多くの "Avalon" の機能を含む ColorScroll.xaml という名前のプログラムのコードです。ColorScroll は、この雑誌の前身である『Microsoft Systems Journal』の 1987 年 5 月号で公開された簡単な Windows プログラムを "Avalon" 用に変更したものです。ColorScroll は、私の著書『プログラミング Windows』でも使用しています。私の著書『C# によるプログラミング Windows』(日経 BP ソフトプレス、2002 年発行) および『Microsoft Visual Basic .NET によるプログラミング Windows』(日経 BP ソフトプレス、2002 年発行) 用に、最近このプログラムを Windows フォームに変換しました。
図 9 ColorScroll.xaml
<DockPanel xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition" def:Language="C#">
<FlowPanel Width="50%" DockPanel.Dock="Left" >
<FlowPanel.Resources>
<Style>
<Text Height="36pt" FontSize="12pt"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Style>
<Style>
<VerticalSlider Width="50%" Margin="25%,0,25%,0"
Minimum="0" Maximum="255"
SmallChange="1" LargeChange="16"
DockPanel.Dock="Fill"/>
</Style>
</FlowPanel.Resources>
<DockPanel Width="33.3%">
<Text DockPanel.Dock="Top" Foreground="Red">Red</Text>
<Text DockPanel.Dock="Bottom" Foreground="Red">0</Text>
<VerticalSlider ID="RedScroll" ValueChanged="OnScrolled"/>
</DockPanel>
<DockPanel Width="33.3%">
<Text DockPanel.Dock="Top" Foreground="Green">Green</Text>
<Text DockPanel.Dock="Bottom" Foreground="Green">0</Text>
<VerticalSlider ID="GreenScroll" ValueChanged="OnScrolled"/>
</DockPanel>
<DockPanel Width="33.3%">
<Text DockPanel.Dock="Top" Foreground="Blue">Blue</Text>
<Text DockPanel.Dock="Bottom" Foreground="Blue">0</Text>
<VerticalSlider ID="BlueScroll" ValueChanged="OnScrolled"/>
</DockPanel>
<def:Code>
<![CDATA[
void OnScrolled(object el, ValueChangedEventArgs args)
{
// Text 要素に新しい値を設定します
Control slider = (Control) el;
DockPanel parent = (DockPanel)
((ILogicalTreeNode) slider).Parent;
Text elemText = (Text) parent.Children[1];
elemText.TextRange.Text =
((int) args.NewValue).ToString();
// パネルの背景色を設定します
ColorPanel.Background=
new SolidColorBrush(
Color.FromRGB((byte) (RedScroll.Value),
(byte) GreenScroll.Value,
(byte) BlueScroll.Value));
}
] ]>
</def:Code>
</FlowPanel>
<TextPanel ID="ColorPanel" DockPanel.Dock="Right"
Width="50%" Background="Black"/>
</DockPanel>
図 10 は、実行中のプログラムを示しています。このプログラムの左側にある 3 つのスライダを操作して、原色「赤」、「緑」、および「青」に基づいた色を選択します。原色の値は、スライダ下部に表示され、スライダを操作した結果生成された色はウィンドウの右側に表示されます。ウィンドウのサイズを変更すると、スライダのサイズはウィンドウの幅と高さに合わせて自動調整されます。
図 10 実行中の ColorScroll
.gif)
Windows API または Windows フォームを使用して、このようなプログラムを記述した場合、ウィンドウ上のすべてのコンポーネントの移動とリサイズ処理が必要となります。この点においては、"Avalon" が非常に優れています。
メイン パネルは DockPanel 要素です。2 つの子要素は、それぞれ親要素の半分の領域を占めています。左側の要素は FlowPanel、右側の要素は色を表示する TextPanel です。TextPanel 要素には、ID 値 ColorPanel が指定されています。
左側の FlowPanel 要素には、3 つの子要素 DockPanel があります。これらの子要素は、それぞれ親要素の 1/3 の領域を占めています。これらの DockPanel 要素は、それぞれ 3 つの子要素を持っています。上部と下部に Text 要素、中央に Slider 要素を保持しています。
Text 要素と Slider 要素の大きさは、ファイルの先頭部分にある Style 要素で指定されています。Text 要素のフォント サイズは 12 ポイント、Text 要素の高さは 36 ポイントに設定されています。テキストは、Text 要素に対して垂直方向に中央揃えに設定され、テキストの上下に視覚的なゆとりが生まれます。2 つの Text 要素は DockPanel 要素の上部と下部でドッキングされているので、これらの Text 要素は親要素の幅全体を占有し、各要素はウィンドウ全体の 1/6 の領域を占有しています。
各 DockPanel 要素の中央には、3 つの Slider 要素が配置されています。ただし、プロパティ シートでは、各スライダに対して親要素の幅の 50% が与えられているので、スライダの左右に親要素の幅の 25% にあたる余白が生まれます。これらの余白は、スライダの両側に視覚的なゆとりをもたらします。
Text 要素のフォント サイズと高さ以外の大きさは、ウィンドウの幅に対するパーセント単位で指定します。これについては多少の議論がありましたが、ピクセル単位で指定するよりは簡単だと思います。
各 Slider 要素には、同様に初期化する必要のあるいくつかの属性があります。各 Slider 要素には、0 から 255 までの範囲があります。方向キーを押すと 1 単位移動します。PageUp キーか PageDown キーを押すか、またはスライダの中央部分をクリックすると、スライダは 16 単位移動します。これらのプロパティは、すべて Style 要素で指定されます (より適切なアプローチでは、まず、Text 要素と Slider 要素が含まれる DockPanel 要素から新しいクラスを派生します。次に、派生したクラスから、赤、緑、青のスクロール バー用の 3 つのオブジェクトを作成します)。
3 つのスクロール バーには、RedScroll、GreenScroll、および BlueScroll という ID 値が設定され、ウィンドウの右側の TextPanel 要素には ID 値 ColorPanel が設定されます。これらすべての ID 値は、スライダの ValueChanged イベントの OnScrolled イベント ハンドラで参照されます。
イベント ハンドラでは、まず適切な処理が行われます。Parent プロパティ、パネルの Children プロパティを使用して、特定のスライダが以前に発生させた兄弟イベントを取得します。つまり、色を数値表記しているスライダ下部にある Text 要素です。その後、メソッドは、Text 要素のテキストにスライダの新しい値を設定します。メソッドは、イベント ハンドラに渡された ValueChangedEventArgs オブジェクトの Value プロパティから、この値を取得します。また、このメソッドでは、オブジェクトのパラメータを Slider 要素にキャストして、その Value プロパティにアクセスすることもできます。
その後、メソッドは 3 つのスライダの Value プロパティに基づいて新しい色を算出して、ID 値 ColorPanel が設定されているウィンドウ右側の TextPanel 要素の新しい Background プロパティに色を設定します。
このプログラムの最新の Win32 API バージョンのコードは 250 行です (『プログラミング Windows 第 5 版』では COLORS1 と呼ばれています)。『C# によるプログラミング Windows』のWindows フォーム バージョンは、約 100 行です。この新しい "Avalon" バージョンのプログラムは、たったの約 60 行です。継承を使用すれば、さらに短縮できると思います。
250 行から 100 行になり、今度は 60 行です。皆さん、これが「進歩」というものです。
ページのトップへ
8. まとめ
"Avalon" と XAML は、過去の Windows ベースのアプリケーション プログラミングとの離別の象徴です。多くの点において、アプリケーションの UI をデザインすることが、以前より簡単になり、アプリケーションを展開することが楽になるでしょう。"Longhorn" ベースのアプリケーションは、UI の定義に軽量の XAML マークアップ言語を使用することにより、プログラミング コードとマークアップ言語の両方のアプローチの利点を組み合わせた、Web プログラミング モデルとデスクトップ プログラミング モデルの収束を新たな段階に進めることは間違いありません。
Charles Petzold は、『MSDN Magazine』 (およびその前身の『MSJ』) を長期に渡って手掛けている編集者です。近々 Microsoft Press から発行される予定の "Avalon" に関する書籍『Programming the Windows Client Platform』 (暫定タイトル) の著者でもあります。彼の Web サイトには、http://www.charlespetzold.com (英語) からアクセスできます。
この記事は、MSDN マガジン - January 2004号からの翻訳です。