为什么这在mvvm模式中是正确的,而messageBox不是?我没有看到差异(MVVM Light)

时间:2014-08-07 16:22:12

标签: wpf mvvm mvvm-light messagebox

我找到了一个使用MVVM Light向用户显示消息的小例子。我猜测它是如何使用MVVM Light的,它尊重MVVM模式。

背后的观察代码:

namespace DialogosPruebas
{
    /// <summary>
    /// Lógica de interacción para MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Messenger.Default.Register<DialogMessage>(
                this,
                msg =>
                {
                    var result = MessageBox.Show(
                        msg.Content,
                        msg.Caption,
                        msg.Button);

                    // Send callback
                    msg.ProcessCallback(result);
                });
        }
    }
}

ViewModel是:

using System;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace DialogosPruebas.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private const string Login = "abcd1234";

        public RelayCommand<string> CheckLoginCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// The <see cref="Message" /> property's name.
        /// </summary>
        public const string MessagePropertyName = "Message";

        private string _message = "Login";

        /// <summary>
        /// Gets the Message property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                if (_message == value)
                {
                    return;
                }    
                _message = value;
                RaisePropertyChanged(MessagePropertyName);
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            CheckLoginCommand = new RelayCommand<string>(CheckLogin);
        }

        private void CheckLogin(string text)
        {
            if (text == Login)
            {
                var message = new DialogMessage("Login confirmed, do you want to continue", DialogMessageCallback)
                {
                    Button = MessageBoxButton.OKCancel,
                    Caption = "Continue?"
                };
                Messenger.Default.Send(message);
            }
        }

        private void DialogMessageCallback(MessageBoxResult result)
        {
            if (result == MessageBoxResult.OK)
            {
                Message = "Continue";
            }
            else
            {
                Message = "Stop";
            }
        }
    }
}

AXML:

<Window x:Class="DialogosPruebas.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
        Title="MainWindow" Height="350" Width="525">


    <Window.DataContext>
        <Binding Path="Main" Source="{StaticResource Locator}"/>
    </Window.DataContext>

    <Grid>
    <StackPanel x:Name="LayoutRoot" Background="Black">
        <TextBlock FontSize="36"
            FontWeight="Bold"
            Foreground="Purple"
            Text="{Binding Message}"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            TextWrapping="Wrap" Margin="0,10" />

        <TextBox x:Name="LoginTextBox" TextWrapping="Wrap" Margin="10,0" FontSize="21.333" Text="Enter login">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="KeyUp">
                    <cmd:EventToCommand Command="{Binding CheckLoginCommand, Mode=OneWay}" CommandParameter="{Binding Text, ElementName=LoginTextBox}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>

        <TextBlock TextWrapping="Wrap" Text="(Enter abcd1234 to trigger the message)" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="16" Foreground="White"/>

    </StackPanel>
    </Grid>
</Window>

嗯,我的疑问是,在视图背后的代码中我们使用MessageBoxViewModel包含此代码:

var message = new DialogMessage("Login confirmed, do you want to continue", DialogMessageCallback)
                {
                    Button = MessageBoxButton.OKCancel,
                    Caption = "Continue?"
                };

                Messenger.Default.Send(message);

这会向视图发送请求,然后向后面的代码发送请求,然后使用MessageBox

为什么这比直接在MessageBox使用ViewModel的此解决方案更好,如下所示:

private void CheckLogin(string text)
        {
            if (text == Login)
            {
                MessageBox.Show("Login correct");
            }
        }

有什么区别?在这两种情况下,我都使用MessageBox,我必须等待用户的回复。

我已经读过在MessageBox使用viewModel并不是一个好主意,但我不知道在这种情况下有什么区别。

2 个答案:

答案 0 :(得分:6)

我认为可能需要采用这种方法的两个原因是:

1 - 您的ViewModel需要进行单元测试。

提出模式对话框(例如MessageBox)会导致单元测试中出现各种问题。解耦Messenger方法是安全的,因为在单元测试中,没有人正在监听消息,或者有一个模拟的监听器只对所有面向用户的提示返回“是”。

2 - 您的ViewModel应该在其他平台上重复使用。

如果您仅针对Windows(WPF),请不要过于担心。

导致与UI完全分离的主要问题是,您是否会在其他平台中重复使用ViewModel。

例如,Android中没有MessageBox.Show(),因此,如果您打算重用ViewModel的“应用程序逻辑”,则需要将该代码抽象出来并在每种情况下提供特定于平台的代码。

如果这些对您来说都不重要,那么IMO完全可以在ViewModel中提升MessageBox,以及其他特定于View的问题(例如Window关闭),考虑到MVVM所需的抽象,这些问题可能过于复杂,没有收获。

答案 1 :(得分:2)

不同之处在于,通过发送消息(DialogMessage),您的ViewModel会要求View显示消息。消息的实际显示方式取决于视图。在这种情况下,View将显示一个简单的MessageBox,但它可以使用UserControl来显示自定义对话框。

使用消息,ViewModel不需要知道消息将如何显示,因此它仍然与视图分离。