XAML は Microsoft だけのモノじゃないので、動的にロードできるよという話

XAML Advent Calendar 2014 – Qiita
http://qiita.com/advent-calendar/2014/xaml

XAML アドベントカレンダーの 18 日目の記事です。サーバーから動的に XAML をロードして表示する、というネタを考えていたのですが、と、その前に XAML って何?ってのを書き残しておきます。ちょっと前に Xamarin カンファレンスで Xamarin.formsとカスタムコントロールの話 ってのを話しました。Xamarin.Forms が扱うものが XAML ならば、そもそも XAML とは何か?ってところを突き止めてみます。実は、SPEC があって、MS-XAML(PDF) からダウンロードができます。そうです。XAML の仕様って公開されている情報なのです。なので C# とか ECMAScript のように誰でも実装できる仕様なんですね。誰でも作れるということは、XAML をサポートしているのは WPF や WinStore だけでなく、Xamarin.Forms でもよいし、Linux 上の mono でもよいわけです。名前空間としては System.Xaml が割り当てられていますが、内実は諸々なアセンブリが作られています。WPF が System.Xaml を使っているように WinStore が System.Xaml を使えばよかったのでしょうが、WinRT の関係か使っていません(使えないのかもしれません)。そのあたりは Sliverlight も使っていないので、MS-XAML として仕様は公開され統一されているものの、実装はそれぞれにあるという変な状態になっています…が、「仕様」が決まっているから、その「仕様」にあわせて実装していけば、他のプラットフォーム(あるいはフレームワーク)でも似た感じで使える、という「保証」があります。

で、具体的にどういう点で XAML が XML よりも強化されているかというと(見た目は XML なのだから、XDocument だけで扱うことも可能です)、先の MS-XAML のスペックを見ていくと、

  • x:Class で特定のクラスと結びつける
  • x:Name でクラス内のフィールドと結びつける
  • 名前=”{Binding プロパティ名}” で、指定したプロパティに結び付ける
  • Grid.Row のようなドット付きのタグ名が特別扱いになっている

が規定されていることがわかります。XAML と名乗っている場合は、この仕様が実装されているわけで、逆に XAML を名乗るならば、この仕様を実装しなければいけません。また、XAML を利用する側からみれば、プラットフォームに依存せずに上記の仕組みが利用できるという訳です。つまり、WPF の XAML であっても WinStore の XAML であっても、Silverlight の XAML であっても、Xamarin.Forms の XAML であっても、上記の書き方(クラス、フィールド、プロパティへのバインド)は同じになるということです。これは、特定のクラスへのバインドが XAML 内に書かれているので、適当なアセンブリがロードされれば良いということです。つまり、テキスト形式としての XAML ファイルをそれぞれのプラットフォーム(Windows, iOS, Android, Linux の OS に限らず、あるいは、Java や Objective-C の言語を問わず)に持って行って、適当なアセンブリ(あるいはライブラリ)をロードしてやって、アセンブリ内にあるクラスやプロパティに結び付ける(普通はリフレクションを使う)ことができますよ、って話なのです。

まあ、実際のところは、XAML をデータとして扱っているのは Windows Workflow Foundation ぐらいしかなくて、そのあたりの広がりに関しては失敗しているように見えますが。

更に言えば、名前空間として System.Xaml を用意して、この中に XamlXmlReader クラスがあるのだから、WPF とか WinStore とか Xamarin.Forms とか、このクラスを使って実装すればよかったと思われるのですが、皆ばらばらの実装になっています。おそらく、PCL やら WinRT やらの制約があって、それぞれで XAML をロードする実装が組み込まれたのだと思うのですが…ちょっと SPEC があるのに混乱させ過ぎな感じが。

ちなみに Open Source になった .NET Core には System.Xaml が含まれていません。mono には System.Xaml が入っているので、これをベースにして Linux への拡張をしてもよいかもしれません。

そんなわけで、動的に XAML をロードする方法は

 var url = "http://moonmile.net/up/samplepage.xaml"; 
 var cl = new HttpClient(); 
 var xaml = await cl.GetStringAsync(new Uri(url)); 
 var doc = XamlReader.Load(xaml) as Grid; 
 this.Content = doc; 

な風に簡単に書けます。これは HttpClient でサーバーから XAML データを取ってきている例ですが、ローカルリソースに XAML ファイルを置いてロードさせることも可能です。ただし、XamlReader.Load は、x:Name が使えなかったり、クラスを探索するのが自前のアセンブリのみだったりするので、ちょっと使い勝手が悪いです。逆に、XamlXmlReader を使えばいいんじゃないかと思ったりもするのですが、実は WinRT のほうには System.Xaml がありません。このあたり、完全に View として切り替え可能な XAML と、バインディング先としてのアセンブリ(オブジェクト)は、もう少し煮詰めて考えてもいい気がしています。ちなみに、少々頓挫中の XFormsPreviewer ですが、先の System.Xaml に則ったスタイルに書き直そうかなと思案中です。ドット付きタグ名のあたりが独自処理になってしまっているので、mono の System.Xaml を参考にする感じに。

カテゴリー: XAML パーマリンク