nuits.jp blog

C#, Xamarin, WPFを中心に書いています。Microsoft MVP for Visual Studio and Development Technologies。なお掲載内容は個人の見解であり、所属する企業を代表するものではありません。

【メモ】Xamarin.FormsでPopModalしてもStackからPageが解放されない問題と、限定的な回避策

あくまでメモです。

  • 検証などが不十分なので、鵜呑みにはしないでください
  • ここでの回避策は特定の利用方法における限定的な回避策です
  • ModalStackが適切な状態になるわけではありません

さて

ModalStackからPageをPopすると、画面は前画面に戻るが、ModalStackにPageが残ったままになる

という問題が発生する事がある、という情報をある人から伺いました。

このことは、PageがModalStackからPopされた時に、PopされたPageと、結果表示されるPageにイベントを通知したいような場合に問題が発生します。
今回はこの時に該当問題の回避方法についてメモを残しておきます。

ModalStakの戻る処理において、アプリケーションからの処理と、AndroidやWin10mの物理Backボタンを押下され、画面が戻った後の処理を包括的に扱う場合、Xamarin.FormsではApplicationクラスのModalPoppedイベントを利用する事は適切な手段の一つです。
ModalPoppedでは、「PopされたPage」しか通知されないため、「Popされた結果、表示状態になったPage」を取得したいとすると、通常はModalStackの末尾のページを利用すればよいでしょう(ModalStackが空ならMainPage)。

こんなイメージです。

public App()
{
    ModalPopped += OnModalPopped;
    ...
}

private void OnModalPopped(object sender, ModalPoppedEventArgs e)
{
    var poppedPage = e.Modal;
    var currentPage = 0 < MainPage.Navigation.ModalStack.Count
        ? MainPage.Navigation.ModalStack.Last()
        : MainPage;
    ...
}

しかし、ModalStackからPageが削除されない可能性を考慮すると、このコードだとcurrentPageが正しく取れない可能性があります。
ではどうすれば良いか?ModalPoppingイベントを活用します。
ModalPoppingではPopされるPageがまだModalStack上に存在するので、その前の画面を取得しておくことでPop後のcurrentPageを確保しておくことができます。(ModalPoppedイベントでは正常時はStackからすでにPageが削除されているため、処理できません)

こんな感じでしょうか?

public App()
{
    ModalPopping += OnModalPopping;
    ModalPopped += OnModalPopped;
    ...
}

private Page _previousPage;
private void OnModalPopping(object sender, ModalPoppingEventArgs e)
{
    int currentIndex = MainPage.Navigation.ModalStack.Count - 1;
    for (; 0 <= currentIndex; currentIndex--)
    {
        var page = MainPage.Navigation.ModalStack[currentIndex];
        if (page == e.Modal)
        {
            break;
        }
    }
    _previousPage = 0 < currentIndex 
        ? MainPage.Navigation.ModalStack[currentIndex - 1] 
        : MainPage;
}

private void OnModalPopped(object sender, ModalPoppedEventArgs e)
{
    var poppedPage = e.Modal;
    var currentPage = _previousPage;
    ...
}

なお、この対処方法はPrism for Xamarin.Formsの次のあたりのコードを参考にしています。
github.com

ご参考にどうぞ。