WPF

【WPF攻略】2.3_ViewModelBaseを実装しよう

本記事では、ReactivePropertyを使うためにとっても役に立つViewModelの基底クラスの実装コードをご紹介しています。

これをやらないと、画面を作るたびに同じコードをたくさん実装する必要があるので、作っておくのがオススメです。

前回の処理で、画面にMahApps.Metroを反映することができました。

なので、さっそく画面の作成を始めていきたいのですが…

その前にやることがあるんです!

とび助

それは何ですか…?

トビタカ

ViewModelの基底クラスを作成することです!

基底クラスとは継承元となるクラスです。共通処理などを実装しておくことで継承先で再利用できるようにします。

親クラスやスーパークラスと呼ばれることもあります。

ViewModelはViewに1対1で対応しています。

なので、画面を作成するたびにViewModelを実装していく必要があるのです。

しかし、各画面に共通する処理を毎回実装するのは手間がかかるうえに、修正が生じた場合は全てのコードを修正する必要があるので、修正漏れなどのリスクが生じます。

なので、基底クラスを作成して1か所にViewModelの共通処理をまとめておくのです。

とび助

修正もしやすいうえに手間もかからなくなるんですね!

トビタカ

はい!では早速実装していきましょう!

ViewModelBase.csを作成する

では、早速プロジェクトのソリューションエクスプローラーを開きましょう。

そこにある①フォルダ『ViewModels』をクリックして選択状態にしてください。

選択状態になったら、『Ctrl』『Shift』『A』を同時に押します。

次の画面『新しい項目の追加』が表示されると思います。

②『クラス』を選択して名前に③『ViewModelBase』と入力し、④『追加(A)』をクリックしてください。

とび助

ViewModelBase.csが追加されました!

トビタカ

では、そこに実装していきますよ!

修飾子を変更して継承元を追加する

先ほど作成した『ViewModelBase.cs』を開いてください。

そしたら、コードを次のように変更しましょう。

トビタカ

6行目の名前空間はそのままにしてくださいね!

using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Reactive.Disposables;

namespace PrismWPF.ViewModels
{
   public abstract class ViewModelBase : BindableBase, IDisposable, IDestructible
   {
   }
}

重要なのは8行目になります。

まずクラスの修飾子がpublic abstractに変わっていますね。

この修飾子を追加することで『継承して使用するクラス』ということを明示できるので、基底クラスとしてしか扱えないようになるのです。

とび助

abstactってそう使うんですね~

トビタカ

基底クラスを作るときに高確率で使用するので覚えておくといいですよ!

つづいて、8行目の継承元に『BindableBase』『IDisposable』『IDestructible』が追加されています。

1つめの『BindableBase』はprismに内蔵されている基底クラスの1つで、ViewModelのクラスに『ViewModelとして動作するための機能を追加するクラス』です。

今回作成するViewModelBaseにもすべてのViewModelに共通する機能を実装させるので、BindableBaseを継承させておきます。

トビタカ

そうすれば、ViewModelBaseを継承するだけで、BindableBaseの機能も使えるようになるのです!

とび助

なるほど!

残りの2つの『IDisposable』と『IDestructible』はリソースを破棄する処理を持っているクラスである事を明示させるためのインターフェースです。

ここで言うリソースとは使用しているメモリなどのこと。

これらを継承しておくことで、ReactivePropertyで使用したリソースを破棄できるようになるので、継承させてリソースを開放するDisposeというメソッドを実装できるようにします。

トビタカ

では、クラスの中身を実装していきますよ!

とび助

頑張って実装します!

リソースの破棄処理を実装する

では、ViewModelBaseの中身を実装していきます。

基本的に下記内容のコードをコピペすることになりますので、まずはコードをお見せしますね。

12~42行目が追加されている部分です。

using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Reactive.Disposables;

namespace PrismWPF.ViewModels
{
    /// ViewModelの基底クラス
    public abstract class ViewModelBase : BindableBase, IDisposable, IDestructible
    {
        /// ReactiveProperty一括破棄用フィールド
        protected CompositeDisposable _disposables = new CompositeDisposable();

        /// 二重破棄防止用フラグ
        private bool _disposed;

          /// リソースの破棄処理
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// リソースの破棄処理の実態
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) { return; }
            if (disposing)
            {
                /* マネージリソースの破棄処理 */
                _disposables?.Dispose();
            }
            try { /* アンマネージドリソースの破棄処理 */ }
            catch { }
            _disposed = true;
        }

        /// ファイナライザ
        ~ViewModelBase() => Dispose(false);

        /// ViewModel破棄
        public void Destroy() => Dispose();
    }
}

とび助

何が実装されているか全然分かりません!

トビタカ

ザックリとご説明しますので、概要を掴んでいただければOKです。

ReactivePropertyは適切にリソースを破棄しないとメモリリークする可能性があります。

ですが、毎回1つずつ破棄処理を実装するとアホみたいに時間がかかるので、フィールド『_disposable』に追加してまとめて破棄する方法が推奨されているんです。

なので、12行目に一括破棄用フィールドの『_disposable』をインスタンス化しておきます。

そして、ViewModelを使わなくなった時に_disposableの中身を一括で破棄するコードを実装していくのですが…、

このままでは2回で破棄されてしまう危険性があるのです。

とび助

二重破棄ってダメなんですか…?

トビタカ

はい!リソースの無駄遣いやセキュリティの問題が起きてしまいます。

詳細は割愛しますが、リソースの二重破棄は多くの問題を発生させる可能性がある処理です。

これを防止するのに役立つのが15行目のbool型のフィールド『_disposed』になります。

破棄が実行された時に『_disposed』をtrueに変更しておくことで、2重破棄を防止するためのフラグとして使用するのです。

トビタカ

では、破棄処理を行うメソッドの実態を追加していきますよ!

次は実際にリソースを破棄するメソッドDisposeを実装していきます。

まず18~22行目はMicrosoftが推奨しているコードなので、そのままコピペしてください。

実際の破棄処理はメソッド『Dispose(引数あり)』に実装しておくことで、継承した各クラスに応じたDisposeを作成できるようにしています。

なお、『GC.SuppressFinalize(this);』のコードは、リソースを自動で破棄する役割を持つGCに、『もう破棄済みなのでファイナライザは実行しないように』と伝える役割を持っています。

トビタカ

GCが2重破棄を引き起こす可能性をつぶしているんです!

とび助

大切な機能みたいなので、そのままコピーしておきますね。

25~36行目がリソース破棄の実際の処理になります。

最初(27行目)にフィールド『_disposed』を確認して、true(破棄済み)であれば破棄処理を行わないようにしています。

そして、28行目で引数を確認し、tureであればGCが管理できるマネージリソースを破棄するようにしているのです。

その後は、使用した外部DLLなどのアンマネージドリソースを破棄する処理をtryの中に実装します。

外部DLLなどのリソースを破棄する際に例外が発生する可能性があるので、Disposeが途中で止まらないようにしておくのです。

そして、最後の処理の35行目でフィールド『_disposed』をtrueにしておくことで、二重破棄が発生しないようにしておきましょう。

トビタカ

あとはオマケだけです!

残りの39・42行目は保険のコードになります。

確実にDisposeを実行するための処理です。

リソースの破棄をやらないとPCの動作などに影響を与える可能性があるので、ファイナライザやDestroy経由でもDisposeが呼び出されるようにしています。

特にDestoryはViewModelを破棄した時に呼び出されるので、画面が不要になったときに確実にDisposeを行えるようになります。

とび助

でも二重破棄はされないんですよね?

トビタカ

はい!フィールドのフラグをチェックするので心配ありませんよ!

おわりに

長くなりましたが、以上でViewModelBaseの実装が完了です。

あとは、これから作成するすべてのViewModelの継承元をBindableBaseからViewModelBaseに変更すれば、簡単にViewModelを作れるようになります。

トビタカ

本当にお疲れさまでした!次回は画面を作る前の最終準備であるリソースファイルを作成します!

とび助

よく分からないけど楽しみにしています!

create_resource_file

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA