しばらくXamarinからは遠ざかっていたのですが、日常の『やるべきこと』が少しずつ片付いてきたので再び手を付けることに。

それにしても、VisualStudio(以下、VS)提供のAndroidエミュレータはかなり便利になりましたね。

以前であれば(私の開発環境のせいかもしれませんが)VS提供のAndroidエミュレータは重すぎて使い物にならず、AndroidStudioで作成したAndroidエミュレータを使用していたものですが…。

久しぶりのAndroidStudio起動で山積していた更新に失敗してしまったためにAndroidStudioを再インストールしなければならない状況に陥り、「それならば試しに…」という事でVS提供のAndroidエミュレータを使ってみてビックリ。

以前までのAndroidStudioで作成したエミュレータと同等かそれ以上のパフォーマンスを発揮してくれました。

これによりAndroidStudioの再インストールも不要、VSのみでXamarin開発をスムーズに行えるようになりました。

さて、そんなこんなで久々にXamarin開発に着手したわけですが、初日から思いっきり躓きました。

やはり普段から触っておかないと感覚が鈍ってしまいますね。コワイコワイ。

今回はそんな久しぶりの躓きから、『MasterDetailPage』に関するお話をご紹介していきます。

※MasterDetailPageとは何ぞや、というお話はリンクのみで私からは割愛させて頂きます。

前提条件

VisualStudioの拡張機能である『Prism Template Pack』を使用してプロジェクトを作成。

各ライブラリのバージョンは以下の通り。

  • NETStandard.Library:2.0.3
  • Xamarin.Forms:4.5.0.495+198-sha.95140943a-azdo.3580428
  • Prism.Unity.Forms:7.2.0.1422

MasterDetailPageの作成

紹介するまででもないかもしれませんが、一応載せておきます。

Viewsフォルダを右クリック、コンテキストメニューの追加から新しい項目を選択します。

『Prism MasterDetailPage(Xamarin.Forms)』を選択し、任意のファイル名を入力して追加をクリックします。

 

以上でMasterDetailPageの作成は完了です。

注意点その① デフォルトのままでは遷移できない

初期画面であるMainPageから、作成したMasterDetailページへの遷移を実装してみます。

<?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="SampleProject.Views.MainPage"
             Title="{Binding Title}">

    <StackLayout VerticalOptions="Center">
        <Button Text="MasterDetailページに遷移"
                Command="{Binding NavigateToMasterDetailCommand}"/>
    </StackLayout>

</ContentPage>
using Prism.Commands;
using Prism.Navigation;
using SampleProject.Views;
using System.Threading.Tasks;

namespace SampleProject.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {

        public DelegateCommand NavigateToMasterDetailCommand { get; set; }

        public MainPageViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            Title = "Main Page";

            NavigateToMasterDetailCommand = new DelegateCommand(async () => await NavigateToMasterDetail());
        }

        public async Task NavigateToMasterDetail()
        {
            await NavigationService.NavigateAsync(nameof(MasterDetail));
        }
    }
}

上記のコードでデバッグ実行してみます。

マウスカーソルの動きが無いので分かりにくいかもしれませんが、ボタンをクリックしてもMasterDetailページに遷移することが出来ません。(試しに他のページへの遷移は…割愛)

さらには例外等も特に発生しません。ここがXamarin開発の難しいところですね。

このMasterDetailページに遷移できない原因を探るべく、以下のようなコードを記述してみました。

using Prism;
using Prism.Ioc;
using SampleProject.ViewModels;
using SampleProject.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace SampleProject
{
    public partial class App
    {
        /* 
         * The Xamarin Forms XAML Previewer in Visual Studio uses System.Activator.CreateInstance.
         * This imposes a limitation in which the App class must have a default constructor. 
         * App(IPlatformInitializer initializer = null) cannot be handled by the Activator.
         */
        public App() : this(null) { }

        public App(IPlatformInitializer initializer) : base(initializer) { }

        protected override async void OnInitialized()
        {
            InitializeComponent();

            //await NavigationService.NavigateAsync("NavigationPage/MainPage");
            await NavigationService.NavigateAsync(nameof(MasterDetail));
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<NavigationPage>();
            containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
            containerRegistry.RegisterForNavigation<MasterDetail, MasterDetailViewModel>();
        }
    }
}

初期起動時の遷移先をMainPageから、作成したMasterDetailページに変更しました。

これでデバッグ実行してみます。すると…。

例外が発生しました。

内容としては読んで字のごとく「MasterDetailページ使うなら、Master(メニューの部分)とDetail(メインとなるページの部分)を定義しておかないとダメだよ」というもの。

ここで作成したMasterDetailページのコードを見返しておきましょう。

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  xmlns:prism="http://prismlibrary.com"
                  prism:ViewModelLocator.AutowireViewModel="True"
                  x:Class="SampleProject.Views.MasterDetail">

    <MasterDetailPage.Master>
        <ContentPage Title="Menu">
            <StackLayout Padding="20">
                <!-- TODO: // Update the Layout and add some real menu items  -->
                <Button Text="ViewA" Command="{Binding NavigateCommand}" CommandParameter="ViewA" />
            </StackLayout>
        </ContentPage>
    </MasterDetailPage.Master>
    
</MasterDetailPage>

メニュー部分であるMasterが定義されていますが、Detailは定義されていないことが分かります。

ということで、新たにContentPageを作成し、Detailを定義してみます。(作成は割愛)

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  xmlns:prism="http://prismlibrary.com" 
                  xmlns:views="clr-namespace:SampleProject.Views"
                  prism:ViewModelLocator.AutowireViewModel="True"
                  x:Class="SampleProject.Views.MasterDetail">

    <MasterDetailPage.Master>
        <ContentPage Title="Menu">
            <StackLayout Padding="20">
                <!-- TODO: // Update the Layout and add some real menu items  -->
                <Button Text="ViewA" Command="{Binding NavigateCommand}" CommandParameter="ViewA" />
            </StackLayout>
        </ContentPage>
    </MasterDetailPage.Master>

    <MasterDetailPage.Detail>
        <NavigationPage>
            <x:Arguments>
                <views:Detail />
            </x:Arguments>
        </NavigationPage>
    </MasterDetailPage.Detail>

</MasterDetailPage>

この状態でデバッグ実行してみると。

MasterDetailページに遷移し、動作も確認することが出来ました。

作成したままのMasterDetailページはDetail部分が定義されていない為、そのままでは遷移できない様です。

注意点その② NavigationPageとの併用は(実質)出来ない

注意点その①で変更したApp.xaml.csを修正し、MainPageからMasterDetailページに遷移するようにしてみます。

MainPageからMasterDetailページへの遷移を確認することが出来ました。が!

赤枠で囲った場所がなんとも気持ち悪い。タブの2段重ねぐらいならまだしも…。

結論から言えば、これはNavigationPageによるもので、NavigationPageを使用しないか、絶対パス指定での遷移を行うことで解消できます。

初期表示からMasterDetailページに遷移するまでの画面数が0であればNavigationPageを使用しない、画面数が1以上存在し、それらの画面で前画面へ戻ることを想定しているのであれば絶対パス指定の遷移を実装するという感じでしょうか。

今回のサンプルで言えば間の画面数が0ですが、絶対パスによる回避策を載せておきます。

using Prism.Commands;
using Prism.Navigation;
using SampleProject.Views;
using System.Threading.Tasks;

namespace SampleProject.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {

        public DelegateCommand NavigateToMasterDetailCommand { get; set; }

        public MainPageViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            Title = "Main Page";

            NavigateToMasterDetailCommand = new DelegateCommand(async () => await NavigateToMasterDetail());
        }

        public async Task NavigateToMasterDetail()
        {
            await NavigationService.NavigateAsync($"/{nameof(MasterDetail)}");
        }
    }
}

実行結果がこちら。

無事にバーが2つ重なることなくMasterDetailページを表示することが出来ました。

まとめ

MasterDetailPageの注意点
  • 作成したばかりの状態では実行しても遷移することが出来ない。
    ⇒Detail部分を定義することで遷移が可能になる。
  • NavigationPageと併用すると上部のバーが2つ重なって表示される。
    ⇒NavigationPageの使用をやめるか、絶対パス指定の遷移により解決できる。

これに加えてビルドエラーとならないコーディングミスにより、久しぶりのXamarin開発は4時間ほどロスしました…。恐るべし。

もう少し、開発のサポート面で改善を期待したいですね。

以上です。

スポンサーリンク