wpf 您是如何在 MVVM 中成功实现 MessageBox.Show() 功能的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1098023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 20:41:42  来源:igfitidea点击:

How have you successfully implemented MessageBox.Show() functionality in MVVM?

wpfmvvmtriggersmessagebox

提问by Edward Tanguay

I've got a WPF application which calls MessageBox.Show() way back in the ViewModel(to check if the user really wants to delete). This actually works, but goes against the grain of MVVMsince the ViewModel should not explicitly determine what happens on the View.

我有一个 WPF 应用程序,它在 ViewModel 中调用 MessageBox.Show()(检查用户是否真的想删除)。这实际上有效,但与 MVVM 的原则背道而驰,因为 ViewModel 不应明确确定 View 上发生的事情。

So now I am thinking how can I best implement the MessageBox.Show() functionalityin my MVVM application, options:

所以现在我在想如何在我的 MVVM 应用程序中最好地实现 MessageBox.Show() 功能,选项:

  1. I could have a message with the text "Are you sure...?" along with two buttons Yes and No all in a Border in my XAML, and create a trigger on the template so that it is collapsed/visible based on a ViewModelProperty called AreYourSureDialogueBoxIsVisible, and then when I need this dialogue box, assign AreYourSureDialogueBoxIsVisible to "true", and also handle the two buttons via DelegateCommand back in my ViewModel.

  2. I could also somehow try to handle this with triggers in XAML so that the Delete button actually just makes some Border element appear with the message and buttons in it, and the Yes button did the actually deleting.

  1. 我可以有一条带有文本“你确定......?”的消息 连同两个按钮 Yes 和 No 都在我的 XAML 的 Border 中,并在模板上创建一个触发器,以便它基于名为AreYourSureDialogueBoxIsVisible的 ViewModelProperty 折叠/可见,然后当我需要这个对话框时,将 AreYourSureDialogueBoxIsVisible 分配给“true” ",并且还通过 DelegateCommand 处理我的 ViewModel 中的两个按钮。

  2. 我也可以尝试使用 XAML 中的触发器来处理这个问题,这样“删除”按钮实际上只是让一些边框元素出现在其中,其中包含消息和按钮,而“是”按钮则执行实际删除操作。

Both solutions seem to be too complexfor what used to be a couple lines of code with MessageBox.Show().

对于过去使用 MessageBox.Show() 的几行代码来说,这两种解决方案似乎都太复杂了。

In what ways have you successfully implemented Dialogue Boxes in your MVVM applications?

您通过哪些方式在 MVVM 应用程序中成功实现了对话框?

采纳答案by Aaron Hoffman

Of the two you mention, I prefer option #2. The Delete button on the page just makes the "Confirm Delete Dialog" appear. The "Confirm Delete Dialog" actually kicks off the Delete.

在你提到的两个中,我更喜欢选项#2。页面上的删除按钮只会显示“确认删除对话框”。“确认删除对话框”实际上启动了删除。

Have you checked out Karl Shifflett's WPF Line Of Business Slides and Demos? I know he does something like this. I'll try to remember where.

您是否查看过 Karl Shifflett 的WPF 业务线幻灯片和演示?我知道他会做这样的事情。我会努力记住在哪里。

EDIT: Check out Demo #11 "Data Validation in MVVM" (EditContactItemsControlSelectionViewModel.DeleteCommand). Karl calls a popup from the ViewModal (What!? :-). I actually like your idea better. Seems easier to Unit Test.

编辑:查看演示 #11“MVVM 中的数据验证”(EditContactItemsControlSelectionViewModel.DeleteCommand)。Karl 从 ViewModal 调用一个弹出窗口(什么!?:-)。我其实更喜欢你的想法。似乎更容易进行单元测试。

回答by wekempf

Services to the rescue. Using Onyx(disclaimer, I'm the author) this is as easy as:

救援服务。使用Onyx(免责声明,我是作者)这很简单:

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

In a running application, this will indirectly call MessageBox.Show("Hello, world!"). When testing, the IDisplayMessage service can be mocked and provided to the ViewModel to do what ever you want to accomplish during the test.

在正在运行的应用程序中,这将间接调用 MessageBox.Show("Hello, world!")。测试时,可以模拟 IDisplayMessage 服务并将其提供给 ViewModel 以执行您在测试期间想要完成的任何操作。

回答by JumpingJezza

To expand on Dean Chalk's answer now that his link is kaput:

现在扩展 Dean Chalk 的答案,因为他的链接是 kaput:

In the App.xaml.cs file we hook up the confirm dialog to the viewmodel.

在 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;
    ...
}

In the view (MainWindowView.xaml) we have a button that calls a command in the ViewModel

在视图 (MainWindowView.xaml) 中,我们有一个按钮可以调用 ViewModel 中的命令

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

The viewmodel (MainWindowViewModel.cs) uses a delegate command to show the "Are you sure?" dialog and perform the action. In this example it is a SimpleCommandsimilar to this, but any implementation of ICommand should do.

视图模型 (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

回答by Grant Crofton

I just create an interface (IMessageDisplay or similar) which gets injected into the VM, and it has methods like a MessageBox (ShowMessage() etc). You can implement that using a standard messagebox, or something more WPF specific (I use this one on CodePlexby Prajeesh).

我只是创建了一个注入到 VM 中的接口(IMessageDisplay 或类似的),它有像 MessageBox(ShowMessage() 等)这样的方法。您可以使用标准消息框或更多 WPF 特定的东西来实现它(我Prajeesh 的CodePlex 上使用了这个)。

That way everything's separated and testable.

这样一切都是分开的和可测试的。

回答by Ignacio Soler Garcia

What about raising an Event like "MessageBoxRequested"handled in the codebehind of the View (anyway it's View only code so I don't see any problem with having this code on the codebehind).

如何引发一个事件,例如"MessageBoxRequested"在视图的代码隐藏中处理(无论如何它只是视图代码,所以我认为在代码隐藏上使用此代码没有任何问题)。

回答by Benjol

Just in case anyone else is still reading and unsatisfied:

以防万一其他人仍在阅读并且不满意:

I just wanted to handle 'notification' type MessageBoxes (i.e. I don't care about the DialogResult), but the problem that I have with most solutions I've read about is that they seem to indirectly force you to choose your View implementation (that is, currently I have a MessageBox.Show, but if I later decide to just fiddle with the visibility of a hidden panel directly in my View, that won't mesh very nicely with an INotificationinterface passed in to the ViewModel).

我只是想处理“通知”类型的 MessageBoxes(即我不关心DialogResult),但是我读过的大多数解决方案的问题是它们似乎间接迫使您选择您的 View 实现(即是,目前我有一个MessageBox.Show,但如果我后来决定直接在我的视图中摆弄隐藏面板的可见性,那将不会与INotification传递给 ViewModel的接口很好地啮合)。

So I went for quick and dirty:

所以我去了又快又脏:

The ViewModel has a string NotificationMessageproperty, with changes notified to PropertyChanged.

ViewModel 有一个string NotificationMessage属性,更改通知到PropertyChanged.

The View subscribes to PropertyChanged, and if it sees the NotificationMessageproperty come through, does whatever it wants.

View 订阅PropertyChanged,并且如果它看到该NotificationMessage属性通过,则执行它想要的任何操作。

OK, so this means the View has code-behind, and the name of PropertyChangedis hard-coded, but it would be hard-coded in the XAML anyway. And it means I avoid all the stuff like converters for Visibility, and properties to say whether the notification is still visible or not.

好的,这意味着 View 有代码隐藏,并且名称PropertyChanged是硬编码的,但无论如何它都会在 XAML 中进行硬编码。这意味着我避免了所有的东西,比如可见性转换器,以及说明通知是否仍然可见的属性。

(Admittedly this is just for a limited use-case (fire and forget), I haven't given much thought to how I might want to extend it.)

(诚​​然,这仅适用于有限的用例(即发即忘),我没有考虑过如何扩展它。)

回答by Elad Katz

I've implemented a Behavior that listens to a Message from the ViewModel. It's based on Laurent Bugnion solution, but since it doesn't use code behind and is more reusable, I think it's more elegant.

我已经实现了一个 Behavior 来监听来自 ViewModel 的消息。它基于 Laurent Bugnion 解决方案,但由于它不使用隐藏代码并且更可重用,因此我认为它更优雅。

Check it out here

在这里查看

Cache of the broken link

断开链接的缓存

回答by Salar

WPF & Silverlight MessageBoxes

WPF 和 Silverlight 消息框

MVVM supported

支持 MVVM

http://slwpfmessagebox.codeplex.com/

http://slwpfmessagebox.codeplex.com/

回答by JohnChris

There is so many answers on this topic that vary from creating a custom class to using third party libraries. I would say use a third party library if you want cool pop ups with nice visuals.

关于这个主题有很多答案,从创建自定义类到使用第三方库都有。如果您想要具有漂亮视觉效果的炫酷弹出窗口,我会说使用第三方库。

But if you just want to use the regular message box from microsoft for your WPF app here is an MVVM/unit test friendly implementation:

但是,如果您只想将来自微软的常规消息框用于您的 WPF 应用程序,这里是一个 MVVM/单元测试友好实现:

Initially I thought I would just inherit from message box and wrap it with an interface but I couldn't due to Message box not having a public constructor, so here is the "easy" solution:

最初我以为我只是从消息框继承并用接口包装它,但由于消息框没有公共构造函数,我不能,所以这是“简单”的解决方案:

Decompiling Message box in visual studio you can see all the method overloads, I checked which ones I wanted then created a new class and added the methods, wrapped it with an interface and ta-da! Now you can use ninject to bind the interface and class, inject it and use Moq to unit test e.t.c.

在visual studio中反编译消息框你可以看到所有的方法重载,我检查了我想要的那些然后创建一个新类并添加方法,用接口和ta-da包装它!现在你可以使用ninject绑定接口和类,注入它并使用Moq进行单元测试等

Create an interface (only added a few of the overloads as I don't need them all):

创建一个接口(只添加了一些重载,因为我不需要它们):

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);
    }

Then we have the class that will inherit from it:

然后我们有将从它继承的类:

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);
        }
    }

Now just use this when injecting e.t.c and boom u have a flimsy abstraction that will do the trick... which is fine depending on where you will use it. My case is a simple app only meant to do a few things, so no point over engineering a solution. Hope this helps someone.

现在只需在注入等时使用它,并且繁荣你有一个脆弱的抽象可以解决问题......这取决于你将在哪里使用它。我的案例是一个简单的应用程序,只能做一些事情,所以没有必要设计一个解决方案。希望这可以帮助某人。

回答by mukapu

I've made a simple MessageBox wrapper control for us to use in pure MVVM solution and still allowing unit testing capability. The details are in my blog http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

我制作了一个简单的 MessageBox 包装器控件,供我们在纯 MVVM 解决方案中使用,并且仍然允许单元测试功能。详细信息在我的博客http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

mukapu

穆卡普