web-dev-qa-db-ja.com

MVVMでMessageBox.Show()機能をどのように実装しましたか?

WPFアプリケーションがありますViewModelでMessageBox.Show()を呼び出します(ユーザーが本当に削除したいかどうかを確認するため)。 これは実際には機能しますが、しかしMVVMの粒度に反する ViewModelはビューで何が発生するかを明示的に決定する必要がないためです。

だから今私は考えていますMVBoxアプリケーションでMessageBox.Show()機能を実装する方法を教えてください MVVMアプリケーションのオプション:

  1. 「Are you sure ...?」というテキストのメッセージを受け取ることができますXAMLのボーダーに2つのボタン[はい]と[いいえ]を追加し、テンプレートにトリガーを作成して、それがAreYourSureDialogueBoxIsVisibleと呼ばれるViewModelPropertyに基づいて折りたたみ/表示されるようにし、このダイアログが必要になったときボックス、AreYourSureDialogueBoxIsVisibleを "true"に割り当て、また、ViewModelでDelegateCommandを介して2つのボタンを処理します。

  2. また、XAMLでトリガーを使用してこれを処理して、[削除]ボタンでメッセージとボタンを含むBorder要素が実際に表示され、[はい]ボタンで実際に削除されるようにすることもできます。

両方のソリューションは複雑すぎるようです以前はMessageBox.Show()を使用した数行のコードでした。

MVVMアプリケーションにダイアログボックスをどのように実装しましたか?

42
Edward Tanguay

あなたが言及した2つのうち、私はオプション#2を好みます。ページの[削除]ボタンをクリックすると、[削除の確認ダイアログ]が表示されます。 「削除の確認ダイアログ」は実際に削除を開始します。

Karl Shifflettの WPF基幹業務スライドとデモ を確認しましたか?私は彼がこのようなことをするのを知っています。どこにあるか覚えておきましょう。

編集:デモ#11「MVVMでのデータ検証」(EditContactItemsControlSelectionViewModel.DeleteCommand)を確認してください。 KarlはViewModalからポップアップを呼び出します(What !? :-)。私は実際にはあなたの考えの方が好きです。単体テストの方が簡単に思えます。

5
Aaron Hoffman

救助へのサービス。 Onyx (免責事項、私は著者です)を使用すると、これは次のように簡単です。

public void Foo()
{
    IDisplayMessage dm = this.View.GetService<IDisplayMessage>();
    dm.Show("Hello, world!");
}

実行中のアプリケーションでは、これは間接的にMessageBox.Show( "Hello、world!")を呼び出します。テスト時には、IDisplayMessageサービスをモックしてViewModelに提供し、テスト中に実行したいことを実行できます。

13
wekempf

彼のリンクがカプットになった今、ディーン・チョークの答えを拡張するには:

App.xaml.csファイルで、確認ダイアログをビューモデルに接続します。

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var confirm = (Func<string, string, bool>)((msg, capt) => MessageBox.Show(msg, capt, MessageBoxButton.YesNo) == MessageBoxResult.Yes);
    var window = new MainWindowView();
    var viewModel = new MainWindowViewModel(confirm);
    window.DataContext = viewModel;
    ...
}

ビュー(MainWindowView.xaml)には、ViewModelのコマンドを呼び出すボタンがあります。

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

Viewmodel(MainWindowViewModel.cs)は、デリゲートコマンドを使用して「Are you sure?」を表示しますダイアログでアクションを実行します。この例では、それは this に似たSimpleCommandですが、ICommandのすべての実装で実行できます。

private readonly Func<string, string, bool> _confirm;

//constructor
public MainWindowViewModel(Func<string, string, bool> confirm)
{
    _confirm = confirm;
    ...
}

#region Delete Command
private SimpleCommand _deleteCommand;
public ICommand DeleteCommand
{
    get { return _deleteCommand ?? (_deleteCommand = new SimpleCommand(ExecuteDeleteCommand, CanExecuteDeleteCommand)); }
}

public bool CanExecuteDeleteCommand()
{
    //put your logic here whether to allow deletes
    return true;
}

public void ExecuteDeleteCommand()
{
    bool doDelete =_confirm("Are you sure?", "Confirm Delete");
    if (doDelete)
    {
        //delete from database
        ...
    }
}
#endregion
5
JumpingJezza

VMに挿入されるインターフェイス(IMessageDisplayなど)を作成するだけで、MessageBox(ShowMessage()など)のようなメソッドがあります。これは、標準のメッセージボックスを使用して実装することも、WPF固有のものを使用して実装することもできます(私は、Prjeeshによって CodePlexでこれを使用 を使用しています)。

そうすれば、すべてが分離され、テスト可能になります。

3
Grant Crofton

ビューのコードビハインドで処理される"MessageBoxRequested"のようなイベントの発生についてはどうですか(とにかくそれはビューのみのコードなので、コードビハインドでこのコードを使用しても問題はありません)。

他の誰かがまだ読んでいて満足できない場合に備えて:

「通知」タイプのメッセージボックスを処理したいだけです(つまり、DialogResultは気にしません)。しかし、これまで読んだほとんどのソリューションで私が抱えている問題は、それらが間接的にビューの実装を選択します(つまり、現在私はMessageBox.Showですが、後でビューで非表示のパネルの可視性を直接操作することにした場合、ViewModelに渡されたINotificationインターフェースとうまく連動しません)。

だから私は素早く汚いために行きました:

ViewModelにはstring NotificationMessageプロパティ。変更はPropertyChangedに通知されます。

ビューはPropertyChangedをサブスクライブし、NotificationMessageプロパティが通過したことを確認すると、必要な処理を実行します。

つまり、これはビューにコードビハインドがあり、PropertyChangedの名前がハードコードされていることを意味しますが、いずれにしてもXAMLでハードコードされます。そしてそれは、Visibilityのコンバーターのようなすべてのものや、通知がまだ表示されているかどうかを示すプロパティを回避することを意味します。

(確かに、これは限られたユースケース(ファイアアンドフォーゲット)のためだけのものです。私はそれをどのように拡張したいかについてはあまり考えていません。)

2
Benjol

このトピックには、カスタムクラスの作成からサードパーティのライブラリの使用までさまざまな答えがあります。ニースのビジュアルを備えたクールなポップアップが必要な場合は、サードパーティのライブラリを使用するといいでしょう。

ただし、WPFアプリにMicrosoftからの通常のメッセージボックスを使用したい場合は、MVVM /ユニットテストに対応した実装をここに示します。

最初はメッセージボックスから継承してインターフェイスでラップするだけだと思っていましたが、メッセージボックスにパブリックコンストラクタがないためにできなかったので、「簡単な」解決策を次に示します。

Visual Studioでメッセージボックスを逆コンパイルすると、すべてのメソッドオーバーロードを確認できます。どのオーバーロードが必要かを確認してから、新しいクラスを作成し、メソッドを追加して、インターフェイスとta-daでラップしました。これで、ninjectを使用してインターフェイスとクラスをバインドし、それを注入して、Moqを使用して単体テストなどを行うことができます。

インターフェースを作成します(すべてを必要としないため、いくつかのオーバーロードを追加しました)。

public interface IMessageBox
    {
        /// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>          
        MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button);

        /// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>           
        MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon);

        /// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>            
        MessageBoxResult Show(string messageBoxText, string caption);
    }

次に、それから継承するクラスがあります。

public class MessageBoxHelper : IMessageBox
    {
        /// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button,
            MessageBoxImage icon)
        {
            return MessageBox.Show(messageBoxText, caption, button, icon, MessageBoxResult.None,
                MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button)
        {
            return MessageBox.Show(messageBoxText, caption, button, MessageBoxImage.None, MessageBoxResult.None,
                MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption)
        {
            return MessageBox.Show(messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None,
                MessageBoxResult.None, MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message and that returns a result.</summary>           
        public MessageBoxResult Show(string messageBoxText)
        {
            return MessageBox.Show(messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None,
                MessageBoxResult.None, MessageBoxOptions.None);
        }
    }

E.t.cを注入するときにこれを使用して、ブームuはトリックを実行する薄っぺらな抽象化を持っています...これは、それをどこで使用するかによって異なります。私の場合は、いくつかのことだけを行うことを意図した単純なアプリなので、ソリューションを設計する意味はありません。これが誰かを助けることを願っています。

1
JohnChris

ViewModelからのメッセージをリッスンするBehaviorを実装しました。これはLaurent Bugnionソリューションに基づいていますが、背後でコードを使用せず、再利用性が高いため、よりエレガントだと思います。

ここで確認してください

壊れたリンクのキャッシュ

1
Elad Katz

単純なMessageBoxラッパーコントロールを作成して、純粋なMVVMソリューションで使用し、ユニットテスト機能を使用できるようにしました。詳細は私のブログにあります http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

ムカプ

1
mukapu

WPFとSilverlightのメッセージボックス

サポートされるMVVM

http://slwpfmessagebox.codeplex.com/

1
Salar

VMから投げるだけです。メッセージボックスを投げるためだけに他人のサービスを利用したり、自分でサービスを書いたりしたくありません。

0
Halmut

最近、この問題に遭遇しました。ViewModelsのMessageBox.Showを、完全にMVVMに準拠したメッセージボックスメカニズムに置き換える必要がありました。

これを達成するために私はInteractionRequest<Notification>およびInteractionRequest<Confirmation>インタラクショントリガーとともに、メッセージボックスの独自のビューを記述しました。

私が実装したものは公開されています here

0
A J Qarshi