動的にXamarin.FormsのXAMLファイルをロードするときのライブラリとして使っている、Xamarin.Forms.XamlProvilder を NuGet で公開しました。
NuGet Gallery | Xamrin.Forms Xaml Provider 0.1.1
https://www.nuget.org/packages/Xamarin.Forms.XamlProvider/0.1.1
XAMLの文字列から ContentPage 等を作るので、こんな風にXAML形式のデータをC#のコード内に埋め込んで記述ができます。
public void LoadSample() { string xml = @"<?xml version='1.0' encoding='utf-8' ?> <ContentPage xmlns='http://xamarin.com/schemas/2014/forms' xmlns:x='http://schemas.microsoft.com/winfx/2009/xaml' x:Class='XFormsPreview.NewPage'> <StackLayout> <Label Text='New Page' /> <Button Text='Click me' /> </StackLayout> </ContentPage>"; var page = PageXaml.LoadXaml<ContentPage>(xml); Assert.IsNotNull(page); var layout = page.Content as StackLayout; Assert.IsNotNull(layout); Assert.AreEqual(2, layout.Children.Count);
文字列から動的に変換するので、ファイルリソースをプロジェクトに埋め込ませて読み込むこともできます。
[TestMethod] public void GridDemoPage() { var fs = File.OpenText(@"XamlGridDemoPage.xaml"); var xaml = fs.ReadToEnd(); var page = PageXaml.LoadXaml<ContentPage>(xaml); Assert.IsNotNull(page); Assert.AreEqual("Grid Demo Page", page.Title ); Assert.AreEqual(new Thickness(0, 20, 0, 0), page.Padding);
これを HTTP 経由で拾ってくるようにしたのが XFormsPreviewer です。
async void OnClickSample0(object sender, EventArgs e) { var hc = new HttpClient(); try { var res = await hc.GetStringAsync("http://localhost:10150/get/0"); var page = PageXaml.LoadXaml(res); await this.Navigation.PushAsync(page); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); } }
当然、HTTP 経由で拾ってくるので、インターネット上に XAML ファイルを配置しておけば、アプリケーションの更新なしに、動的に Xamarin製 XAML ファイルをダウンロードして表示を変えることが可能です…が、いまのところ、プレビューの機能しかないので静的ページしか表示できません。この部分はちょうど Javascript 無しの WebView みたいなものです。
■名前を取得する
ver.0.1.1 では、x:Name で Element を参照できるようにしました。
public void TestName() { string xml = @"<?xml version='1.0' encoding='utf-8' ?> <ContentPage xmlns='http://xamarin.com/schemas/2014/forms' xmlns:x='http://schemas.microsoft.com/winfx/2009/xaml' x:Class='XFormsPreview.NewPage'> <StackLayout> <Label x:Name='label1' Text='New Page' /> <Button x:Name='button1' Text='Click me' /> </StackLayout> </ContentPage> "; var page = PageXaml.LoadXaml<ContentPage>(xml); Assert.IsNotNull(page); var layout = page.Content as StackLayout; var label1 = page.FindByName<Label>("label1"); var button1 = page.FindByName<Button>("button1"); Assert.IsNotNull( label1 ); Assert.AreEqual("New Page", label1.Text); Assert.AreEqual("Click me", button1.Text); }
XAML内に x:Name で名前を付けておくと、Xamarin.Forms や WPFなどでは、それぞれのプロパティにしてくれますが、動的にXAMLファイルをロードする場合は直接プロパティを参照しづらいので、FindByName<Label>(“name”) のように参照します。FindByName 自体は Xamarin.Forms にもあるし、Silverlight でよく使うパターンです。
こうすると、固定で Xamarin.Forms を作ったときと同じように、各種のプロパティが設定できます。
var page = PageXaml.LoadXaml<ContentPage>(xml); var label1 = page.FindByName<Label>("label1"); label1.Text = "change display";
■BindingContent を実装中
ただし、x:Name でコントロールのオブジェクトを取ってくると、動的にロードされた XAML との接点が多くなってしまうので、もっと沿結合にするため BindingContext の部分を実装中です。MVVM パターンにしたがって、
INotifyPropertyChanged インターフェースと ICommand インターフェースが実装されていれば、ViewModel がそのまま利用できる感じになる予定です。