Xamarin無償化をよそにXamarin.FormsビューとUWPネイティブビューの融合の話
Xamarin 無償化!
//build/ 2016にて
Xamarinが無償化しましたね。
待ちに待ってた情報です。
昨年Windows Phone関連でライセンスもらってたんですが…。
これからも使える!というのは大きいです。
ま、今回はそれはそれとして…。
Xamarin.Forms
Xamarin.Formsというのは、iOS / Android / WindowsPhoneで共通に使える画面表示の仕組みを実装したものです。
以前は上記3系統でしたが、今ではWindows 8 Store / UWPも対応しているので、デスクトップからモバイルまでまさに1コードで同一の操作感を実現できる仕組みです。
詳しくは、メインサイトや日本語情報等を参考にしてください。
このあたりは先刻ご存知、ということで話を進めます。
さて、今回は基本的にUWPでの話で、サンプルソースはGitHubに置いています。
Xamarin.FormsビューとUWPネイティブビューの融合
Xamarin.FormsでNative Viewを使う
Xamarin.Formsのビューは、UWPの標準ビューを「Xamarin.Formsのビュークラス」と「レンダラークラス」という薄皮二枚かませて他のプラットフォームとの互換性を持たせています。
実際に使用者側が使うのがビュークラス、 ライブラリ作成者側が苦労する 使用者から隠蔽されているのがレンダラークラス、になり、Nativeのビューはレンダラーが作成と保持、ビュークラスに設定したプロパティをNativeのビューに反映させる、という動作をします。
レンダラーはViewRenderer<>を継承したクラスをしています。
これはパラメータに2つのTypeを指定し、前がXamarin.FormsのViewクラス、次がNative Viewクラスです。
この設定によってこのレンダラーがViewとNative Viewを関連づけている、ということを宣言しています。
また、ソースファイルの最初に assembly: ExportRenderer
を書かなければいけません。
これは引数にXamarin.FormsのViewとレンダラーのTypeを取ります。
この宣言によって、Viewとレンダラーを関連づけています。
サンプル(Test1.uwp/MainPage.xaml.cs)
このサンプルはXamarin.FormsでNative Viewを使うだけのものになっています。
まずは8行目、
[assembly: ExportRenderer(typeof(Test1.uwp.XamarinView.NativeView), typeof(Test1.uwp.Native.NativeViewRenderer))]
です。
assembly:
は、ソースの最初に書かなければいけない、ということでここに書いてあります。
この宣言で Test1.uwp.XamarinView.NativeView
のレンダラーは Test1.uwp.Native.NativeViewRenderer
であることを宣言しています。
次に11行目、
public class NativeViewRenderer : ViewRenderer<Test1.uwp.XamarinView.NativeView, Windows.UI.Xaml.Controls.TextBlock>
これで NativeViewRenderer
クラスは Test1.uwp.XamarinView.NativeView
を表示する際に Windows.UI.Xaml.Controls.TextBlock
を使うことを宣言しています。
そして、Xamarin.FormsのViewの NativeView
は72行目で宣言しています。
public class NativeView : View
{
public static readonly BindableProperty TextProperty = BindableProperty.Create("ItemTextWidth", typeof(string), typeof(string), String.Empty, BindingMode.OneWay, null, null, null, null);
public string Text
{
get { return (string)base.GetValue(NativeView.TextProperty); }
set { base.SetValue(NativeView.TextProperty, value); }
}
}
Viewを継承してTextプロパティを宣言しているだけのクラスですね。
このクラスを実際に作っているのは106行目、
Content = new Test1.uwp.XamarinView.NativeView()
{
Text = "Hello, world !!",
},
NativeViewを作ると、自動的にレンダラーが作られ、レンダラーの OnElementChanged
関数が呼ばれます。
これは15行目にあり、その中の19行目からの
if (Control == null)
{
nativeView = new Windows.UI.Xaml.Controls.TextBlock()
{
FontSize = 60,
};
SetNativeControl(nativeView);
}
ここでNative Viewが設定されていなければ作って設定しています。
Xamarin.FormsのViewの子要素としてNative Viewを配置する
これは先の NativeView
を、そのまま子要素を持てるXamarin.Formsのビューの子要素として追加するだけです。
サンプル(Test2.uwp/MainPage.xaml.cs)
今度はTest1.uwpから NativeView
、 NativeViewRenderer
はそのまま、106行からのViewの定義のみ変更しています。
Content = new StackLayout()
{
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Padding = new Thickness(50),
BackgroundColor = Color.Aqua,
Children = {
new Test2.uwp.XamarinView.NativeView()
{
Text = "Hello, world !!",
BackgroundColor = Color.White,
},
},
}
Test1.uwpのViewの作成時に NativeView
の親要素として StackLayout
を追加しました。
これでウインドウ中央、50pxのボーダーに囲まれたテキスト表示になります。
Native Viewの子要素としてXamarin.FormsのViewを配置する
さて。
すでにあるNative Viewや、上記で作ったレンダラーが保持しているNative View に直接Xamarin.FormsのViewを配置するにはどうしたらいいでしょうか。
実は、ViewRendererは Windows.UI.Xaml.Controls.Panel
を継承しているので、Viewからレンダラーを取得して子要素として付け加えるとコントロールツリーの中に追加されて使えるようにはなります。
StackPanel basePanel = new StackPanel()
{
Name = "TextBase",
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
};
Label textLabel = new Label()
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.Start,
HorizontalTextAlignment = Xamarin.Forms.TextAlignment.Center,
VerticalTextAlignment = Xamarin.Forms.TextAlignment.Start,
FontSize = 40,
Text = "Hello, world",
};
LabelRenderer renderer = (LabelRenderer)textLabel.GetOrCreateRenderer();
basePanel.Children.Add(renderer);
こんな感じですね。
ところで、なんで「使えるように は なります」、というような書き方をしているかといいますと。
実はこのままではtextLabelは表示されません。
Windows10 + Visual Studio 2015 Update 2でデバッグ実行してライブビジュアルツリーで見ていただくと、以下のようになります。
TextBase [StackPanel]
の子要素の [Panel]
つまり、LabelRendererのRenderSizeが0x0になっています。
ViewRendererはArrangeOverrideやMeasureOverrideのメソッドを持っており、中でオーバーライドしているはずなのですが、大きさ計算が想定される値を計算していないようです。
nativeView.LayoutUpdated
内でrendererやtextLabelをArrangeしてやるとちゃんと表示されるので、計算するようにしてしまえば動作はさせられますが、せっかく 手抜きのために Xamarin.Formsを使っているのにコードが増えてしまったら本末転倒です。
本当はこれがやりたかったんですが、どうもそういうわけで一旦あきらめたのでした。
Author And Source
この問題について(Xamarin無償化をよそにXamarin.FormsビューとUWPネイティブビューの融合の話), 我々は、より多くの情報をここで見つけました https://qiita.com/yakumomo/items/74be7508b300196c6894著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .