在通用 Windows 应用程序中使用 MVVM Light 进行验证

18

在为通用Windows应用程序应用程序设置MVVM Light后,我有以下结构,并且想知道使用UWP和MvvmLight进行验证的最简洁方法,以便在需要时通知用户出现错误并可能重置文本框值。唯一的诀窍是Textbox是UserControl的一部分(为了清晰起见清理了不必要的XAML代码),因为它将被多次使用。同时,我添加了DataAnnotations和ValidationResult仅供演示,并不意味着这是最佳实践或迄今为止工作。

就绑定和添加/删除值而言,该代码运行良好

  • ViewModel

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Views;
using System;
using System.ComponentModel.DataAnnotations;

public class ValidationTestViewModel : ViewModelBase
 {
   private int _age;

  [Required(ErrorMessage = "Age is required")]
  [Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
  [CustomValidation(typeof(int), "ValidateAge")]
  public int Age
    {
      get { return _age; }
      set
      {
        if ((value > 1) && (value =< 100))
            _age= value;
      }
    }

  public static ValidationResult ValidateAge(object value, ValidationContext validationContext)
   {
      return null;
   }
}
  • 查看

    <Page
     x:Class="ValidationTest.Views.ValidationTestPage"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="using:ValidationTest.Views"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     DataContext="{Binding ValidationTestPageInstance, Source={StaticResource  Locator}}" 
    xmlns:views="using:ValidationTest.Views">
    
         <views:NumberEdit TextInControl="{Binding Age, Mode=TwoWay}" />
    
    </Page>
    
  • UserControl

    <UserControl
     x:Class="ValidationTest.Views.Number"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="using:ValidationTest.Views"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     x:Name="userControl1">
      <Grid>
    
      <TextBox x:Name="content" Text="{Binding TextInControl, ElementName=userControl1, Mode=TwoWay}"></TextBox>
       </Grid>
    
     </UserControl>
    
  • UserControl 代码后台:

    public partial class NumberEdit : UserControl
    {
       public string TextInControl
          {
            get { return (string)GetValue(TextInControlProperty); }
            set {
                  SetValue(TextInControlProperty, value);
                }
    }
    
    public static readonly DependencyProperty TextInControlProperty =
        DependencyProperty.Register("TextInControl", typeof(string),
                                       typeof(NumberEdit), new PropertyMetadata(null));
    
     }
    
  • 2个回答

    2
    我们通常在MVVM Light中使用IDialogService接口来向用户通知错误,其中IDialogServiceShowError方法、ShowMessage方法和ShowMessageBox方法。
    我们应该能够使用PropertyChangedCallback值创建一个PropertyMetadata实例,在依赖属性的有效属性值更改时调用它。当它被调用时,我们可以在其中使用ShowMessage方法。
    例如:
    public sealed partial class NumberEdit : UserControl
    {
        public NumberEdit()
        {
            this.InitializeComponent();
        }
    
        public static readonly DependencyProperty TextInControlProperty =
         DependencyProperty.Register("TextInControl", typeof(string),
                                        typeof(NumberEdit), new PropertyMetadata(null, new PropertyChangedCallback(StringChanged)));
    
        private static void StringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            int value;
            Int32.TryParse(e.NewValue.ToString(), out value);
            if (0 < value && value < 99)
            {
            }
            else
            {
                var dialog = ServiceLocator.Current.GetInstance<IDialogService>();
                dialog.ShowMessage("Age should be between 1 to 100", "Error mesage");
            }
        }
    
        public string TextInControl
        {
            get { return (string)GetValue(TextInControlProperty); }
            set
            {
                SetValue(TextInControlProperty, value);
            }
        }
    }
    

    如果您想重置TextBox的值,您应该能够在Age属性中使用RaisePropertyChanged。如果我们不在Age属性中使用RaisePropertyChanged,当Age值发生变化时,TextBox的值将不会改变。
    有关RaisePropertyChanged的更多信息,请参见INotifyPropertyChanged接口
    例如:
    public int Age
    {
        get { return _age; }
        set
        {
            if ((value > 1) && (value <= 100))
                _age = value;
            RaisePropertyChanged("Age");
        }
    }
    

    更新:
    在您的页面中,您应该在控件中添加DataContext。
    <Page x:Class="Validation_Using_MVVM_Light_in_a.SecondPage"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          mc:Ignorable="d"
          xmlns:local="using:Validation_Using_MVVM_Light_in_a"
    xmlns:VM="using:Validation_Using_MVVM_Light_in_a.ViewModel">
    
        <Page.Resources>
            <VM:ValidationTestViewModel x:Key="MyViewModel" />
        </Page.Resources>
        <Grid>
            <local:NumberEdit  DataContext="{StaticResource MyViewModel}"  TextInControl="{Binding Age, Mode=TwoWay}" />
        </Grid>
    </Page>
    

    需要在XAML中进行任何更改吗?此外,PropertyChangedCallback链接无法正常工作。 - usefulBee
    @usefulBee 你应该能够将 DataContext 添加到页面或控件中,同时我已经更新了 StringChanged 方法中的代码。 - Jayden
    以下是关于这种方法的一些问题:1)我发现使用RaisePropertyChanged没有任何好处,删除它不会影响任何东西。2)既然所有属性都在ViewModel中声明,为什么要在Code Behind中使用StringChanged?3)显示对话框消息缺乏即时验证,因为它取决于用户首先将焦点从文本框移开。4)即使使用了RaisePropertyChanged,在TwoWay绑定模式下,文本框的值也永远不会重置为旧值。 - usefulBee

    0
    你在这里缺少的是调用Validator.ValidateObject进行实际验证。这将应用验证属性到数据上,并且如果你已经实现了IValidatableObject.Validate(你应该实现它,而不是拥有像ValidateAge这样的临时函数),还会调用它。
    就像这样:
            // Validate using:
            // 1. ValidationAttributes attached to this validatable's class, and
            // 2. ValidationAttributes attached to the properties of this validatable's class, and 
            // 3. this.Validate( validationContext)
            // 
            // Note, for entities, a NotSupportedException will be thrown by TryValidateObject if any of 
            // the primary key fields are changed. Correspondingly the UI should not allow modifying 
            // primary key fields. 
            ValidationContext validationContext = new ValidationContext(this);
            List<ValidationResult> validationResults = new List<ValidationResult>(64);
            bool isValid = Validator.TryValidateObject( this, validationContext, validationResults, true);
            Debug.Assert(isValid == (validationResults.Count == 0));
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接