XAML
最終更新:
atachi
XAMLの記述
XAMLでの各要素はそのままクラスコードに直結します。
XAMLでButton要素を使用する場合、Button要素の実体はSystem.Windows.Controls.Buttonクラスです。
そのためXAMLではXMLの要素をオブジェクト要素と呼んでいます。
属性の設定(属性構文)
XMLの属性は、XAMLではオブジェクトのプロパティへ値を設定する意味を持ちます。
<Button Text="Buttonです" />
XAMLで記述されたこの内容は、次のようなC#コードと同じ意味を持ちます。
Button btn = new Button();
btn.Text = "Buttonです";
ただし、XAMLでの記述のすべてがC#コードの等価コードとして表現できません。
次のXAMLはBackgroundプロパティを設定していますが、見たとおりの解釈ではC#等価コードとしては表現できません。
<Button Text="Buttonです" Background="Blue"/>
Button btn = new Button();
btn.Text = "Buttonです";
btn.Background = "Blue"; // コンパイルエラー
XAMLではBackgroundプロパティの値を文字列で設定していますが、ButtonクラスのBackgroundプロパティの型は文字列型ではないため、もしXAMLの見たままのコードをC#コードで記述しようとするとエラーが発生するのです。
ButtonクラスのBackgroundプロパティはBrush型です。
XAMLでは、与えられた「Blue」という文字を巧妙にBrush型に変換し(型コンバーター)、ButtonクラスのBackgroundプロパティへ設定しています。
属性の記述方法は、XMLの属性として記述する方法以外にも次のような記述方法ができます。
<Button>
<Button.Text>
Buttonです
</Button.Text>
<Button.Background>
<SolidColorBrush Color="Blue"/>
</Button.Background>
</Button>
このようなプロパティの記述方法をプロパティ要素と呼びます。
この記述内容は、説明してきたXAMLと同じ結果をもたらしますが、 Backgroundプロパティの設定ではSolidColorBrush要素を明示的に記述するなど、属性がどんな変換がなされているかが表現できています。
オブジェクトのプロパティを設定するにあたって、属性構文を使うかプロパティ要素を使うかはスタイルの問題で重要ではありません。
省略可能なプロパティ
オブジェクト要素が持つプロパティの中には、
コンテンツプロパティ
と呼ばれるプロパティが設定されているものがあります。
コンテンツプロパティは、プロパティ要素による記述で、ノードの記述を省略しても良いプロパティ要素です。
Border.Childはコンテンツプロパティなので省略しても良いことになっているため、前者のBorderは「<Border.Child>」のノードを省略しています。
次の2つのBorderはどちらも同じデザインです。
<Border>
<TextBox Width="300"/>
</Border>
<!--explicit equivalent(省略しない場合)-->
<Border>
<Border.Child>
<TextBox Width="300"/>
</Border.Child>
</Border>
テキストコンテンツ
Button要素のラベルに表示するテキストの設定はText属性で指定していましたが、Buttonのように文字列がコンテンツとして重要な意味を持つオブジェクト要素の中には、次のように記述できるものがあります。
<Button>Buttonです</Button>
マークアップ拡張書式
XAMLの属性には文字列としての意味を持つ値しか記述できませんが、画像などのリソースや表示したい内容を動的に変更(バインディング)したい場合があります。
属性にこのような特殊な方法で値を設定する方法としてマークアップ拡張機能があります。
マークアップ拡張機能を使うには、次のマークアップ拡張書式をXAMLの属性に記述します。
{拡張名
値
}
{拡張名
キー1=値1,キー2=値2
...}
拡張名 | 用途 | 説明 |
Binding | データバインディング | |
DynamicResource | リソース参照 | |
MultiBinding | データバインディング | |
PriorityBinding | データバインディング | |
TemplateBinding | データバインディング | |
RelativeSource | データバインディング | データバインディングでBinding.RelativeSourceプロパティに対してのみ使用できるマークアップ拡張 |
StaticResource | リソース参照 | 定義済みのインスタンスを検索し、XAMLの属性値として使用する。(リソースについて) |
XAMLの構文定義について
用途 | プレフィックス定義 | 定義の名前空間 | |
XAMLの名前空間 | xmlns | http://schemas.microsoft.com/winfx/2006/xaml/presentation | |
追加の名前空間 | xmlns:x | http://schemas.microsoft.com/winfx/2006/xaml | リファレンス |
名前空間(x:)
ディレクティブ | 説明 |
x:Code | インラインコードを記述する(詳細) |
x:XData |
属性 |
x:Array |
x:Class |
x:ClassModifier |
x:FieldModifier |
x:Key |
x:Name |
x:Shared |
x:Subclass |
x:TypeArguments |
マークアップ拡張 | 説明 |
x:Null | XAML プロパティの値として null を指定する。 |
x:Static | |
x:Type | 指定した XAML 型の基になる型の CLR Type オブジェクトを指定します。 |
CLR名前空間をXAMLの名前空間から参照
xmlns属性に「clr-namespace:***」という書式で、CLR名前空間を参照するXAML名前空間を定義できる。
<Window xmlns:dn="clr-namespace:System;assembly=mscorlib">
...
</Window>
.NET Frameworksの名前空間をバインドすると便利かもしれない。
参照方法 | 説明 |
clr-namespace:System;assembly=mscorlib | Int16やDecimalといった基本型 |
clr-namespace:System.Collections;assembly=mscorlib | Listなどのコレクション型 |
XAMLと分離コード
XAMLは、それ自体の追加の定義を分離コードで定義することができます。
- <Page
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- x:Class="ExampleNamespace.ExamplePage">
- </Page>
このようにx:Class属性を使用して、このXAMLに対応したクラスをExampleNamespace.ExamplePageに実装します。
この場合、Pageオブジェクト要素の分離コードとなるため、ExampleNamespace.ExamplePageクラスはPageクラスをスーパークラスに設定しなければなりません。
namespace ExampleNamespace {
public partial class ExamplePage : Page {
public ExamplePage() {
InitializeComponent(); // 必須
}
}
}
ExamplePageクラスに実装したメソッドやプロパティはXAMLから呼び出すことができます。また、XAMLで定義した名前付きのオブジェクト要素をExamplePageクラスから呼び出すこともできます。
動作的にはIDEによってXAMLファイルはC#コードにコンバートされます。
C#ではクラスの定義を複数のファイルに記述することができるクラス(パーシャルクラス)があるので、プログラマが記述するコードとツールが出力するコードを分けることができます。(上記のように分離コードではpartial修飾子をつけてクラスを定義している)
XAML内コード(インラインコード)
x:Code要素を使ってXAML内にコードを記述することができます。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<x:Code><![CDATA[
void Clicked(object sender, RoutedEventArgs e)
{
button1.Content = "Hello World";
}
]]></x:Code>
<Button Name="button1" Click="Clicked">Click Me!</Button>
</Page>
インラインコードは短いロジックを記述するには便利ですが、使用にはいくつかの制限があります。
- インターフェースとロジックとの切り分けが曖昧
- プラグマの記述ができない(特に#ifdefがつかえないのは痛い)
リソース
リソースを使用するにはルート要素が持つResourcesプロパティを使用します。
外部リソースでは再利用可能なスタイルやインスタンスなどを定義したファイルをリソースとして定義しておくことができます。
外部リソースを1つだけ読み込む場合には、Resourcesに直接ResourceDictionary要素を子要素に追加していきます。
複数の外部リソースを読み込む場合は、次のようにリソースのマージを行います。
<Window.Resources>
<ResourceDirectory>
<ResourceDirectory.MergedDictionaries>
<ResourceDictionary Source="MyResource1.xaml" />
<ResourceDictionary Source="MyResource2.xaml" />
<ResourceDictionary Source="MyResource3.xaml" />
</ResourceDirectory.MergedDictionaries>
</ResourceDirectory>
</Window.Resources>
リソースの定義方法
任意のクラスのインスタンス
<Window.Resources>
<SolidColorBrush x:Key="resBrush" Color="Yellow" />
</Window.Resources>
コレクション
<col:ArrayList x:Key="DataSource">
<sys:DateTime>1/2/2003 5:00:00</sys:DateTime>
<sys:DateTime>4/5/2006 13:13:13</sys:DateTime>
<sys:DateTime>7/8/2009 23:59:59</sys:DateTime>
</col:ArrayList>
.NET Frameworksのコレクションクラスを使用するために、XMLノードの属性にxmlns属性を使ってそれぞれのコレクションに名前をつけます。 上記のサンプルコードでのcolやsysといった名前空間は次の通りです。
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
ObjectDataProvider
<Window.Resources>
<!-- the customers datasource -->
<ObjectDataProvider
x:Key="CustomerDataProvider"
ObjectType="{x:Type local:CustomerDataProvider}"/>
<ObjectDataProvider
x:Key="Customers"
MethodName="GetCustomers"
ObjectInstance="{StaticResource CustomerDataProvider}" />
<!-- the orders datasource -->
<ObjectDataProvider
x:Key="OrdersDataProvider"
ObjectType="{x:Type local:OrdersDataProvider}"/>
<ObjectDataProvider
x:Key="Orders"
MethodName="GetOrdersByCustomer"
ObjectInstance="{StaticResource OrdersDataProvider}" >
<ObjectDataProvider.MethodParameters>
<x:Static Member="system:String.Empty"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
ObjectDataProviderはObjectInstanceに指定した任意のインスタンスからデータを取得する。
class MyData {
public string[] GetNames() {
return new string[] {"北海道","青森","秋田"};
}
}
XmlDataProvider
<XmlDataProvider x:Key="MyDataSource" XPath="/Companies">
<x:XData>
<Companies xmlns="">
<Company>
<Name>Acme Inc.</Name>
<Contact>
<Name>John Doe</Name>
<PhoneNumber>111</PhoneNumber>
<PhoneNumber>222</PhoneNumber>
</Contact>
<Contact>
<Name>Billy Bob</Name>
<PhoneNumber>333</PhoneNumber>
<PhoneNumber>444</PhoneNumber>
</Contact>
</Company>
<Company>
<Name>Large Corp.</Name>
</Company>
</Companies>
</x:XData>
</XmlDataProvider>
リソースの使用方法
- StaticResourceマークアップ拡張
- コードからの利用
コードからリソースを参照する方法
ResourcesプロパティにアクセスするかFindResourceメソッドを使用します。
// Resourcesプロパティを使用してリソースを取得
SolidColorBrush scb = this.Resources["resBrush"] as SolidColorBrush;
SolidColorBrush scb2 = this.Resources["resBrush2"] as SolidColorBrush;
SolidColorBrush escb = this.FindResource("resBrush") as SolidColorBrush;
Contract.Requires(scb == escb, "異なるリソースです");
ソースからリソースを参照できるので、次のようにXAMLで定義したリソースをソースコードから初期化して、任意のデータが初期化されたインスタンスをリソースとして定義できます。
- public partial class MainPage : UserControl
- {
- public MainPage()
- {
- InitializeComponent();
- MainData data = (MainData)Resources["mainDataDataSource"];
- data.GridItemSelectIndex = 0;
- }
- }
- <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="BindingSample01.MainPage"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:appDatas="clr-namespace:BindingSample01.Datas"
- mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
- <UserControl.Resources>
- <appDatas:MainData x:Key="mainDataDataSource"/>
- </UserControl.Resources>
- <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource mainDataDataSource} }">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="200"/>
- <ColumnDefinition Width="*"/>
- </Grid.ColumnDefinitions>
- <data:DataGrid Grid.Column="0" AutoGenerateColumns="False"
- ItemsSource="{Binding GridItemList, Mode=TwoWay}"
- SelectedIndex="{Binding GridItemSelectIndex, Mode=TwoWay}">
- <data:DataGrid.Columns>
- <data:DataGridTextColumn Header="ID" Binding="{Binding Id, Mode=TwoWay}"/>
- <data:DataGridTextColumn Header="NAME" Binding="{Binding Name, Mode=TwoWay}"/>
- </data:DataGrid.Columns>
- </data:DataGrid>
- <StackPanel Grid.Column="1" DataContext="{Binding GridItemSelectedItem, Mode=TwoWay}">
- <TextBox Text="{Binding Id, Mode=TwoWay}"></TextBox>
- <TextBox Text="{Binding Name, Mode=TwoWay}"></TextBox>
- <TextBox Text="{Binding Comment, Mode=TwoWay}"></TextBox>
- </StackPanel>
- </Grid>
- </UserControl>
オブジェクトのツリー構造
XAMLは次のようにXMLをネストしてレイアウトを作成します。
<DockPanel
Name="ParentElement"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<!--implicit: <DockPanel.Children>-->
<ListBox DockPanel.Dock="Top">
<!--implicit: <ListBox.Items>-->
<ListBoxItem>
<TextBlock>Dog</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>Cat</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>Fish</TextBlock>
</ListBoxItem>
<!--implicit: </ListBox.Items>-->
</ListBox>
<Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
<!--implicit: </DockPanel.Children>-->
</DockPanel>
このツリー構造には、UIに関するノードとデータに関するノードが混じっています。 XAMLではこのような階層構造を論理ツリーと呼びます。
UIに関するノードだけを含めたものをビジュアルツリーと呼びます。
これらツリー構造はXMALに記述された構造がすべてではありません。
Buttonノードは次のようなビジュアルツリーを持ちます。
<Button>
<Chrome>
<ContentPresenter>
<StackPanel>
<Image />
<TextBlock />
</StackPanel>
</ContentPresenter>
</Chrome>
</Button>
VisualStudioのフォームエディタにはツールバーにButtonコントロールがあり、それを貼り付けるだけでボタンが表示されますが、ButtonコントロールはさらにImageコントロールやTextBlockコントロールによって構成されていることがわかります。
VisualObject
コントロールやコンテナなどUIのレイアウトや操作に関する機能を持つコンポーネントをVisualObjectと呼びます。
XAMLによるフォームのレイアウトで、ビジュアルツリーはVisualObjectのノードだけ抽出してできたツリー構造を示します。
VisualObjectはSystem.Windows.Media.Visualクラスをスーパークラスにもつクラスのことです。