4

我有一个小型测试 WPF MVVM 应用程序在其中工作,其中一个视图允许用户更改客户的名字或姓氏,并且全名会自动更改,因此通信从 M 到 MV 到 V 并返回,一切都完全解耦,到目前为止一切顺利。

但是现在,当我考虑如何开始扩展它以使用 MVVM 模式构建大型应用程序时,我发现解耦是一个障碍,即:

  • 我将如何进行验证消息,例如,如果回到 LastName 设置器中的模型中,我添加了阻止设置超过 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
    }
}
4

3 回答 3

6

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

于 2009-04-03T09:04:24.823 回答
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 设置为视图的数据上下文:

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

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

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

于 2009-04-06T11:23:59.703 回答
0

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

于 2009-07-07T12:04:17.477 回答