nuits.jp blog

C#, Xamarin, WPFを中心に書いています。Microsoft MVP for Development Technologies。

【Xamarin.Forms】汎用的なBehavior集ライブラリを作ってる

ふと思い立って、汎用的なBehaviorライブラリを作ってます。

とりあえず作っているのは

  • Eventを指定してEventが発生したら何かする抽象Behavior
  • Eventが発生したらCommandを実行するBehavior
  • DisplayAlertを表示してCommandを実行するBehavior
  • ActionSheetを表示してCommandを実行するBehavior

探せばありそうな気もしますし、一部Prismにも含まれていたりしますが、.NET Standardの学習もかねて作ってます。
まだ作ってる途中で全然テストとか書いてないけどw

動作感としてはこんな感じです。

f:id:nuitsjp:20170610193344g:plain

ちょっと見にくいけど選択した行が、下のSelectedItemに表示されていたりします。
まぁ、そこはBehaviorでやるほどではありませんが。
気になった方は良かったら続きをぜひ!

続きを読む

【メモ】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

ご参考にどうぞ。

Xamarin.FormsでAndroidやiOSで例外がさっぱりわかんねえよ!って時の一つの解決方法

なんだかVisual Studio 2017になって、例外が余計に分かりにくくなったような気がするのは気のせいでしょうか?
本当、どうでもいいことでミスしていて(MasterDetailPageのMaster側のPageにTitleを設定するのを忘れていた。。。とか)、でもそれに気が付けないってことがままある気がします。

そんな時、もしかしたらこれで一発解決することがあるかもしれません。
方法は簡単です。

UWPのローカルマシンでデバッグしてみる。

そうすると、例外が人間に読める感じで出力されることもある気がします。 てことで、困ったときはダメ元でお試しあれ。

デフォルトでUWPのビルドと配置が有効になっていない点にご注意ください。

今日はそれだけです!
ではまた~。

Xamarin.Forms Navigation Overview

久しぶりにJXUGでしゃべらせていただきました。

完全に物理バックボタンを忘れていたので、そのうちどこかで追記なり補足します。

jxug.connpass.com

発表に利用した資料はこちらに公開しています。

www.slideshare.net

ただ、スライドだけで理解いただけるものを作るスキルがないので、ここに話した内容も要約して載せておこうと思います。
良かったらご覧ください。

続きを読む

【メモ】INavigationがPlatformによって実装が異なる話

あくまでメモでなのでご注意を

  • 網羅的な説明になっていません
  • 内容の正誤を十分検証していません

つぎのようなコードを呼ぶとき

page.Navigation.PushAsync(new NewPage());

NavigationプロパティはINavigationインターフェースの実装クラスで、その実態はNavigationProxyクラスです。
NavigationProxyはあくまでProxyクラスで、実体?はInnerプロパティに設定されています。

Xamarin.Forms/NavigationProxy.cs at b9b9d2536ff0cd6ae4526356d663706c721e481f · xamarin/Xamarin.Forms · GitHub

画面遷移するたびにProxyは作られて、Innnerは遷移前から遷移後へ受け渡されていきます。

実体は大きく二つあって、Modal遷移の場合はApplicationクラスの内部クラスのNavigationImplクラスが

Xamarin.Forms/Application.cs at e6d5186c8acbf37b877c7ca3c77a378352a3743d · xamarin/Xamarin.Forms · GitHub

Hierarchical遷移(つまりNavigationPage)の場合はNavigationPageの内部クラスのNavigationImplクラスが使われます。

Xamarin.Forms/NavigationPage.cs at 070f1dcdb50b47c7abdebf91010626d55881a8fc · xamarin/Xamarin.Forms · GitHub

でまぁ、これで終わりじゃないのがめんどうくさくって、ApplicationクラスのMainPageプロパティの設定時にNavigationImpleのさらにInnnerへ各プラットフォーム別の実装が設定されます。
Androidの場合はここ

Xamarin.Forms/Platform.cs at e6d5186c8acbf37b877c7ca3c77a378352a3743d · xamarin/Xamarin.Forms · GitHub

各PlatformクラスがINavigationを実装しているんですね。

つまりINavigationの振る舞いは、当然と言えばそれまでですがプラットフォームによって異なります。
動作もちょっと違います。
私が知っている範囲だと、Modal遷移した時のNavigationStackの要素数が0だったり1だったりプラットフォームによって違う気がしています。

この辺りちゃんと調べて、必要であれば貢献したいですね。
以上です。

【Xamarin.Forms】DisplayAlertを表示するBehavior

みなさんWPF用のMVVMインフラストラクチャであるLivetをご存知ですか?

ふとしたきっかけで、Livetの機能を見ていて
「指定されたEventでダイアログを表示し、その後ViewModelをよびだす」
機能が、Xamarin.Formsでも使えると便利だなと思いました。
そこで指定EventでDisplayAlertを表示しAcceptボタンが押されたときにCommandを事項するBehaviorを作成してみました。

続きを読む