手続き型言語にどっぷり浸かっているせいか、宣言型の言語というものはどうもわかりにくい。SQL 然り、XAML 然り。先ず、XAML でわかりにくいと思ったのは「子要素の扱い」だ。
Code 1
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="200" Width="200" >
Hello World
</Window>
Code 2
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="200" Width="200" >
<Button>Hello</Button>
<Button>World</Button>
</Window> Code 1 は、Window に "Hello World" と表示しているだけ。Code 2 はコンパイルエラーになる。Window が持てる子要素は 1 つだけなのだ。次は、Window に Grid を持たせ、Button を 2 つ配置した。
Code 3
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="200" Width="200" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0">Hello</Button>
<Button Grid.Row="1">World</Button>
</Grid>
</Window> Gird にはいくつでも子要素を持たせることができる。
Window に子要素を設定すると Window.Content に設定され、Grid に子要素を設定すると Grid.RowDefinitions の 1 要素となる(※1)。この様に、子要素は親要素の「どれに設定される」のか決まっていないが、XAML コンパイラはそれをどうやって判断しているのだろうか?
XAML コンパイラは子要素の取り扱いを親要素に一任している。その際に肝となるのが IAddChild インターフェースだ。IAddChild インターフェースを持っているクラスは子要素を持つことができ、IAddChild インターフェースを実装してさえいれば、親要素は子要素を好きなように扱える。Window の場合は、AddChild メソッドに渡された Object を Content に設定しているのだろう。
この事実を知っていると XAML の構造がすっきりと理解できる。
また、XAML はコレクションのようなものも扱えるので(※2)、ICollection インターフェースを実装しているクラスも子要素を持てるみたいだ。
※1 厳密には、Grid.RowDifinitions の要素となるのは RowDifinition のインスタンス。
※2 XAML は UI 専用言語ではない