WPF MVVM使用数据注释进行验证

4

我是WPF MVVM架构的新手。使用LINQ to SQL可以工作,但需要使用数据注释来进行验证。如何使用数据注释提供验证?有没有一个好的例子可以提供?

我的模型是:

namespace EmployeeApp.Model
{
    public class Employee : ObservableObject
    {
        #region Constructor
        public Employee(int empId = 0, string empName = "", int age = 0)
        {
            _empId = empId;
            _empName = empName;
            _age = age;

        }
        #endregion

        #region Properties
        private int _empId = 0;

        public int EmpId
        {
            get { return _empId; }
            set
            {
                _empId = value;
                RaisePropertyChangedEvent("ID");
            }
        }

        private string _empName = string.Empty;

        public string EmpName
        {
            get { return _empName; }
            set
            {
                _empName = value;
                RaisePropertyChangedEvent("Name");
            }
        }

        private int _age = 0;

        public int Age
        {
            get { retuenter code heren _age; }
            set
            {
                _age = value;
                RaisePropertyChangedEvent("Age");
            }
        }


        #endregion
    }
    }

非常感谢您的提前帮助。

你检查了更新的解决方案吗? - blfuentes
1个回答

13
例如:代码 MSDN 参考数据注释 在您的模型类中添加引用:
using System.ComponentModel.DataAnnotations;

那么,只需要为您的属性添加以下内容即可。
[Required(ErrorMessage = "Email address is required")] 
[EmailAddress(ErrorMessage = "Email Address is Invalid")] 
public string Email 
{ 
    get { return GetValue(() => Email); } 
    set { SetValue(() => Email, value); } 
}

完整示例已更新: 你的类应该使用一个基类PropertyChangedNotification 创建PropertyChangedNotification类如下所示(在链接页面上可以找到它)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace EmployeeApp.Notification
{
   public abstract class PropertyChangedNotification : INotifyPropertyChanged, IDataErrorInfo
   {
      #region Fields

      private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

      #endregion

      #region Protected

      /// <summary>
      /// Sets the value of a property.
      /// </summary>
      /// <typeparam name="T">The type of the property value.</typeparam>
      /// <param name="propertySelector">Expression tree contains the property definition.</param>
      /// <param name="value">The property value.</param>
      protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
      {
         string propertyName = GetPropertyName(propertySelector);

         SetValue<T>(propertyName, value);
      }

      /// <summary>
      /// Sets the value of a property.
      /// </summary>
      /// <typeparam name="T">The type of the property value.</typeparam>
      /// <param name="propertyName">The name of the property.</param>
      /// <param name="value">The property value.</param>
      protected void SetValue<T>(string propertyName, T value)
      {
         if (string.IsNullOrEmpty(propertyName))
         {
            throw new ArgumentException("Invalid property name", propertyName);
         }

         _values[propertyName] = value;
         NotifyPropertyChanged(propertyName);
      }

      /// <summary>
      /// Gets the value of a property.
      /// </summary>
      /// <typeparam name="T">The type of the property value.</typeparam>
      /// <param name="propertySelector">Expression tree contains the property definition.</param>
      /// <returns>The value of the property or default value if not exist.</returns>
      protected T GetValue<T>(Expression<Func<T>> propertySelector)
      {
         string propertyName = GetPropertyName(propertySelector);

         return GetValue<T>(propertyName);
      }

      /// <summary>
      /// Gets the value of a property.
      /// </summary>
      /// <typeparam name="T">The type of the property value.</typeparam>
      /// <param name="propertyName">The name of the property.</param>
      /// <returns>The value of the property or default value if not exist.</returns>
      protected T GetValue<T>(string propertyName)
      {
         if (string.IsNullOrEmpty(propertyName))
         {
            throw new ArgumentException("Invalid property name", propertyName);
         }

         object value;
         if (!_values.TryGetValue(propertyName, out value))
         {
            value = default(T);
            _values.Add(propertyName, value);
         }

         return (T)value;
      }

      /// <summary>
      /// Validates current instance properties using Data Annotations.
      /// </summary>
      /// <param name="propertyName">This instance property to validate.</param>
      /// <returns>Relevant error string on validation failure or <see cref="System.String.Empty"/> on validation success.</returns>
      protected virtual string OnValidate(string propertyName)
      {
         if (string.IsNullOrEmpty(propertyName))
         {
            throw new ArgumentException("Invalid property name", propertyName);
         }

         string error = string.Empty;
         var value = GetValue(propertyName);
         var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>(1);
         var result = Validator.TryValidateProperty(
             value,
             new ValidationContext(this, null, null)
             {
                MemberName = propertyName
             },
             results);

         if (!result)
         {
            var validationResult = results.First();
            error = validationResult.ErrorMessage;
         }

         return error;
      }

      #endregion

      #region Change Notification

      /// <summary>
      /// Raised when a property on this object has a new value.
      /// </summary>
      public event PropertyChangedEventHandler PropertyChanged;

      /// <summary>
      /// Raises this object's PropertyChanged event.
      /// </summary>
      /// <param name="propertyName">The property that has a new value.</param>
      protected void NotifyPropertyChanged(string propertyName)
      {
         this.VerifyPropertyName(propertyName);

         PropertyChangedEventHandler handler = this.PropertyChanged;
         if (handler != null)
         {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
         }
      }

      protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
      {
         var propertyChanged = PropertyChanged;
         if (propertyChanged != null)
         {
            string propertyName = GetPropertyName(propertySelector);
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
         }
      }

      #endregion // INotifyPropertyChanged Members

      #region Data Validation

      string IDataErrorInfo.Error
      {
         get
         {
            throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead.");
         }
      }

      string IDataErrorInfo.this[string propertyName]
      {
         get
         {
            return OnValidate(propertyName);
         }
      }

      #endregion

      #region Privates

      private string GetPropertyName(LambdaExpression expression)
      {
         var memberExpression = expression.Body as MemberExpression;
         if (memberExpression == null)
         {
            throw new InvalidOperationException();
         }

         return memberExpression.Member.Name;
      }

      private object GetValue(string propertyName)
      {
         object value;
         if (!_values.TryGetValue(propertyName, out value))
         {
            var propertyDescriptor = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
            if (propertyDescriptor == null)
            {
               throw new ArgumentException("Invalid property name", propertyName);
            }

            value = propertyDescriptor.GetValue(this);
            _values.Add(propertyName, value);
         }

         return value;
      }

      #endregion

      #region Debugging

      /// <summary>
      /// Warns the developer if this object does not have
      /// a public property with the specified name. This 
      /// method does not exist in a Release build.
      /// </summary>
      [Conditional("DEBUG")]
      [DebuggerStepThrough]
      public void VerifyPropertyName(string propertyName)
      {
         // Verify that the property name matches a real,  
         // public, instance property on this object.
         if (TypeDescriptor.GetProperties(this)[propertyName] == null)
         {
            string msg = "Invalid property name: " + propertyName;

            if (this.ThrowOnInvalidPropertyName)
               throw new Exception(msg);
            else
               Debug.Fail(msg);
         }
      }

      /// <summary>
      /// Returns whether an exception is thrown, or if a Debug.Fail() is used
      /// when an invalid property name is passed to the VerifyPropertyName method.
      /// The default value is false, but subclasses used by unit tests might 
      /// override this property's getter to return true.
      /// </summary>
      protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

      #endregion // Debugging Aides
   }
}

在您的视图中,您可以添加以下内容:
<Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
    <TextBox Grid.Column="0"
           Name="txtEmail" 
           Text="{Binding Email, Mode=TwoWay, 
           UpdateSourceTrigger=PropertyChanged, 
           ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Width="100">
    </TextBox>
    <TextBlock Grid.Column="1" Grid.Row="0" 
               Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=txtEmail}" 
               Foreground="Red" Margin="5,0,0,0"/>
</Grid>

关于按钮点击时的验证,只需实现命令并在那里发出通知即可。
点击时进行验证的更改。
视图。
<TextBox Grid.Column="0"
           Name="txtTestValue" Text="{Binding MyTextValue, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Width="100">
            </TextBox>
            <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=txtTestValue}" Foreground="Red" Margin="5,0,0,0"/>
<Button Content="Validate" Command="{Binding ValidationCommand}">

</Button>

模型

ValidationCommand = new RelayCommand(ExecuteMyCommand,() => true);

....

public RelayCommand ValidationCommand
{
    get;
    private set;
}

private void ExecuteMyCommand()
{
    OnValidate("MyTextValue");
}

谢谢您的回复。那么我们如何在视图中的按钮点击事件中实现这个呢? - Arun
1
@blacai:你的代码只有一个非常小的错误:ArgumentException构造函数的参数名应该是不正确参数的名称,而不是参数的值。使用new ArgumentException("Invalid property name: " + propertyName, "propertyName")代替new ArgumentException("Invalid property name", propertyName)。 - Liero
我真的很喜欢存储在字典中的“后备字段”。 - Jone Polvora

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