仕事でタブページをあれこれしていた時のことです。

タブページに別定義のページ(以降子タブ)を埋め込んだ際に、子タブのコンストラクタ⇒タブページのコンストラクタの順に処理が行われます。

子タブページの描画が、タブページ生成時点で完了するんですが、子タブへの遷移で再描画(一部初期化)されてしまうケースがありました。

また、子タブへの遷移時にある処理を行いたいという要望もあったため、タブ選択(遷移)時のイベントを検知することにしました。

調べたところ、PrismにIActiveAwareという便利なインターフェースが用意されていました。

TabbedPageの基本あれこれ

NavigationPageとの併用

各所でご紹介されていると思うので、コードのみ載せておきます。

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
            xmlns:view="clr-namespace:ActiveAwareSample.Views"
            prism:ViewModelLocator.AutowireViewModel="True"
            x:Class="ActiveAwareSample.Views.PrismTabbedPage1">
    <NavigationPage Title="Page1">
        <x:Arguments>
            <view:PrismContentPage1/>
        </x:Arguments>
    </NavigationPage>
    <NavigationPage Title="Page2">
        <x:Arguments>
            <view:PrismContentPage2/>
        </x:Arguments>
    </NavigationPage>
    <NavigationPage Title="Page3">
        <x:Arguments>
            <view:PrismContentPage3/>
        </x:Arguments>
    </NavigationPage>
</TabbedPage>

TabbedPageからの遷移方法

※NavigationPageとして定義した場合の遷移方法です。

using ActiveAwareSample.Views;
using Prism.Navigation;
using System.Threading.Tasks;

namespace ActiveAwareSample.ViewModels
{
    public class PrismContentPage1ViewModel : ViewModelBase
    {
        public PrismContentPage1ViewModel(INavigationService navigationService) : base(navigationService)
        {

        }

        public async Task NavigateToPage4()
        {
            // タブ内で遷移する場合
            await base.NavigationService.NavigateAsync(typeof(PrismContentPage4).Name);

            // タブごと遷移する場合
            await base.NavigationService.NavigateAsync("NavigationPage / PrismTabbedPage1/PrismTabbedPage4");
        }
    }
}

タブページごと遷移する場合にはルートからのModal再生成が必要になります。

その為、タブページの表示にかかる処理を遷移の際にもう一度走らせなければなりません。

※この辺もっといい方法があったら教えて頂きたいです…。

タブ選択時のイベント検知(IActiveAwareの実装)

子タブページにIActiveAwareを実装するだけで出来ます。

using ActiveAwareSample.Views;
using Prism;
using Prism.Navigation;
using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ActiveAwareSample.ViewModels
{
    public class PrismContentPage1ViewModel : ViewModelBase, IActiveAware
    {
        public PrismContentPage1ViewModel(INavigationService navigationService) : base(navigationService)
        {

        }

        private bool _isActive;
        public bool IsActive
        {
            get { return _isActive; }
            set
            {
                if (value) Debug.WriteLine($"{typeof(PrismContentPage1).Name} is Active!");
                SetProperty(ref this._isActive, value);
            }
        }

        public event EventHandler IsActiveChanged;

    }
}

【情報求ム】IActiveAware実装における落とし穴(?)

バグなのか、私の実装がおかしいのか、MasterDetailPageのDetailでタブページを定義した際にIsActiveの変更通知を受け取ることが出来ません。

※原因(MasterDetailPage)すら特定できず、会社では丸一日悩んでました。

<?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="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
                  xmlns:view="clr-namespace:ActiveAwareSample.Views"
                  prism:ViewModelLocator.AutowireViewModel="True"
                  x:Class="ActiveAwareSample.Views.PrismMasterDetailPage1">

    <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>
            <NavigationPage.ToolbarItems>
                <ToolbarItem Icon="ic_launcher.png"/>
            </NavigationPage.ToolbarItems>
            <x:Arguments>
                <view:PrismTabbedPage1/>
            </x:Arguments>
        </NavigationPage>
    </MasterDetailPage.Detail>
    
</MasterDetailPage>

回避方法としてはMasterDetailPageのDetail部分にダミーページを定義し、ダミーページからタブページへと遷移した際にはIsActiveの変更通知を受け取ることが出来ました。

ただこの実装方法では無駄なクラスが増えてしまうため、得策とは言えません。

私の実装に誤り等ある場合、指摘いただけたら幸いです。

※とりあえず、仕事ではダミーページ使います。

次回はタブ選択検知を必要になった理由である『GoogleMaps』あたりをご紹介しようと思います。

以上です。

スポンサーリンク