読者です 読者をやめる 読者になる 読者になる

C# + WPFで、DataContextChangedイベントハンドラーではなくMessageBoxServiceを使ってMessageBoxを表示する

C# WPF

最近、「ひと目でわかる Visual C# 2013/2012 アプリケーション開発入門」を参考にアプリを作っています。
ひと目でわかる Visual C# 2013/2012 アプリケーション開発入門 (MSDNプログラミングシリーズ)

 
ただ、MessageBoxの表示について、より良さそうな方法があったため、メモを残しておきます。

 
2015/12/10 追記 ここから

公式Blogに以下の記事が掲載されましたので、Microsoft.TeamFoundation.MVVMの使用前に記事を確認してみてください。
Microsoft.TeamFoundation.MVVM 名前空間の利用について - Visual Studio サポート チーム blog - Site Home - MSDN Blogs

2015/12/10 追記 ここまで

 

環境

  • Windows7
  • .NET Framework 4.5
    • Microsoft.TeamFoundation.MVVM名前空間を使ったMVVMのWPFアプリ
    • ViewModelは、Microsoft.TeamFoundation.MVVM.ViewModelBaseを継承したもの
  • 作るもの:ボタンを押したらメッセージを表示する

 
なお、ボタンを押した時のコマンド実行やMessageBoxの表示 (DataContextChangedイベントハンドラー編)の詳細な説明は、上記書籍に説明があるかと思います。

 

ボタンを押した時のコマンド実行について

今回は、以下のようにしてコマンド実行をButton要素へデータバインドしています(詳細な説明は上記書籍を参照)。

 

ViewModel

ICommand型のプロパティを用意し、その中でRelayCommandを使ってコマンド実行のデリゲートを渡し、ExecuteShowCommandメソッドに実際の処理を書きます。

class MessageBoxServiceViewModel : Microsoft.TeamFoundation.MVVM.ViewModelBase
{
    private ICommand _showCommand;
    public ICommand ShowCommand
    {
        get
        {
            if (_showCommand == null)
            {
                _showCommand = new Microsoft.TeamFoundation.MVVM.RelayCommand(ExecuteShowCommand);
            }
            return _showCommand;
        }
    }

    private void ExecuteShowCommand(object x)
    {
        // コマンド実行時の処理
    }
}

 

View

xamlのButton要素へ、ICommand型のプロパティ(ここではShowCommand)をデータバインドして完成です。

<Button Command="{Binding Path=ShowCommand}"></Button>

 

MessageBoxの表示 (DataContextChangedイベントハンドラー編)

処理自体はViewModel内のExecuteShowCommandメソッドの中へ書けばいいのですが、Viewと分離するためにいろいろと追加します。

 

ViewModel

publicなActionデリゲートを用意して、ExecuteShowCommandでActionデリゲートを呼びます。

public Action<string> ShowInformationMessageBox { get; set; }

private void ExecuteShowCommand(object x)
{
    ShowInformationMessageBox("hoge");
}

 

View

コードビハインドで、ViewのMessageBoxとViewModelのActionプロパティを紐付ける、DataContextChangedイベントハンドラーを書きます。

x.ShowInformationMessageBox = (message) => MessageBox.Show(message, "info", MessageBoxButton.OK, MessageBoxImage.Information);

 
この方法でもいいのですが、画面ごとにMessageBox向けのActionデリゲートとコードビハインドを追加しなくてはならず、少々面倒に感じていました。

 

MessageBoxの表示 (MessageBoxService編)

他により良い方法がないかと探してみたところ、フォーラムにgekkaさんのコードがありました。
Microsoft.TeamFoundation.MVVMを利用してViewModelからMessageBoxを表示させる方法

ただ、

var mbs = base.ResolveService<Microsoft.TeamFoundation.MVVM.IMessageBoxService>();
mbs.Show(Name);

ResolveServiceを使って解決している部分が気になりました。

 
Microsoft.TeamFoundation.MVVM.ViewModelBaseに使えるものがないかをMSDNを見たところ、MessageBoxServiceというgetアクセサのみを持つプロパティがありました。
ViewModelBase.MessageBoxService プロパティ (Microsoft.TeamFoundation.MVVM) - MSDN

そこで、MessageBoxServiceによるMessageBoxの表示を試してみます(ようやく本題)。

 

ViewModel

MessageBoxServiceプロパティを使うことで、ExecuteShowCommandメソッドは簡潔になります。

private void ExecuteShowCommand(object x)
{
    MessageBoxService.ShowInformation("hoge");
}

 
なお、MessageBoxServiceはIMessageBoxService型を返すため、エラー表示をするShowError()メソッドや、MessageBox.Show()と同じような使い方ができるShow()メソッドも使えたりします。
IMessageBoxService インターフェイス (Microsoft.TeamFoundation.MVVM) - MSDN

 

View

xamlのWindow要素に以下を追加します。

xmlns:mvvm="clr-namespace:Microsoft.TeamFoundation.MVVM;assembly=Microsoft.TeamFoundation.Controls"
mvvm:MVVMSupport.ViewModel="{Binding}"

 
なお、xamlに追加していない場合は、実行時に以下のエラーが表示されます。

Service not found: Microsoft.TeamFoundation.MVVM.IMessageBoxService. Make sure that 'mvvm:MVVMSupport.ViewModel="{Binding}"' is in your .xaml file.

 
また、Viewのコードビハインドが不要となるため、以上でDataContextChangedと同じ処理になります。

 

感想

DataContextChangedイベントハンドラーと比べてMessageBoxServiceでは、

  • Viewではメッセージボックスを表示するためのコードビハインドがなくなった
    • xamlのWindow要素への追加が増えたものの、ほぼ定型文
  • ViewModelではActionプロパティが不要になった

となり、より簡潔に書けそうです。

 
なお、Microsoft.TeamFoundation.MVVM名前空間に関する情報があまり見つからなかったため、何か不具合があるかもしれません。

その時は都度追加していければと思います。

 

ソースコード

GitHubに上げました(プロジェクト名は「MessageBoxMVVM」)。
CSharp-Sample/MVVMApp/MessageBoxMVVM at master - thinkAmi/CSharp-Sample - GitHub

なお、GitHub上のコードでは、MessageBoxService.ShowInformation()の他に、ShowError()やShow()なども使っています。