如何从模型中发送验证消息以在MVVM模式中查看?

时间:2009-04-03 08:49:00

标签: wpf validation mvvm

我有一个小型测试WPF MVVM应用程序,其中一个视图允许用户更改客户的名字或姓氏,全名自动更改,因此通信从M到MV到 - V和背部,一切都完全解耦,到目前为止一直很好。

但是现在我想看看我将如何开始扩展这个以使用MVVM模式构建大型应用程序,我发现解耦成为一个障碍,即:

  • 我将如何进行验证消息,例如如果回到LastName setter中的模型中,我添加的代码可以防止设置超过50个字符的名称,如何向视图发送消息告诉它显示名称太长的消息

  • 在复杂的应用程序中我可能在屏幕上有几十个视图,但我知道在MVVM中每个视图都有一个且只有一个ViewModel分配给它以提供它数据和行为,所以视图如何相互交互,例如在上面的验证示例中,如果回到客户模型中我们想要通知特定的“MessageAreaView”以显示消息“姓氏可能只包含50个字符。”,如何将堆栈中的信息传递给该特定视图?

CustomerHeaderView.xaml(查看):

<UserControl x:Class="TestMvvm444.Views.CustomerHeaderView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <StackPanel HorizontalAlignment="Left">
            <ItemsControl ItemsSource="{Binding Path=Customers}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <StackPanel Orientation="Horizontal">
                                <TextBox
                                Text="{Binding Path=FirstName, Mode=TwoWay}" 
                                Width="100" 
                                Margin="3 5 3 5"/>
                                <TextBox 
                                Text="{Binding Path=LastName, Mode=TwoWay}" 
                                Width="100"
                                Margin="0 5 3 5"/>
                                <TextBlock 
                                Text="{Binding Path=FullName, Mode=OneWay}" 
                                Margin="0 5 3 5"/>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</UserControl>

Customer.cs(型号):

using System.ComponentModel;

namespace TestMvvm444.Model
{
    class Customer : INotifyPropertyChanged
    {

        public int ID { get; set; }
        public int NumberOfContracts { get; set; }

        private string firstName;
        private string lastName;

        public string FirstName
        {
            get { return firstName; }
            set
            {
                if (firstName != value)
                {
                    firstName = value;
                    RaisePropertyChanged("FirstName");
                    RaisePropertyChanged("FullName");

                }
            }
        }

        public string LastName
        {
            get { return lastName; }
            set
            {
                if (lastName != value)
                {
                    lastName = value;
                    RaisePropertyChanged("LastName");
                    RaisePropertyChanged("FullName");
                }
            }
        }

        public string FullName
        {
            get { return firstName + " " + lastName; }
        }



        #region PropertChanged Block
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
        #endregion
    }
}

3 个答案:

答案 0 :(得分:6)

要进行验证,请让您的视图模型实现IDataErrorInfo。至于视图之间的通信,不要害怕编写UI服务(例如,允许和查看模型以提供将在UI中某处显示的消息的消息服务)。或者,如果视图模型之间存在硬关系(例如,一个视图模型拥有另一个视图模型),则拥有视图模型可以保存对子视图模型的引用。

答案 1 :(得分:2)

添加验证消息的一种非常简单的方法是使用绑定。

向视图模型添加一个可通知的属性,用于定义是否应显示验证消息:

private Boolean _itemValidatorDisplayed;
public Boolean ItemValidatorDisplayed
{
    get { return _itemValidatorDisplayed; }
    set
    {
        _itemValidatorDisplayed= value;
        _OnPropertyChanged("ItemValidatorDisplayed");
    }
}

添加一个将bool转换为可见性的转换器类:

using System;
using System.Windows;

namespace xxx
{
    public class BoolToVisibilityConverter : IValueConverter
    {
        public bool Negate { get; set; }

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool val = System.Convert.ToBoolean(value);
            if (!Negate)
            {
                return val ? Visibility.Visible : Visibility.Collapsed;
            }
            else
            {
                return val ? Visibility.Collapsed : Visibility.Visible;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

从视图绑定到属性并应用转换器:

    <UserControl x:Class="ViewClass"
    ...
    >
        <UserControl.Resources>
            <contract:BoolToVisibilityConverter Negate="False"
                                                x:Key="BoolToVisibilityConverter" />
        </UserControl.Resources>

...

<TextBlock Visibility="{Binding Converter={StaticResource BoolToVisibilityConverter}, Path=ItemValidatorDisplayed}" />

...

</UserControl>

您需要将ViewModel设置为视图的datacontext:

namespace xxx
{
    public partial class ViewClass: UserControl
    {
        public ViewClass()
        {
            InitializeComponent();

            this.DataContext = new ViewClass_ViewModel();
        }
}
}

Bingo - 将完美的工作验证推送到任何关注订阅此ViewModel / Property的视图。

答案 2 :(得分:0)

您还可以将验证绑定到SL3中的源对象集合: - )