实现WPF TextBoxes的validation

我有3个TextBoxes( Id1NameSalary )。 IdSalary应包含整数, Name应仅包含字符。 我需要对TextBox进行validation,它应该在输入错误的字符或整数时显示错误。 也可以只在没有代码隐藏的Xaml中完成吗? 请帮我提供所需的代码

这是Xaml代码:

    

有3种方法可以实现validation:

  1. validation规则
  2. INotifyDataErrorInfo的实现
  3. IDataErrorInfo的实现

validation规则示例

 public class NumericValidationRule : ValidationRule { public Type ValidationType { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { string strValue = Convert.ToString(value); if (string.IsNullOrEmpty(strValue)) return new ValidationResult(false, $"Value cannot be coverted to string."); bool canConvert = false; switch (ValidationType.Name) { case "Boolean": bool boolVal = false; canConvert = bool.TryParse(strValue, out boolVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of boolean"); case "Int32": int intVal = 0; canConvert = int.TryParse(strValue, out intVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Int32"); case "Double": double doubleVal = 0; canConvert = double.TryParse(strValue, out doubleVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Double"); case "Int64": long longVal = 0; canConvert = long.TryParse(strValue, out longVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Int64"); default: throw new InvalidCastException($"{ValidationType.Name} is not supported"); } } } 

XAML:

非常重要 :不要忘记设置ValidatesOnTargetUpdated="True" ,如果没有此定义,它将无法工作。

             

INotifyDataErrorInfo示例

 public abstract class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo { #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } ValidateAsync(); } #endregion public virtual void OnLoaded() { } #region INotifyDataErrorInfo private ConcurrentDictionary> _errors = new ConcurrentDictionary>(); public event EventHandler ErrorsChanged; public void OnErrorsChanged(string propertyName) { var handler = ErrorsChanged; if (handler != null) handler(this, new DataErrorsChangedEventArgs(propertyName)); } public IEnumerable GetErrors(string propertyName) { List errorsForName; _errors.TryGetValue(propertyName, out errorsForName); return errorsForName; } public bool HasErrors { get { return _errors.Any(kv => kv.Value != null && kv.Value.Count > 0); } } public Task ValidateAsync() { return Task.Run(() => Validate()); } private object _lock = new object(); public void Validate() { lock (_lock) { var validationContext = new ValidationContext(this, null, null); var validationResults = new List(); Validator.TryValidateObject(this, validationContext, validationResults, true); foreach (var kv in _errors.ToList()) { if (validationResults.All(r => r.MemberNames.All(m => m != kv.Key))) { List outLi; _errors.TryRemove(kv.Key, out outLi); OnErrorsChanged(kv.Key); } } var q = from r in validationResults from m in r.MemberNames group r by m into g select g; foreach (var prop in q) { var messages = prop.Select(r => r.ErrorMessage).ToList(); if (_errors.ContainsKey(prop.Key)) { List outLi; _errors.TryRemove(prop.Key, out outLi); } _errors.TryAdd(prop.Key, messages); OnErrorsChanged(prop.Key); } } } #endregion } 

查看模型实施:

 public class MainFeedViewModel : BaseViewModel//, IDataErrorInfo { private ObservableCollection _feedItems; [XmlIgnore] public ObservableCollection FeedItems { get { return _feedItems; } set { _feedItems = value; OnPropertyChanged("FeedItems"); } } [XmlIgnore] public ObservableCollection FilteredFeedItems { get { if (SearchText == null) return _feedItems; return new ObservableCollection(_feedItems.Where(x => x.Title.ToUpper().Contains(SearchText.ToUpper()))); } } private string _title; [Required] [StringLength(20)] //[CustomNameValidationRegularExpression(5, 20)] [CustomNameValidationAttribute(3, 20)] public string Title { get { return _title; } set { _title = value; OnPropertyChanged("Title"); } } private string _url; [Required] [StringLength(200)] [Url] //[CustomValidation(typeof(MainFeedViewModel), "UrlValidation")] ///  /// Validation of URL should be with custom method like the one that implemented below, or with ///  public string Url { get { return _url; } set { _url = value; OnPropertyChanged("Url"); } } public MainFeedViewModel(string url, string title) { Title = title; Url = url; } ///  /// ///  public MainFeedViewModel() { } public MainFeedViewModel(ObservableCollection feeds) { _feedItems = feeds; } private string _searchText; [XmlIgnore] public string SearchText { get { return _searchText; } set { _searchText = value; OnPropertyChanged("SearchText"); OnPropertyChanged("FilteredFeedItems"); } } #region Data validation local ///  /// Custom URL validation method ///  ///  ///  ///  public static ValidationResult UrlValidation(object obj, ValidationContext context) { var vm = (MainFeedViewModel)context.ObjectInstance; if (!Uri.IsWellFormedUriString(vm.Url, UriKind.Absolute)) { return new ValidationResult("URL should be in valid format", new List { "Url" }); } return ValidationResult.Success; } #endregion } 

XAML:

                         

IDataErrorInfo

查看型号:

 public class OperationViewModel : ViewModelBase, IDataErrorInfo { private const int ConstCodeMinValue = 1; private readonly IEventAggregator _eventAggregator; private OperationInfoDefinition _operation; private readonly IEntityFilterer _contextFilterer; private OperationDescriptionViewModel _description; public long Code { get { return _operation.Code; } set { if (SetProperty(value, _operation.Code, o => _operation.Code = o)) { UpdateDescription(); } } } public string Description { get { return _operation.Description; } set { if (SetProperty(value, _operation.Description, o => _operation.Description = o)) { UpdateDescription(); } } } public string FriendlyName { get { return _operation.FriendlyName; } set { if (SetProperty(value, _operation.FriendlyName, o => _operation.FriendlyName = o)) { UpdateDescription(); } } } public int Timeout { get { return _operation.Timeout; } set { if (SetProperty(value, _operation.Timeout, o => _operation.Timeout = o)) { UpdateDescription(); } } } public string Category { get { return _operation.Category; } set { if (SetProperty(value, _operation.Category, o => _operation.Category = o)) { UpdateDescription(); } } } public bool IsManual { get { return _operation.IsManual; } set { if (SetProperty(value, _operation.IsManual, o => _operation.IsManual = o)) { UpdateDescription(); } } } void UpdateDescription() { //some code } #region Validation #region IDataErrorInfo public ValidationResult Validate() { return ValidationService.Instance.ValidateNumber(Code, ConstCodeMinValue, long.MaxValue); } public string this[string columnName] { get { var validation = ValidationService.Instance.ValidateNumber(Code, ConstCodeMinValue, long.MaxValue); return validation.IsValid ? null : validation.ErrorContent.ToString(); } } public string Error { get { var result = Validate(); return result.IsValid ? null : result.ErrorContent.ToString(); } } #endregion #endregion } 

XAML:

                                

您还可以在视图模型中按如下方式实现IDataErrorInfo 。 如果你实现IDataErrorInfo ,你可以在那里进行validation,而不是特定属性的setter,然后每当出现错误时,返回一条错误消息,以便有错误的文本框周围有一个红色框,表示错误。

 class ViewModel : INotifyPropertyChanged, IDataErrorInfo { private string m_Name = "Type Here"; public ViewModel() { } public string Name { get { return m_Name; } set { if (m_Name != value) { m_Name = value; OnPropertyChanged("Name"); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public string Error { get { return "...."; } } ///  /// Will be called for each and every property when ever its value is changed ///  /// Name of the property whose value is changed ///  public string this[string columnName] { get { return Validate(columnName); } } private string Validate(string propertyName) { // Return error message if there is error on else return empty or null string string validationMessage = string.Empty; switch (propertyName) { case "Name": // property name // TODO: Check validiation condition validationMessage = "Error"; break; } return validationMessage; } } 

并且您必须在XAML中设置ValidatesOnDataErrors=True ,以便按如下方式调用IDataErrorInfo的方法:

  

要仅使用XAML完成此操作,您需要为各个属性添加validation规则。 但我会建议你采用代码背后的方法。 在您的代码中,在属性设置器中定义您的规范,并在不符合您的规范时抛出exception。 并使用错误模板在UI中向用户显示错误。 您的XAML将如下所示

        

代码背后:

 public partial class MainWindow : Window { private ExampleViewModel m_ViewModel; public MainWindow() { InitializeComponent(); m_ViewModel = new ExampleViewModel(); DataContext = m_ViewModel; } } public class ExampleViewModel : INotifyPropertyChanged { private string m_Name = "Type Here"; public ExampleViewModel() { } public string Name { get { return m_Name; } set { if (String.IsNullOrEmpty(value)) { throw new Exception("Name can not be empty."); } if (value.Length > 12) { throw new Exception("name can not be longer than 12 charectors"); } if (m_Name != value) { m_Name = value; OnPropertyChanged("Name"); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

我已经实现了这个validation。 但你会被用在后面的代码。 这是太容易和简单的方式。

XAML:对于名称Validtion,只输入AZ和az中的字符。

   

代码背后。

 private void first_name_texbox_PreviewTextInput ( object sender, TextCompositionEventArgs e ) { Regex regex = new Regex ( "[^a-zA-Z]+" ); if ( regex.IsMatch ( first_name_texbox.Text ) ) { MessageBox.Show("Invalid Input !"); } } 

对于Salary和IDvalidation,用[0-9]+替换正则表达式构造函数传递的值。 这意味着您只能输入从1到无限的数字。

您还可以使用[0-9]{1,4}定义长度。 这意味着您只能输入小于或等于4位数的数字。 这个baracket意味着{至少,多少个}。 通过这样做,您可以在文本框中定义数字范围。

可能对别人有帮助。

XAML:

代码背后。

 private void salary_texbox_PreviewTextInput ( object sender, TextCompositionEventArgs e ) { Regex regex = new Regex ( "[^0-9]+" ); if ( regex.IsMatch ( salary_texbox.Text ) ) { MessageBox.Show("Invalid Input !"); } } 

谈到穆罕默德·迈赫迪的答案,最好这样做:

 private void salary_texbox_PreviewTextInput(object sender, TextCompositionEventArgs e) { Regex regex = new Regex ( "[^0-9]+" ); if(regex.IsMatch(e.Text)) { MessageBox.Show("Error"); } } 

因为与TextCompositionEventArgs进行比较时,它也会获得最后一个字符,而使用textbox.Text则不会。 使用文本框,错误将在下一个插入的字符后显示。