MVVMでダイアログを表示したいときどうする?

MVVM パターンでのダイアログの扱いに悩む

MVVM パターンを使った開発だと、私の場合、ViewModel にデータの保持やサービス呼び出しを実装していきます。サービス呼び出しはエラーが発生する場合があるので、そのときはエラーダイアログ*1を出すんですが、処理をどこに記述しようかいつも頭を悩ませます。いまだ手探りの状態です。

私の周りでは下に挙げる方法を使っているみたい

(1)コマンド至上主義!派

ViewModelがもつコマンドをボタンにバインドする。エラーが発生したときはViewModel内で処理してダイアログを出す。単体テストのことを考えて、ダイアログを表示する部分は差し替え可能にする。

// ViewModel 内でダイアログを表示するときに使う
public static class ViewModelErrorDialog
{
    // 単体テストではこいつを差し替える
    internal static Func<string,bool?> _showErrorDialogFunc;

    static ViewModelErrorDialog()
    {
        _showErrorDialogFunc = s =>
        {
            return new ErrorDialog(s).ShowDialog();
        };
    }

    public static bool? ShowErrorDialog(string message)
    {
        return _showErrorDialogFunc(message);
    }
}

public class FooViewModel : ViewModelBase
{
    // ViewModel はコマンドを提供
    public ICommand LoadCommand { get;set; }

    public FooViewModel()
    {
        LoadCommand = new DelegateCommand(LoadData);
    }

    private void LoadData(object param)
    {
        var client = new FooServiceClient();
        var data = client.GetData();
        if (null == data)
        {
            // ViewModel の中にダイアログを表示する処理を書いてしまう
            ViewModelErrorDialog.ShowErrorDialog("取得失敗");
        }
        else
        {
            this.Data = data;
        }
    }
}
(2)メソッドでもいいんじゃない?派

エラー処理でダイアログを出したいときは、コマンドではなくメソッドを ViewModel に用意。ボタンがクリックされたら、View のイベントハンドラ内で ViewModel のメソッドを呼び出す。ダイアログは View が表示。

public class FooViewModel : ViewModelBase
{
    // ...省略...
    
    // ViewModel はメソッドを提供
    public bool LoadData()
    {
        var client = new FooServiceClient();
        var data = client.GetData();
        if (null == data)
        {
            return false;
        }
        else
        {
            this.Data = data;
            return true;
        }
    }
}

public class FooView : UserControl
{
    // ...省略...

    // View は ViewModel のメソッドを呼び出す
    private void OnLoadButtonClick(object sender, RoutedEventArgs e)
    {
        var viewModel = (FooViewModel)DataContext;
        if (viewModel.LoadData() == false)
        {
            new ErrorDialog("取得失敗").ShowDialog();
        }
    }
}

みなさんどうしています?

もしかしたら「今どきダイアログなんて出さないよ」という人もいるかも。
自分は今のところ(2)の方法でやっていますね。シンプルなので。

*1:MessageBox のときもある