前回はデバイス固有の電話機能を使ってみました。
Problem
Xamarin.Formsの2.2から各OS固有のコントロール (Native Control)をSharedプロジェクトから生成して、ContentView.ContentやLayout.Childrenに埋め込むことができるようになりました。
このあたりの説明は、P3PPP様の下記の記事が参考になります。
で、ポイントは、Sharedプロジェクトで**#ifディレクティブ**で分岐するとかする必要があるとのこと。
でも、思うわけですよ。C++ならともかく、モダンなC#でそんなことしたくないわけですよ。
ディレクティブだらけのソースなんか保守性低下するのは目に見えていますから。
で、思いました。
PCLから、Native Controlを使えないか?って。
Resolution
仕組みは単純で、前回話したDependencyServiceを上手く使うだけです。
各プロジェクトでNative Controlを生成して、それをPCL側で使用するだけです。
ただし、当然Native Controlは、そのままでは、Xamarin.FormsのUIに追加できません。
Xamarin.Forms.Viewに変換する必要があります。
この変換は拡張メソッドのXamarin.Forms.Platform.iOS.LayoutExtensions.ToView、Xamarin.Forms.Platform.Android.LayoutExtensions.ToView、Xamarin.Forms.Platform.UWP.LayoutExtensions.ToViewを使用して、Native ControlをViewに変換します。
今回のソースは下記になります。
PCL
最初にPCLの説明をします。
Serviceのinterface
3つのデバイスのNative Controlを生成するためのinterfaceであるINativeControlServiceを定義します。
INativeControlService.cs
1 | namespace Xamarin.Forms.Portable9.Services |
各プロジェクトはPCLを参照しているので、このinterfaceを実装して各固有機能へのアクセスを提供します。
View
Viewです。
今回はソースから追加するので、
MainPage.xaml
1 | <?xml version="1.0" encoding="utf-8" ?> |
Contentを空にしました。
このMainPageの生成は、PCLのApp.csで実施します。
App.cs
1 | using Xamarin.Forms.Portable9.Services; |
iOS
PCLで定義したインターフェースを実装します。
NativeControlService.cs
1 | using UIKit; |
Android
NativeControlService.cs
1 | using Android.Widget; |
UWP
NativeControlService.cs
1 | using Windows.UI.Xaml.Controls; |
実行してみる
iOS
Android
UWP
**Layout cycle detected. Layout could not complete.**という例外を投げたため実行できませんでした。何故だ….
Conclusion
Sharedプロジェクトでしか使えない、というのはディレクティブの関係なので、固有のUI生成を各プロジェクトに任せてしまえば、どうとでもなります。
XAMLで使えるように工夫次第でできるはずです。
ContentViewの派生クラスのコンストラクタでDependencyService経由で生成したNative Controlをラップすれば、それをXAMLから使うことができるでしょう。
Source Code
https://github.com/takuya-takeuchi/Demo/tree/master/Xamarin/09_Xamarin.Forms.Portable9