如何从ViewModel中分离ViewModel属性验证?

Noo*_*per 9 c# oop wpf mvvm mvvm-light

我正在使用MVVMLight.这是我的Department模特/ POCO课程.我不想以任何方式污染它.

 public partial class Department
    {
        public int DepartmentId { get; set; }
        public string DepartmentCode { get; set; }
        public string DepartmentFullName { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

这是CreateDepartmentViewModel:

public class CreateDepartmentViewModel : ViewModelBase
{
    private IDepartmentService departmentService;
    public RelayCommand CreateDepartmentCommand { get; private set; }

    public CreateDepartmentViewModel(IDepartmentService DepartmentService)
    {
        departmentService = DepartmentService;
        this.CreateDepartmentCommand = new RelayCommand(CreateDepartment, CanExecute);
    }

    private Department _department = new Department();
    public Department Department
    {
        get
        {
            return _department;
        }
        set
        {
            if (_department == value)
            {
                return;
            }
            _department = value;
            RaisePropertyChanged("Department");
        }
    }

    private Boolean CanExecute()
    {
        return true;
    }
    private void CreateDepartment()
    {
        bool success = departmentService.SaveDepartment(_department);
    }
}
Run Code Online (Sandbox Code Playgroud)

DepartmentCodeDepartmentFullName 被结合(如下所示)的用户界面.

 <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Department Code" Grid.Row="0"/>
        <TextBox Grid.Row="0" Text="{Binding Department.DepartmentCode, Mode=TwoWay}"  Margin="150,0,0,0"/>

        <TextBlock Text="Department Name" Grid.Row="1"/>
        <TextBox Grid.Row="1" Text="{Binding Department.DepartmentFullName, Mode=TwoWay}" ToolTip="Hi" Margin="150,0,0,0"/>

        <Button Grid.Row="2" Content="Save" Width="50" Command="{Binding CreateDepartmentCommand}"/>
    </Grid>
Run Code Online (Sandbox Code Playgroud)

在保存部门之前,我需要验证两者DepartmentCode 并且其中DepartmentFullName包含一些文本.

我的验证逻辑应该放在哪里?在ViewModel本身?如果是这样,我如何将我的验证逻辑解耦,以便它也可以进行单元测试?

小智 8

我发现完成此操作的最简单方法是使用

System.Windows.Controls.ValidationRule
Run Code Online (Sandbox Code Playgroud)

它只需要 3 个直接的步骤。

首先创建一个ValidationRule。这是一个完全独立的类,存在于您的模型和视图模型之外,并定义应如何验证文本数据。在这种情况下,一个简单的 String.IsNullOrWhiteSpace 检查。

public class DepartmentValidationRule : System.Windows.Controls.ValidationRule
{
    public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo ultureInfo)
    {
        if (String.IsNullOrWhiteSpace(value as string))
        {
            return new System.Windows.Controls.ValidationResult(false, "The value is not a valid");
        }
        else
        {
            return new System.Windows.Controls.ValidationResult(true, null);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,通过指定 Text 绑定的 ValidationRules 属性,指定您的 TextBox 应使用新类的实例对输入的 Text 执行验证。如果验证失败,您将获得 TextBox 边框变为红色的额外奖励。

    <TextBlock Text="Department Code" Grid.Row="0"/>
    <TextBox Name="DepartmentCodeTextBox"  Grid.Row="0" Margin="150,0,0,0">
        <TextBox.Text>
            <Binding Path="Department.DepartmentCode" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:DepartmentValidationRule/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBlock Text="Department Name" Grid.Row="1"/>
    <TextBox Name="DepartmentNameTextBox" Grid.Row="1" ToolTip="Hi" Margin="150,0,0,0">
        <TextBox.Text>
            <Binding Path="Department.DepartmentFullName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:DepartmentValidationRule/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
Run Code Online (Sandbox Code Playgroud)

最后,如果任一 TextBox 验证失败,请创建一个样式以禁用“保存”按钮。我们通过绑定到我们将验证规则绑定到的文本框的 Validation.HasError 属性来做到这一点。我们将这种样式命名为 DisableOnValidationError 只是为了让事情变得显而易见。

    <Grid.Resources>
        <Style x:Key="DisableOnValidationError" TargetType="Button">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=DepartmentCodeTextBox}" Value="True" >
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=DepartmentNameTextBox}" Value="True" >
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>
Run Code Online (Sandbox Code Playgroud)

最后我们在 Save 按钮上设置 DisableOnValidationError 样式

    <Button Grid.Row="2" Content="Save" Width="50" Command="{Binding CreateDepartmentCommand}"
            Style="{StaticResource DisableOnValidationError}"/>
Run Code Online (Sandbox Code Playgroud)

现在,如果您的任一文本框未通过验证,则文本框将突出显示并且“保存”按钮将被禁用。

DepartmentValidationRule 与您的业务逻辑完全分离,并且是可重用和可测试的。