Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN*
Search Microsoft.com for:
The Beta Experience

XAML の優位点

Peter Himschoot
U2U - ブリュッセル

適用対象:
  • Visual Studio .NET 2005
  • .NET 3.0
  • Windows Vista (Beta 2、June CTP、...)
  • XAML (June CTP)
概要:

.NET 3.0 (以前は WinFx と呼ばれていました) では、XAML を使用してオブジェクト グラフを作成および構築できます。たとえば、Windows Presentation Foundation (旧称 Avalon) では、XAML を使用して次世代のユーザー インターフェイスを構築できます。この記事では、旧式のアドベンチャー ゲーム Zork などの迷宮のサンプルを構築することで、(XML よりも豊富な) XAML の構文について考察します。これは Windows Presentation Foundation ではなく、XAML および作成するアプリケーションでの XAML の使用方法に関する記事です。WPF を使用するサンプルもありますが、それは単に XAML の概念を説明するためです。



ダウンロード:
目次:


XAML を使用する理由

最初に、XAML (または XML) を使用してオブジェクトを記述する利点について考察します。ご存知のとおり、コードにオブジェクトを直接作成できます。オブジェクトを XAML として定義すると、この XAML を変換できるツール、たとえば、オブジェクトの編集や変更が可能なカスタム エディタなどを簡単に構築できます。最も良い例が、ユーザー インターフェイスを構築する Microsoft Interactive Designer です。このツールは、デザイナ (すばらしいユーザー インターフェイスを構築する才能を持つ人々、および 1 行のコードも記述せずに、アニメーションやデータ バインディングなどを使用して芸術的なユーザー インターフェイスを構築する開発者) によって使用されます。これこそが XAML の能力なのです。


XAML を使用した Maze オブジェクト

XAML を説明するために Windows Presentation Foundation を使用せずに (XAML よりも WPF の詳細な説明が必要になってしまうため)、XAML と共に使用するカスタム オブジェクト階層を構築します。この方法を使用して、XAML を使用したオブジェクトの構築の詳細、XAML の高度な機能の活用方法、および用途に応じて XAML を拡張する方法について考察できます。部屋とアイテムが配置された小さな迷宮を構築するために、最初に XAML.Mazes という名前の新しいライブラリ オブジェクトの作成から開始します。class1.cs ファイルを Maze.cs という名前に変更し、次のクラスを構築します。

namespace XAML.Maze {
public class Maze {
private string _name;
public string Name {
get { return _name; }
set { _name = value; }
}
}
}

このプロジェクトを構築します。後で XAML で記述したこのオブジェクトを使用しますが、最初にアプリケーションを構築して、maze をメモリにロードします。


XAML をアプリケーションにロードする

ソリューションにコンソール アプリケーションを追加し、これを XAML.TheMaze と呼びます。プロジェクトにリファレンス (WindowsBase、PresentationCore、PresentationFramework、XAML.Mazes) を追加して、複数の using ステートメントを追加します。

using System.Windows;
using System.Windows.Markup;
using XAML.Mazes;
using System.IO;

Main メソッドを次のように実装します。

private const string myMaze = "../../myMaze.xaml";
static public Maze LoadMaze( string fromFile ) {
FileStream xamlFile = new FileStream ( fromFile, FileMode.Open, FileAccess.Read );
Maze theMaze = (Maze) XamlReader.Load ( xamlFile );
return theMaze;
}
static void Main( ) {
Maze myMaze = Program.LoadMaze ( Program.myMaze );
Console.ReadLine ( );
}

このコードは、System.Windows.Markup.XamlReader クラスを使用して、ディスクから myMaze.xaml という名前の XAML ファイルをロードします。そのために、Maze オブジェクト用の XAML ファイルを構築します。次に、新しい XML ファイルを myMaze.xaml と呼ばれるコンソール プロジェクトに追加します。Visual Studio で新しい XAML ファイルを追加すると、Cider が組み込まれた XAML エディタが起動するため少し時間がかかります。しばらくお待ちください。

<u:Maze
xmlns:u="clr-namespace:XAML.Mazes;assembly=XAML.Mazes" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
</u:Maze>

XAML の名前空間

この XAML ファイルでは、最初に、Maze と呼ばれるルート オブジェクトを使用します。ただし、XAML は Maze クラスを認識していないため、XamlReader にこのクラスの定義場所を通知する必要があります。このために、xml-namespace を宣言します。

 xmlns:u="clr-namespace:XAML.Mazes;assembly=XAML.Mazes" 

これは XAML で使用される特殊構文であり、u プレフィックスを XAML.Mazes CLR 名前空間および XAML.Mazes アセンブリにマッピングします。もう 1 つの名前空間の宣言では特定の XAML 拡張機能をポイントしますが、これは後半で説明します。Maze エレメントは u プレフィックスを使用するため、XamlReader は XAML.Mazes アセンブリをロードして XAML.Mazes.Maze クラスを検索する必要があることを認識します。XamlReader は、Maze オブジェクトの新しいインスタンスを作成し、Name プロパティに “The Amazing XAML Maze” という文字列を設定します。

Visual Studio Debugger を使用してこのアプリケーションをステップスルーすると、Maze オブジェクトが Name プロパティの設定で作成されることが確認できます。


オブジェクトのプロパティ

XAML は既定のコンストラクタを呼び出すことでオブジェクトを作成し、それぞれの xml 属性を使用して同じ名前のオブジェクトにプロパティを設定します。これで、Maze インスタンスの Name プロパティの値が正しく設定されます。実際には、使用している XAML は、次に示すものと同等のコードです。

Maze myMaze = new Maze ( );
myMaze.Name = "The Amazing XAML Maze";

Maze に Room を追加する

すべての迷宮では、一度は迷うことができるように複数の部屋が必要です。そこで、新しい Room クラスを XAML.Mazes プロジェクトに追加します。

public class Room
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
}

Room クラスは public です。

XAML maze ファイルに複数の部屋を追加します。

<u:Maze
xmlns:u="clr-namespace:XAML.Mazes;assembly=XAML.Mazes" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<u:Room Name="room_1" Description="The first room" />
<u:Room Name="room_2" Description="The second room" />
</u:Maze>

ただし、アプリケーションを実行すると、“Cannot add content to an object of type 'XAML.Mazes.Maze'.” (オブジェクト タイプ 'XAML.Mazes.Maze' にコンテンツを追加することはできません) という例外が発生します。これは maze オブジェクトに room オブジェクトを追加する場所を XAML が認識していないためです。


プロパティ-エレメント構文

Maze クラスで別の属性を使用して、XAML から room オブジェクトを maze に追加することができるでしょうか。当然できません。この理由は、第一に room はそれ自体が複雑なオブジェクトであり、さらに複数の部屋を設定しているためです。このために XAML はプロパティ-エレメント構文をサポートしており、この構文では <Class.Property> などの表記を使用できます。ただし、最初に Rooms プロパティを Maze に追加します。

public RoomCollection _rooms
= new RoomCollection();
public RoomCollection Rooms
{
get { return _rooms; }
}

これには Rooms コレクションが必要であるため、新しい RoomCollection クラスを XAML.Mazes プロジェクトに追加します。

public class RoomCollection : List<Room>
{
}

XAML ファイルを次のように変更します。

<u:Maze
xmlns:u="clr-namespace:XAML.Mazes;assembly=XAML.Mazes" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<u:Maze.Rooms>
<u:Room Name="room_1" Description="The first room" />
<u:Room Name="room_2" Description="The second room" />
</u:Maze.Rooms>
</u:Maze>

これは、XamlReader が <u:Maze.Rooms> 構文を認識できるため、有効です。さらに、RoomCollection クラスは ICollection インターフェイスを実装します。Room オブジェクトは標準オブジェクトとして構築されてから、ICollection.Add メソッドを呼び出すことで Rooms プロパティに追加されます。XAML では別の手法を使用することもできます。これについては記事の後半で詳しく説明します。

XAML ファイルは次のコードと同様の処理を実行します。

Maze myMaze = new Maze ( );
myMaze.Name = "The Amazing XAML Maze";
ICollection rooms = myMaze.Rooms;
Room room1 = new Room();
room1.Name = "room_1";
room1.Description = "The first room";
Rooms.Add( room1 );
Room room2 = new Room();
room2.Name = "room_2";
room2.Description = "The second room";
Rooms.Add( room2 );

XAML で複数の名前空間を使用する

ソリューションを少し整理してみましょう。最初に、自分の部屋を Maze クラスとは別の名前空間に設定します (プロジェクトの最後にどれだけの数の異なる room クラスが存在するかは予測できません)。そのために、Room および Rooms クラスを XAML.Mazes.Rooms 名前空間に移動します。

namespace XAML.Mazes.Rooms

Maze クラスで RoomCollection を使用するために、Maze クラスに新しい using ステートメントを追加します。

using XAML.Mazes.Rooms;

ここでアプリケーションを実行すると、“The tag 'Room' does not exist in XML namespace 'clr-namespace:XAML.Mazes;assembly=XAML.Mazes'” (タグ "Room" が XML の名前空間 "clr-namespace:XAML.Mazes;assembly=XAML.Mazes" に存在しません) という例外が発生します。

これはどうすれば解決できるでしょうか。

XAML は、実際には同じ XML 名前空間にマッピングされた複数の CLR 名前空間を使用できます。これにはいくつかの処理を実行する必要があります。最初に、次の CLR 属性を Maze クラスなどの XAML.Mazes プロジェクトに追加します (これをコンパイルするには、WindowsBase、PresentationCore、および PresentationFramework アセンブリを再び参照する必要があります)。

...
using System.Windows.Markup;
[assembly: XmlnsDefinition("http://www.u2u.be/amazingxaml", "XAML.Mazes")]
[assembly: XmlnsDefinition("http://www.u2u.be/amazingxaml", "XAML.Mazes.Rooms")]
[assembly: XmlnsPrefix("http://www.u2u.be/amazingxaml", "u")]
namespace XAML.Mazes
{
...

さらに、XAML ファイルでマッピングを指定する必要があります。

<?xml version="1.0" encoding="Shift_JIS" ?>
<?Mapping XmlNamespace="http://www.u2u.be/amazingxaml" ClrNamespace="XAML.Mazes" AssemblyName="XAML.Mazes"?>
<u:Maze
xmlns:u="http://www.u2u.be/amazingxaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<u:Maze.Rooms>
<u:Room Name="room_1" Description="The first room" />
<u:Room Name="room_2" Description="The second room" />
</u:Maze.Rooms>
</u:Maze>

このマッピングは XAML パーサーに XAML.Mazes アセンブリをロードするように命令します。XmlnsDefinition 属性は、XAML.Mazes アセンブリと XAML.Mazes.Rooms アセンブリのすべてのクラスを識別するために使用されます。そのため、ここで、この属性を XAML ファイルで使用できます。

実際には、次のように XAML ファイルで u: プレフィックスを取り除くことができます。

<?xml version="1.0" encoding="utf-8" ?>
<?Mapping XmlNamespace="http://www.u2u.be/amazingxaml" ClrNamespace="XAML.Mazes" AssemblyName="XAML.Mazes"?>
<Maze
xmlns="http://www.u2u.be/amazingxaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<Maze.Rooms>
<Room Name="room_1" Description="The first room" />
<Room Name="room_2" Description="The second room" />
</Maze.Rooms>
</Maze>

リソース

すべての迷宮には、ドアを開く、パズルを解くなどの行動に必要な複数のアイテムが含まれます。ここで迷宮に複数のアイテムを追加します。これらのアイテムはさまざまな場所に移動し、すぐに入手することができないようにするために、これらのアイテムを格納する別の場所が必要です。標準のアイテム コレクションを使用せずに、標準の XAML リソースを使用してアイテムを格納します。WPF ではリソースを使用して、スタイルやデータ オブジェクトなど、一般的に再使用されるオブジェクトを発見するための簡単な方法をそのオブジェクトに提供します。リソースは、ResourceDictionary クラスを使用して key 値のコレクションとして格納されるため、これをパブリック プロパティとして Maze クラスに追加します。

private ResourceDictionary _resources
= new ResourceDictionary();
public ResourceDictionary Resources
{
get { return _resources; }
set { _resources = value; }
}

当然ですが、Item クラスが必要なため、XAML.Mazes.Items 名前空間に新しい Item クラスを作成します。

namespace XAML.Mazes.Items
{
public class Item
{
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
private double _weight;
public double Weight
{
get { return _weight; }
set { _weight = value; }
}
}
}

また、Item クラスの先頭に、特定の XmlnsDefinition 属性を追加する必要があります。

[assembly: XmlnsDefinition("http://www.u2u.be/amazingxaml", "XAML.Mazes.Items")]

これで、XAML を使用してアイテムを迷宮に追加する準備ができました。

<Maze
xmlns="http://www.u2u.be/amazingxaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<Maze.Resources>
<Item x:Key="theKey" Description="A long, rusty key" Weight="55"/>
</Maze.Resources>
...
</Maze>

これについては、いくつかの注意点があります。最初に、リソースは key 値のペアとして格納され、その値がオブジェクトです。ただし、どこで key を取得しているのでしょうか。x:Key=”…” が、この目的で使用されます。x:Key は XAML の標準機能であり (x: プレフィックス)、key 値を XAML パーサーに送るために使用します。これで、key 値を ResourceDictionary インスタンスに追加できます。


型コンバータ

Weight プロパティは double ですが、XAML ではこの値は string になります。XAML では、どのようにして string を double に変換するのでしょうか。まるでターゲットの型 (またはターゲットのプロパティ) が TypeConverter を装備しているようです。TypeConverter は型を変換する標準的な方法です。.NET ランタイムでは標準の組み込み型の TypeConverter を提供します。これで、XAML は string を integer や double などに変換できます。

このシステムを使用することには利点があります。いくつかの部屋にアイテムが配置されているときに TypeConverter を使用すると、その部屋のアイテムの一覧を簡単に作成できます。最初に、新しい ItemNames プロパティを Room クラスに追加します。

using System.ComponentModel;
using XAML.Mazes;
... private static string[] noItemNames = new string[] { };
private string[] _itemNames = null;
[TypeConverter ( typeof ( StringArrayConverter ) )]
public string[] ItemNames {
get {
if( _itemNames == null )
return noItemNames;
else
return _itemNames;
}
set { _itemNames = value; }
}

ItemNames プロパティは文字列の配列です。型コンバータを使用すると、コンマ区切りのアイテム名の一覧が文字列の配列に変換されます。TypeConverterAttribute を使用して、XAML が変換を実行するために呼び出す必要のある TypeConverter を指定します。StringArrayConverter クラスを XAML.Mazes プロジェクトに追加します。

using System.ComponentModel;
using System.Globalization;
namespace XAML.Mazes
{
public class StringArrayConverter : TypeConverter
{
public override bool CanConvertFrom(
ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
else
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(
ITypeDescriptorContext context, CultureInfo culture, object value)
{
return (value as string).Split(',');
}
}
}

(TypeConverter については .NET のマニュアルに詳しく紹介されているため、ここでの説明は省略します)

これで部屋にアイテムを追加できます。

<Maze
xmlns="http://www.u2u.be/amazingxaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<Maze.Resources>
<Item x:Key="theKey" Description="A long, rusty key" Weight="55"/>
<Item x:Key="oldKnife" Description="An old, antique knife" Weight="65"/>
<Item x:Key="oldFork" Description="An old, antique fork" Weight="65"/>
</Maze.Resources>
<Maze.Rooms>
<Room Name="room_1" Description="The first room" ItemNames="theKey"/>
<Room Name="room_2" Description="The second room" ItemNames="oldKnife, oldFork"/>
</Maze.Rooms>
</Maze>

部屋にあるアイテムを実際に使用するには、maze のリソースを使用してアイテムを検索する、Items プロパティを追加する必要があります。

using XAML.Mazes.Items;
using System.Diagnostics;
...
private Item[] _items = null;
public Item[] Items {
get {
if( _items == null ) {
_items = LookupItemNames ( );
}
return _items;
}
}
private Item[] LookupItemNames( ) {
List<Item> items = new List<Item>( );
foreach( string itemName in ItemNames ) {
Item it = this.Maze.Resources[itemName.Trim()] as Item;
if( it != null )
items.Add ( it );
else
Debug.WriteLine ( "Item with name {0} was not found", itemName );
}
return items.ToArray ( );
}

このコードでは、room の maze にアクセスする必要があるため、このサポートを追加します (コードを Maze クラスに追加)。

static Maze _current;
public static Maze Current { get { return _current; } }
public Maze( ) {
_current = this;
}

マークアップ拡張機能

今度は部屋に出口を追加します。最初に、XAML.Mazes プロジェクトに新しい Exit クラスを追加します。

using XAML.Mazes;
using XAML.Mazes.Rooms;
using System.Windows.Markup;
[assembly: XmlnsDefinition("http://www.u2u.be/amazingxaml", "XAML.Mazes.Exits")]
namespace XAML.Mazes.Exits
{
public enum Direction
{
North,
East,
West,
South
};
public class Exit
{
private Direction _direction;
public Direction Direction
{
get { return _direction; }
set { _direction = value; }
}
private Room _room;
public Room Room
{
get { return _room; }
set { _room = value; }
}
}
}

当然、ExitCollection クラスも追加します。

public class ExitCollection : List<Exit>{
}

Exits プロパティを Room クラスに追加します。

using XAML.Mazes.Exits;
private ExitCollection _exits = new ExitCollection();
public ExitCollection Exits
{
get { return _exits; }
}

Exits を XAML ファイルに追加します。

<Room Name="room_1" Description="The first room" ItemNames="theKey">
<Room.Exits>
<Exit Direction="North" Room="???" />
<Exit Direction="East" Room="???" />
</Room.Exits>
</Room>

方角は簡単に指定することができ、XAML は自動的に文字列を一覧に変換しますが、部屋を指定するには、少し工夫が必要です。room エレメントは XML の無限ループを引き起こす可能性があるため、使用することはできません。room1 では、北側の出口が実際には同じ部屋になってしまっています。

マークアップ拡張機能Extension を使用します。これはパース時に XAML によって呼び出される特殊なクラスであり、ProvideValue メソッドを呼び出すことによって、マークアップ拡張機能に値を返すことを要求します。Markup Extension は XAML では簡単に使用できます。拡張機能の名前を中かっこ { <拡張機能> …} で囲んで指定するだけです。たとえば、WPF は {x:Resource} 構文を使用してリソースを検索します。固有の拡張機能を構築します。最初に、XAML.Mazes プロジェクトに新しい GoTo クラスを追加します (XAML で GoTo を使用することは、だれも思い付かないでしょう)。

using System.Windows.Markup;
using XAML.Mazes.Rooms;
...
[MarkupExtensionReturnType(typeof(Room))]
public class GoTo : MarkupExtension
{
internal class DelayRoomLookup : Room
{
public DelayRoomLookup(string name) :
base( name ) { }
}
private string _roomName;
private string RoomName { get { return _roomName; } }
public GoTo(string roomName)
{
_roomName = roomName;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
Room otherRoom = Maze.Current[RoomName];
if (otherRoom != null)
return otherRoom;
else
return new DelayRoomLookup(RoomName);
}
}

すべてのマークアップ拡張機能は MarkupExtension ベース クラスから派生し、ProvideValue メソッドをオーバーライドする必要があります。マークアップ拡張機能の初期化は通常、コンストラクタによって実行されるため、文字列の引数を設定できます。MarkupExtensionReturnTypeAttribute を使用すると、ProvideValue メソッドから Room オブジェクトが返されるということがわかると、XAML をさらに深く理解できます。ProvideValue メソッドは、部屋の名前ごとに検索を実行して移動先の部屋を返します。

この拡張機能を完了するには、名前ごとに部屋を検索するための読み取り専用のインデクサが Maze クラスに必要です。

public Room this[string roomName]
{
get
{
return Rooms.Find(delegate(Room room)
{
return room.Name == roomName;
});
}
}

当然ですが、Exit クラスに DelayRoomLookup クラスのサポートを追加する必要があります。ここで、Exit の Room プロパティを変更します。

public Room Room
{
get {
if (_room is GoTo.DelayRoomLookup)
{
GoTo.DelayRoomLookup lookup = _room as GoTo.DelayRoomLookup;
_room = Maze.Current[lookup.Name];
}
return _room;
}
set { _room = value; }
}

この機能を完了するために、Room クラスに GoTo メソッドを追加します。

public Room GoTo(Direction direction)
{
Exit exit = Exits.Find(delegate(Exit e) { return (e.Direction == direction); });
if (exit != null)
return exit.Room;
else
return this;
}

実際には、複数の string を使用して GoTo 拡張機能を初期化できます。たとえば、プレーヤーがアイテムを所有しているときにのみ使用できる出口を作成する場合を考えます。

public GoTo(string roomName, string itemName)
{
RoomName = roomName;
RequiredItemName = itemName;
}

XAML ファイルで次のように指定します。

<Exit Direction="North" Room="{GoTo room_1, theKey}" />

または、名前のプロパティを使用します。

private string _roomName;
public string RoomName
{
get { return _roomName; }
protected set { _roomName = value; }
}
private string _requiredItedName;
public string RequiredItemName
{
get { return _requiredItedName; }
protected set { _requiredItedName = value; }
}

次に、これを XAML ファイルに設定します。

<Exit Direction="North" Room="{GoTo RoomName=room_1, RequiredItemName=theKey}" />

ContentPropertyAttribute を使用する

アイテムの中には記述が非常に長くなるものがあります。この例では、アイテムの XAML エレメントが非常に長く、読み取るのが困難です。これは解決できるのでしょうか。ContentPropertyAttribute をクラスに追加すると、エレメントの内部テキストを記述として使用できるため、この問題を解決できます。

[ContentProperty("Description")]
public class Item
{ ... }

XAML アイテムは次のようになります (アイテム コレクションに追加)。

<Item x:Key="mustyBook" Weight="100">
An old musty book called -- Programming Windows 3.1 --
</Item>

実際に、これと同じ手法を使用して、<Maze.Rooms> プロパティ-エレメントを取り除くことができます。

[ContentProperty("Rooms")]
public class Maze
{ ... }

<Maze.Rooms> と </Maze.Rooms> タグを XAML ファイルから削除します。

<Maze
xmlns="http://www.u2u.be/amazingxaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="The Amazing XAML Maze"
>
<Maze.Resources>
...
</Maze.Resources>
<Room Name="room_1" Description="The first room" ItemNames="theKey">
<Room.Exits>
<Exit Direction="North" Room="{GoTo RoomName=room_1, RequiredItemName=theKey}" />
<Exit Direction="East" Room="{GoTo room_2}" />
</Room.Exits>
</Room>
<Room Name="room_2" Description="The second room" ItemNames="oldKnife, oldFork"/>
</Maze>

Room.Exits に対しても同じ処理を実行できます。


アタッチ プロパティ

XAML に関して説明する必要があることが 1 つ残っています。WPF では、コントロールに対してすべての種類のコンテナが存在します。たとえば、Canvas コントロールは子コントロールを任意の場所に配置できます。レイアウトを提供する Grid コントロールもあります。問題は、コントロールごとに収容コントロールでの配置場所を指定する必要があることです。当然ですが、コンテナごとにコントロールにプロパティを追加することは面倒な作業です。XAML では、アタッチ プロパティを使用してこの問題を解決します。アタッチ プロパティを使用すると、コントロールから収容コントロールにコントロール自体の配置場所を指定できます。次に例を示します。

<Window x:Class="AttachedPropertySample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AttachedPropertySample" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Grid.Column="1">Click me!</Button>
</Grid>
</Window>

この例では、ボタンを 2x2 グリッドの右下隅に配置しています。ボタンの位置を記憶するのは、実際には Grid クラスであり、button クラスは Grid クラスから独立しています。

アタッチ プロパティを使用して、プレーヤーの開始部屋を設定するために、XAML.Mazes プロジェクトに新しい player クラスを追加します。

public class Player
{
static Room _startRoom;
}

当然、Maze には player プロパティを追加します。

private Player _player;
public Player Player
{
get { return _player; }
set { _player = value; }
}

アタッチ プロパティ構文を使用して、いずれかの部屋を開始部屋として設定します。

<Room Player.Start="true" Name="room_1" ...

アタッチ プロパティは static Get<PROPERTY> および Set<Property> メソッドを追加することで実装されます。

public class Player
{
public static void SetStart(Room room, bool isStart)
{
if (room == null)
throw new ArgumentException("Invalid room");
_startRoom = room;
}
public static bool GetStart(Room room)
{
return _startRoom;
}
...
}

Set メソッドには 2 つの引数を指定します。1 つはアタッチ プロパティが使用されているエレメント (Room クラス) で、もう 1 つはプロパティの値 (この例では Boolean) です。実装は通過した部屋を開始部屋として記憶します (Boolean 引数を無視)。


改良点

Mapping 処理命令を省略することで、XAML ソリューションをさらに改良できます。<?Mapping …> 処理命令を XAML ファイルから削除し、コンソール アプリケーションの LoadMaze メソッドに次のコードを追加します。

static public Maze LoadMaze( string fromFile ) {
FileStream xamlFile
= new FileStream ( fromFile, FileMode.Open, FileAccess.Read );
ParserContext parserContext = new ParserContext();
XamlTypeMapper mapper = new XamlTypeMapper(new string[] { "XAML.Mazes" });
parserContext.XamlTypeMapper=mapper;
Maze theMaze =
(Maze)XamlReader.Load(xamlFile, parserContext);
return theMaze;
}

XamlTypeMapper から XamlReader に XAML.Mazes アセンブリをロードして XmlnsDefinitions を処理するように命令が発行されて、Maze クラスが特定されます。


XAML の手引き

簡単なリファレンスとして XAML を使用する場合の一般的な重要事項は、次のとおりです。

Q: XAML ファイルで複数のクラスを使用し、それらをすべて同じ CLR 名前空間に設定できますか?

A: 次のように、XML の名前空間宣言をエレメントに追加します (大文字表記箇所はプレースホルダです)。

 xmlns:PREFIX="clr-namespace:NAMESPACE.CLASS;assembly=ASSEMBLYNAME" 

これで <prefix:class>xml element 構文によりオブジェクトを使用できます。

Q: XAML ファイルで、複数の CRL 名前空間からクラスを使用する必要がありますか?

A: XmlnsDefinition 属性を使用して、名前空間を同じ xml 名前空間にマッピングしてから

<?Mapping XmlNamespace="…" ClrNamespace="…" AssemblyName="…"?>XML 処理命令を使用します。または、XamlTypeMapper を使用します。

Q: オブジェクト生成のための構文をさらに短くできますか?

A: カスタム TypeConverter を使用し、文字列のパースによってオブジェクトを作成します。

Q: クラスに複数のオブジェクトを追加します。そのための簡単な構文はありますか?

A: ContentPropertyAttribute を使用します。ただし、これは 1 つのプロパティに対してのみ機能します。他のプロパティにはプロパティ-エレメント構文を使用します。

Q: クラスから独立する必要のある他のオブジェクトと対話する方法はあります?

A: アタッチ プロパティ構文を使用します。あらゆるオブジェクトと対話できますが、そのための処理が必要です。

Q: オブジェクトのグラフを指定して、簡単な編集用ツールを構築するための簡単な方法はありますか?

A: XAML を使用してください。


著者紹介




Peter Himschoot 氏は U2U の設計者兼トレーナーであり、.NET、Visual Studio Team System、BizTalk Server、および .NET 3.0 (WinFx) を専門に扱っています。
また、彼は Microsoft Regional Director でもあります。

彼への問い合わせ先は peter@u2u.net です。

また、彼のブログ http://blog.u2u.info/DottextWeb/peter/ もご覧ください。

お気に入りの T シャツ: >> Code is poetry! <<


U2U Training and Consultancy Services はベルギーにある Microsoft .NET のトレーニング センターです。詳細については、www.u2u.be を参照してください。


© 2008 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy Statement
Microsoft