 
Xamarin逆引きTips
Xamarin.Formsでカスタムダイアログを表示するには?(MessagingCenter利用)
Xamarin.Formsで共通のAPIが提供されているダイアログ(=iOSのUIAlertView/AndroidのAlertDialog)以外のプラットフォーム個別のダイアログを表示する方法を解説。その呼び出しにはMessagingCenterが利用できる。
 前回のTipsでは、Xamarin.Formsで、単純なダイアログボックス(DisplayAlert、DisplayActionSheet)やインジケーター(IsBusy)を表示する方法を説明した。これら以外のダイアログを表示したい場合は、独自に実装する必要がある。その実装方法を本Tipsで説明する。プログラムは前回のものを拡張するので、まずは前回のTips内容を実装してほしい。
■
 Xamarin.Formsに用意されているのは、前回説明したDisplayAlertとDisplayActionSheetの2つのダイアログのみだ。Androidには、他にもListDialogやProgressDialogなど、さまざまなダイアログが存在する。
 これらのダイアログを使用する場合は、プラットフォーム固有の実装をするしかない。Xamarin.Formsからプラットフォーム固有の機能を利用するには?では、DependencyServiceを利用したが、今回はもう1つの解決手段であるMessagingCenterを利用する方法を解説する。
MessagingCenterは、MVVMパターンで主に使われる「Messenger」と呼ばれるオブザーバーパターンの機能を提供するものだ。ここでの使い方は、固有のプラットフォーム側が「監視者」となり、Xamarin.Forms側からのメッセージを受け取った時にプラットフォーム固有の処理を行う、というものだ。
MessagingCenterを利用して、カスタムダイアログを表示する例として、プログレスダイアログを表示する処理をiOSとAndroidで実装する。
1. Androidでプログレスダイアログの表示を実装する
 AlertDialogSample.AndroidプロジェクトのMainActivity.csファイルで、メッセージを監視し、通知を受け取ったらProgressDialogを表示する処理を記述する。
| ……省略…… [Activity(Label = "AlertDialogSample.Android.Android", MainLauncher = true)] public class MainActivity : AndroidActivity {   private const int DIALOG_ID_PROGRESS = 1;   protected override void OnCreate(Bundle bundle)   {     base.OnCreate(bundle);     Xamarin.Forms.Forms.Init(this, bundle);     MessagingCenter.Subscribe<MainPage, bool>(this, "progress_dialog", //←1     (page, isVisible) =>            //←2     {       this.RunOnUiThread(() =>      //←3       {         if (isVisible)          {           this.ShowDialog(DIALOG_ID_PROGRESS);         }         else         {           this.DismissDialog(DIALOG_ID_PROGRESS);         }       });     });     SetPage(App.GetMainPage());   }   protected override Dialog OnCreateDialog(int id) //←4   {     if (id == DIALOG_ID_PROGRESS)     {       var dialog = new ProgressDialog(this);       dialog.SetMessage("Now progress...");       dialog.SetCanceledOnTouchOutside(false);       dialog.SetCancelable(false);       return dialog;     }     return base.OnCreateDialog(id);   } } | 
 メッセージの監視を行うのが1のSubscribeメソッドだ。ここでは「MainPageから送信される“progress_dialog”というIDのメッセージ」を監視している。そのメッセージに添付されるデータの型はboolだ。
 2は、メッセージを受信した時の処理を表す。メッセージはUIスレッドから送信されるとは限らないので、3でUIスレッドで処理が実行されるようにし、bool値に応じてShowDialogメソッドまたはDismissDialogメソッドを呼び出す。
 ProgressDialogの表示は、AndroidのActivityライフサイクルにのっとって4のOnCreateDialogメソッド内で行う。
2. Xamarin.Forms側でプログレスダイアログを表示する(=メッセージを送信する)
 AlertDialogSampleプロジェクトに戻り、MainPage.csファイルにボタンを1つ追加し、クリックされた時の処理でプログレスダイアログを表示させるためのメッセージ送信を行う。
| ……省略…… public class MainPage : ContentPage {   public MainPage()   {     ……省略……    var buttonProgress = new Button     {       Text = "Show ProgressDialog"     };     buttonProgress.Clicked += async (sender, e) =>      {       MessagingCenter.Send(this, "progress_dialog", true); //←1       await Task.Delay(3000);       MessagingCenter.Send(this, "progress_dialog", false);     };     this.Content = new StackLayout     {       ……省略……       Children =       {         buttonDialog1,         buttonDialog2,         buttonIndicator,         buttonProgress,       }     };   } } | 
 メッセージを送信する処理が1のMessagingCenter.Sendメソッドだ。第1引数が送信元のオブジェクトであり、前述のSubscribeメソッドの第1型パラメーターと一致させる必要がある。第2引数はメッセージのID、第3引数がメッセージに添付するデータだ。
この一連の処理は、「ダイアログを表示する→3秒待つ→ダイアログを非表示にする」となる。
3. Androidで実行する
ここまでのプログラムをAndroidで実行し、[Show ProgressDialog]ボタンを押すと、次の画面のようになり、3秒後にプログレスダイアログは消える。

4. iOSでプログレスダイアログの表示を実装する
次に、iOS側にもプログレスダイアログの表示を実装する。CocoaTouchには、AndroidのようなProgressDialogは存在しないので、Objective-C言語によるライブラリSVProgressHUDをXamarin.iOSで使用できるようにしたBTProgressHUDを使用する。
5. BTProgressHUDをプロジェクトに組み込む
次の画面に示すように、AlertDialogSample.iOSプロジェクトの[Components]フォルダーの右クリックメニューから[Get More Components]を選択し、それにより表示される[Xamarin Components]画面から「BTProgress」と検索して表示される「BTPogressHUD」を選択する。
表示される画面で[Add to App]ボタンを押すと、プロジェクトに追加される(次の画面)。
6. iOSでプログレスダイアログの表示を実装する
 AppDeletagate.csファイルで、Androidと同様にメッセージを監視し、BTProgressHUDを使用してプログレスを表示する処理を記述する。
| ……省略…… [Register("AppDelegate")] public partial class AppDelegate : UIApplicationDelegate {   UIWindow window;   public override bool FinishedLaunching(UIApplication app, NSDictionary options)   {     Forms.Init();     MessagingCenter.Subscribe<MainPage, bool>(this, "progress_dialog", //←1     (page, isVisible) =>     {       this.InvokeOnMainThread(() =>        {         if (isVisible)          {           BTProgressHUD.Show("Now progress...", -1, ProgressHUD.MaskType.Black);         }         else         {           BTProgressHUD.Dismiss();         }       });     });     window = new UIWindow(UIScreen.MainScreen.Bounds);     window.RootViewController = App.GetMainPage().CreateViewController();     window.MakeKeyAndVisible();     return true;   } } | 
 1が追記した処理であるが、ほぼ説明は必要ないだろう。iOSではUIスレッドでの実行をInvokeOnMainThreadメソッドに指定したデリゲートのメソッド内で行うことと、ProgressDialogに代わりBTProgressHUDを使用している箇所がAndroidと異なるだけだ。
7. iOSで実行する
ここまでのプログラムをiOSで実行し、[Show ProgressDialog]ボタンを押すと、次の画面のようになり、3秒後にプログレスダイアログは消える。

まとめ
 Xamarin.Formsで、標準提供のダイアログボックス以外を使うには、プラットフォーム固有の実装を行う必要があり、その呼び出しにはMessagingCenterが利用できる。
 MessagingCenterは簡単に使用できるが、プラットフォーム側と頻繁にデータの受け渡しをするような機能には、「Tips:Xamarin.Formsからプラットフォーム固有の機能を利用するには?」で解説したDependencyServiceを使うべきだろう。
MessagingCenterの公式な解説はXamarin DevelopersのXamarin.Forms(英語)を参照されたい。
※以下では、本稿の前後を合わせて5回分(第8回~第12回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
 
8. Xamarin.Formsからプラットフォーム固有の機能を利用するには?(DependencyService利用)
UIを共通化するフレームワーク「Xamarin.Forms」で、「DependencyService」機能を使用してiOS/Androidの各プラットフォーム固有の機能を実装する方法を解説する。
 
9. Xamarin.Formsでダイアログボックス(とBusyインジケーター)を表示するには?
ダイアログ(=iOSのUIAlertView/AndroidのAlertDialog)や、処理実行中を示すBusyインジケーターを、Xamarin.Formsで表示する方法を解説する。これらは共通のAPIを使って実装できる。
 
10. 【現在、表示中】≫ Xamarin.Formsでカスタムダイアログを表示するには?(MessagingCenter利用)
Xamarin.Formsで共通のAPIが提供されているダイアログ(=iOSのUIAlertView/AndroidのAlertDialog)以外のプラットフォーム個別のダイアログを表示する方法を解説。その呼び出しにはMessagingCenterが利用できる。
 
11. Xamarin Studio(Mac版)で複数のソリューションを開く/複数起動するには?
Macで開発中に、複数のソリューションをXamarin Studioで開く方法と、複数のXamarin Studioを立ち上げる方法を説明する。
 
12. Xamarin.Formsの既存のコントロールを拡張するには?
Xamarin.Formsのコントロールにはプラットフォーム共通の基本的な機能しか含まれていない。既存のコントロールを拡張して、ネイティブ側で機能を追加する方法を解説。





 
  
 
