Xamarin
Xamarin.Forms

Xamarin.Formsでページが破棄されたタイミングでViewModelのDisposeなどを実行する

More than 3 years have passed since last update.

Xamarin.FormsでViewModelを使っている場合、そのページが破棄されたタイミングでイベントの購読解除等を行うDispose的な処理を書きたいなと思っていてその方法を調べてみました。

ページが破棄されるのをStackからページがPopされることとすれば、Popに関するイベントを捉えるのが良さそうです。

調べてみるとPushAsyncはNavigationPageのPoppedイベント、PushModalAsyncはApplication.CurrentのModalPoppedイベントで対処できるようです。


ViewModelに適当なインターフェースを適用する

例としてIDisposableを適用。自作インターフェースでももちろん可。

public class ViewModelSample : IDisposable

{
public void Dispose(){
//後始末
}
}


NavigationPageのPoppedに登録

PopAsyncでPopしたページはNavigationPageのPoppedイベントで拾えるのでNavigationPageのコンストラクタ等で処理を登録しておきます。毎回書くのがめんどうならBaseクラスにでも。

public MainNavi() {

this.Popped += (sender, e) => {
(e.Page.BindingContext as IDisposable)?.Dispose();
};
}


Application.CurrentのModalPoppedに登録

PopModalAsyncでPopしたページはApplication.CurrentのModalPoppedイベントで拾えるのでAppクラスのコンストラクタ等で処理を登録しておきます。

public App(){

Application.Current.ModalPopped = (sender, e) => {
(e.Modal.BindingContext as IDisposable)?.Dispose();
};
}


注意事項

PopToRootAsyncを使うと処理対象のページを見失います。

PoppedToRootイベントで拾えそうなんですが、そこで拾えるのは直前のページだけで他のページは参照できません。しかもPoppedToRootはPopした後なのでNavigationStackにも残ってません。なのでPopと特定の処理を合わせたい場合はPopToRooTAsyncは使わないようにするか、同様の動きをする機能を独自に実装するしかないと思います。


補足

iOSではTabbedPageの現在表示中のタブをクリックするとルートページに戻ります。これはPopToRootしているように見えますが実際は1ページずつPopしているようなのでPoppedイベントの方で処理できます。


参考

https://forums.xamarin.com/discussion/57043/viewmodels-not-disposing-on-navigation-pop