nuits.jp blog

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

Prism for WPF で非表示となったViewのインスタンスを破棄する設定

Prism for WPFではRegionという機能を利用することで、画面遷移実装する事ができます。具体的にはこちらをご覧ください。

さて、Regionを利用して画面遷移した場合、デフォルトの振る舞いではViewやViewModelのインスタンスは、一度生成されると以後はキャッシュされて再利用されます。この振舞は、画面のプロパティの初期化もれによる不具合を引き起こしがちです。*1

このため、個人的にはパフォーマンスを特別に気にする個所を除いて、インスタンスのキャッシュを無効化したいと思う事が良くあります。

本稿では非表示となったViewのインスタンスを破棄する方法を、二つのユースケースにたいして実現方法を紹介したいと思います。

非アクティブになったViewのインスタンスをすべて破棄する方法

この方法を利用した場合、前に遷移(RequestNavigate)した場合も、戻った(JournalのGoBackした)場合でも、非表示となった画面は破棄される点に注意してください。

手段は二つあります。

  1. ViewかViewModelの何れかでIRegionMemberLifetimeインターフェースを実装し、KeepAliveプロパティの値をfalseにする
  2. ViewかViewModelの何れかにRegionMemberLifetimeAttributeを追加し、KeepAliveプロパティの値をfalseにする

1.のコードは次のように

public class MainWindowViewModel : BindableBase, IRegionMemberLifetime
{
    public bool KeepAlive { get; } = false;
}

2.のコードは次のように実装します。

[RegionMemberLifetime(KeepAlive = false)]
public class MainWindowViewModel : BindableBase
{
    ...
}

個人的にはKeepAliveを変更する可能性があるのであればIRegionMemberLifetimeを、変更する必要がないのであればRegionMemberLifetimeAttributeを利用するのが好みです。実装先はViewModelでしょうか。

戻る時だけ破棄したい場合

さて、先の実装だと常にViewもViewModelも再生成されます。

ですが例えば、A画面からB画面へ遷移し、B画面からA画面へ戻った場合、B画面のインスタンスは破棄したいが、A画面のインスタンスは維持したいというケースも多いはずです。

これを実現する方法としては、ViewModel(もしくはView)でIRegionMemberLifetimeを実装し、戻る処理時のみKeepAliveをfalseにするように実装すれば解決できるでしょう。

具体的な実装方法は色々考えられますが、ここでは共通のViewModelの基底クラスを作成する方法を紹介しましょう。

基底のViewModelとして次のようなクラスを作成します。

public abstract class ViewModelBase : BindableBase, IRegionMemberLifetime
{
    public bool KeepAlive { get; private set; } = true;

    protected void Pop(IRegionManager regionManager, string regionName)
    {
        var journal = regionManager.Regions[regionName].NavigationService.Journal;
        if (journal.CanGoBack)
        {
            KeepAlive = false;
            journal.GoBack();
        }
    }
}

戻る処理を実装するViewModelでこのクラスを継承し、Popメソッドを呼べば戻った際に、ViewとViewModelのインスタンスが破棄されるようになります。

という訳で今回はここまで。それではまた!

*1:何らかの登録処理を連続して行うような業務アプリケーションを想像してください。メニュー画面から登録画面に遷移し、登録完了後メニュー画面に戻るとします。続けて登録画面に遷移すると、登録画面とそのViewModel(およびそれらが参照しているインスタンス)は再利用されるため、登録後から再度表示するまでの間に初期化処理していないと、以前の登録内容が表示されてしまうといった不具合を引き起こします。