MVVM に MessageBox.Show() 機能をどのように実装しましたか?
-
11-09-2019 - |
質問
WPF アプリケーションを持っています ViewModel で MessageBox.Show() を呼び出します。 (ユーザーが本当に削除したいかどうかを確認するため)。 これは実際に機能します, 、 しかし MVVMの性質に反する ViewModel は View で何が起こるかを明示的に決定すべきではないためです。
それで今私は考えています MessageBox.Show() 機能を最適に実装するにはどうすればよいですか 私の MVVM アプリケーションのオプション:
「よろしいですか...?」 2つのボタンに加えて、はい、いいえ私のXAMLの境界にあり、テンプレートにトリガーを作成して、viewmodelpropertyと呼ばれるviewmodelpropertyに基づいて崩壊/表示されるようにします。 AreYourSureDialogueBoxIsVisible, 次に、このダイアログ ボックスが必要な場合は、AreYourSureDialogueBoxIsVisible を "true" に割り当て、ViewModel に戻って DelegateCommand を介して 2 つのボタンも処理します。
また、何らかの方法で XAML のトリガーを使用してこれを処理して、[削除] ボタンが実際にメッセージとボタンを含む Border 要素を表示し、[はい] ボタンで実際に削除するようにすることもできます。
どちらのソリューションも複雑すぎるようです 以前は MessageBox.Show() を使用した数行のコードでした。
どのような方法で MVVM アプリケーションにダイアログ ボックスを実装することに成功しましたか?
解決
あなたが挙げた 2 つのうち、私はオプション #2 を好みます。ページ上の削除ボタンを押すと、「削除の確認ダイアログ」が表示されます。「削除の確認ダイアログ」が実際に削除を開始します。
カール・シフレットの作品はチェックしましたか? WPF 基幹業務のスライドとデモ?彼がこんなことをするのは知っています。どこだか思い出してみます。
編集:デモ #11「MVVM でのデータ検証」(EditContactItemsControlSelectionViewModel.DeleteCommand) を確認してください。Karl は ViewModal からポップアップを呼び出します (何!?:-)。実際のところ、私はあなたのアイデアの方が好きです。単体テストの方が簡単なようです。
他のヒント
救助サービス。使用する オニキス (免責事項、私が著者です) これは次のように簡単です。
public void Foo()
{
IDisplayMessage dm = this.View.GetService<IDisplayMessage>();
dm.Show("Hello, world!");
}
実行中のアプリケーションでは、これにより間接的に MessageBox.Show("Hello, world!") が呼び出されます。テスト時に IDisplayMessage サービスをモックして ViewModel に提供し、テスト中に実行したいことを実行できます。
Dean Chalk のリンクが kaput になったので、Dean Chalk の答えを詳しく説明します。
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)は、代表団コマンドを使用して「確かですか?」を表示します。ダイアログとアクションを実行します。この例では、 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
VM に挿入されるインターフェイス (IMessageDisplay など) を作成するだけです。これには、メッセージ ボックス (ShowMessage() など) のようなメソッドがあります。標準のメッセージボックス、またはより WPF 固有のものを使用して実装できます (私は これは CodePlex にあります プラジーシュと呼ばれる男)。
そうすれば、すべてが分離され、テスト可能になります。
次のようなイベントを発生させるのはどうでしょうか "MessageBoxRequested"
ビューのコードビハインドで処理されます(いずれにせよ、これはビューのみのコードなので、このコードをコードビハインドに含めることに問題はありません)。
純粋な MVVM ソリューションで使用できるように、単体テスト機能を備えた単純な MessageBox ラッパー コントロールを作成しました。詳細はブログに書いてあります http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
ムカプ
ViewModel からのメッセージをリッスンする Behavior を実装しました。これは、Laurent Bugnion ソリューションに基づいていますが、コードビハインドを使用せず、再利用可能であるため、よりエレガントだと思います。
まだ読んで満足していない人がいる場合に備えて:
「通知」タイプのメッセージボックスを処理したかっただけです(つまり、気にしない DialogResult
)、しかし、私が読んだほとんどの解決策で私が抱えている問題は、それらが間接的にView実装の選択を強制しているように見えることです(つまり、現在私は MessageBox.Show
, ただし、後でビュー内で非表示のパネルの可視性を直接いじることに決めた場合、それはあまりうまく噛み合いません。 INotification
ViewModel に渡されるインターフェイス)。
そこで私は手短に行ってみました。
ViewModel には、 string NotificationMessage
プロパティに変更が通知される PropertyChanged
.
ビューがサブスクライブするのは、 PropertyChanged
, 、そしてそれが見た場合、 NotificationMessage
財産がやって来て、望むことは何でもします。
OK、つまり、ビューにはコードビハインドがあり、名前は PropertyChanged
はハードコーディングされていますが、XAML ではいずれにせよハードコーディングされます。つまり、可視性のコンバーターや、通知がまだ表示されているかどうかを示すプロパティなどをすべて回避していることになります。
(確かに、これは限られた使用例 (ファイアアンドフォーゲット) のためのものであり、どのように拡張するかについてはあまり考えていません。)
VM からスローするだけです。メッセージボックスをスローするためだけに、他の人のサービスを使用したり、独自のサービスを作成したりしたくありません。
最近、ViewModel の MessageBox.Show を完全に MVVM の苦情メッセージ ボックス メカニズムに置き換える必要があるという問題に遭遇しました。
これを達成するために私が使用したのは InteractionRequest<Notification>
そして InteractionRequest<Confirmation>
インタラクション トリガーとともに、メッセージ ボックス用に独自のビューを作成しました。
実装したものを公開します ここ
このトピックについては、カスタム クラスの作成からサードパーティ ライブラリの使用まで、さまざまな回答が数多くあります。素敵なビジュアルを備えたクールなポップアップが必要な場合は、サードパーティのライブラリを使用することをお勧めします。
ただし、WPF アプリに Microsoft の通常のメッセージ ボックスを使用したいだけの場合は、MVVM/単体テストに適した実装を次に示します。
当初、メッセージ ボックスから継承してインターフェイスでラップしようと考えましたが、メッセージ ボックスにはパブリック コンストラクターがないためそれができませんでした。そのため、「簡単な」解決策は次のとおりです。
Visual Studio でメッセージ ボックスを逆コンパイルすると、すべてのメソッド オーバーロードが表示されます。必要なものを確認してから、新しいクラスを作成してメソッドを追加し、インターフェイスでラップして、タダ!これで、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);
}
}
あとは、その他を注入するときにこれを使用するだけです。そうすれば、うまくいく薄っぺらな抽象化ができあがります...使用する場所によっては問題ありません。私の場合は、いくつかのことを行うことだけを目的とした単純なアプリなので、ソリューションを設計することに意味はありません。これが誰かの役に立てば幸いです。