目录
最完美的是最后一个。
在输入数据的时候,数据验证是必不可少的一部分,提高用户的交互性和使用性。
看了很多数据验证的,而且官网的demo也有问题(切换点击的时候,会产生白色的点),本人进行一一整理和对比。
首先验证有3种,本文只说2种,另一种性能比较低,就不说了。
分别是IDataErrorInfo和ValidationRule。
1.建立一个wpf项目

2. Person类代码
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public class Person : IDataErrorInfo
- {
- private int age;
-
- public int Age
- {
- get { return age; }
- set { age = value; }
- }
-
- public string Error
- {
- get
- {
- return null;
- }
- }
-
- public string this[string name]
- {
- get
- {
- string result = string.Empty;
-
- if (name == "Age")
- {
- if (this.age < 0 || this.age > 150)
- {
- result = "年龄大于0或者小于150";
- }
- }
- return result;
- }
- }
- }
- }
3.xaml界面
数据源是:
"data"/>
style是:
其中(Validation.Errors)/ErrorContent}可以修改成(Validation.Errors)[0].ErrorContent},都会报错。
-
-
整体代码
"WpfApp2.MainWindow" - 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"
- xmlns:local="clr-namespace:WpfApp2"
- mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800">
-
-
"data"/> -
-
-
-
-
"20"> -
输入age: -
"{StaticResource textBoxInError}"> -
-
"Age" Source="{StaticResource data}" - ValidatesOnExceptions="True"
- UpdateSourceTrigger="PropertyChanged">
-
-
-
-
-
-
-
鼠标悬停查看验证错误消息。 -
-
4.效果

清空的时候会报错

不要使用这个方法
ValidationRule验证有3种。
第一种:
1.建立一个程序

2.AgeRangeRule类
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Controls;
-
- namespace WpfApp2
- {
- public class AgeRangeRule : ValidationRule
- {
- public int Min { get; set; }
- public int Max { get; set; }
-
- public AgeRangeRule()
- {
- }
-
- public override ValidationResult Validate(object value, CultureInfo cultureInfo)
- {
- int age = 0;
-
- try
- {
- if (((string)value).Length > 0)
- age = Int32.Parse((String)value);
- }
- catch (Exception e)
- {
- return new ValidationResult(false, $"非法字符或{e.Message}");
- }
-
- if ((age < Min) || (age > Max))
- {
- return new ValidationResult(false, $"请输入年龄范围: {Min}-{Max}.");
- }
- return ValidationResult.ValidResult;
- }
- }
- }
3.实现属性通知的功能,在输入数据的时候,可以进行提示
BindingBase类
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public class BindingBase : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- //protected virtual void OnPropertyChanged(string propertyName)
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- }
4.建立VM,MainViewModel类,继承BindingBase
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public class MainViewModel : BindingBase
- {
- public MainViewModel()
- {
-
- }
-
- private string name;
-
- public string Name
- {
- get { return name; }
- set
- {
- name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
-
- private int number;
-
- public int Number
- {
- get { return number; }
- set
- {
- number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
- }
- }
5.xaml界面
"WpfApp2.MainWindow" - 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"
- xmlns:local="clr-namespace:WpfApp2"
- mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800">
-
-
"validationTemplate"> -
-
"Red" FontSize="20">! -
-
-
-
-
-
-
-
"textBox1" Width="50" FontSize="15" - Validation.ErrorTemplate="{StaticResource validationTemplate}"
- Style="{StaticResource textBoxInError}">
-
-
"Number" UpdateSourceTrigger="PropertyChanged" > -
-
"21" Max="130"/> -
-
-
-
-
别忘记进行绑定
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
-
- namespace WpfApp2
- {
- ///
- /// MainWindow.xaml 的交互逻辑
- ///
- public partial class MainWindow : Window
- {
- public MainWindow()
- {
- InitializeComponent();
- this.DataContext = new MainViewModel();
- }
- }
- }
6.效果

这个可以使用,切换的瞬间有白色的点。
第二种:
1.建立一个wpf程序
2.BindingBase类
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp1
- {
- public class BindingBase : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- //protected virtual void OnPropertyChanged(string propertyName)
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-
- }
3.建立MV,MainViewModel代码,继承BindingBase,NumberValidationRule是规则,自己可以进行修改
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using System.Windows.Controls;
-
- namespace WpfApp1
- {
- public class MainViewModel : BindingBase
- {
- public MainViewModel()
- {
-
- }
-
- private int name;
-
- public int Name
- {
- get { return name; }
- set
- {
- name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
-
- private string number;
-
- public string Number
- {
- get { return number; }
- set
- {
- number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
- }
-
- //继承一个ValidationRule,重写Validate方法
- public class NumberValidationRule : ValidationRule
- {
- public override ValidationResult Validate(object value, CultureInfo cultureInfo)
- {
- double myValue = 0;
- if (double.TryParse(value.ToString(), out myValue))
- {
- if (myValue >= 0 && myValue <= 100)
- {
- return new ValidationResult(true, null);
- }
- }
-
- return new ValidationResult(false, "请输入 0 至 100的数字");
- }
- }
-
- }
4.xaml界面,其中资源中是弹框的信息的模板
"WpfApp1.MainWindow" - 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"
- xmlns:local="clr-namespace:WpfApp1"
- mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800">
-
-
"validationTemplate"> -
"Horizontal" x:Name="stack"> -
-
-
"adorner"/> -
-
-
"popup" AllowsTransparency="True" Placement="Right" > -
"errorBorder" Background="#ffdc000c" Opacity="0" MinHeight="30" > -
"0" Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)/ErrorContent}" Foreground="White" TextWrapping="Wrap" VerticalAlignment="Center"/> -
-
-
-
-
"True" Binding="{Binding ElementName=adorner,Path=AdornedElement.IsKeyboardFocused}"> -
-
"IsOpen" TargetName="popup" Value="true"/> -
-
-
"fadeInStoryboard"> -
-
"00:00:00.15" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="1"/> -
-
-
-
-
"fadeInStoryboard"/> -
"fadeOutStoryBoard"> -
-
"00:00:00" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="0"/> -
-
-
-
-
-
-
-
-
"textbox" Width="200" Height="40" Validation.ErrorTemplate="{StaticResource validationTemplate }"> -
-
"Name" UpdateSourceTrigger="PropertyChanged"> -
-
"True" /> -
-
-
-
-
"100" Height="50" HorizontalAlignment="Right"> -
5.效果

这个可以使用,不会出现白点,但是界面变化的时候,输入框不会跟着走。
第三种:
这是最完美的,也是最复杂的。
1.首先建立一个wpf程序

2.BindingBase类,用于属性通知
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public class BindingBase : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- //protected virtual void OnPropertyChanged(string propertyName)
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- }
3.IValidationExceptionHandler类,用于界面继承
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public interface IValidationExceptionHandler
- {
- ///
- /// 是否有效
- ///
- bool IsValid
- {
- get;
- set;
- }
- }
- }
4.VM,MainViewModel类,继承BindingBase和IValidationExceptionHandler
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WpfApp2
- {
- public class MainViewModel : BindingBase , IValidationExceptionHandler
- {
- public MainViewModel()
- {
-
- }
-
- private int name;
-
- public int Name
- {
- get { return name; }
- set
- {
- name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
-
- private string number;
-
- public string Number
- {
- get { return number; }
- set
- {
- number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
- }
- }
-
- private bool isValid = true;
- public bool IsValid
- {
- get
- {
- return isValid;
- }
- set
- {
- if (value == isValid)
- return;
- isValid = value;
- OnPropertyChanged();
- }
- }
- }
- }
5.NotifyAdorner类,用于提示
- using System;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
-
- namespace WpfApp2
- {
- public class NotifyAdorner : Adorner
- {
-
- private VisualCollection _visuals;
- private Canvas _canvas;
- private Image _image;
- private TextBlock _toolTip;
- ///
- /// 构造
- ///
- ///
- ///
- public NotifyAdorner(UIElement adornedElement, string errorMessage)
- : base(adornedElement)
- {
- _visuals = new VisualCollection(this);
-
- BuildNotifyStyle(errorMessage);
-
- _canvas = new Canvas();
-
- _canvas.Children.Add(_image);
-
- _visuals.Add(_canvas);
-
- }
-
- private void BuildNotifyStyle(string errorMessage)
- {
- _image = new Image()
- {
- Width = 24,
- Height = 24,
- Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "/img/error.png", UriKind.Absolute))
- };
-
- _toolTip = new TextBlock()
- {
- FontSize = 14,
- Text = errorMessage
- };
-
- _image.ToolTip = _toolTip;
- }
-
- protected override int VisualChildrenCount
- {
- get
- {
- return _visuals.Count;
- }
- }
-
- protected override Visual GetVisualChild(int index)
- {
- return _visuals[index];
- }
-
- public void ChangeToolTip(string errorMessage)
- {
- _toolTip.Text = errorMessage;
- }
-
- protected override Size MeasureOverride(Size constraint)
- {
- return base.MeasureOverride(constraint);
- }
-
- protected override Size ArrangeOverride(Size finalSize)
- {
- _canvas.Arrange(new Rect(finalSize));
-
- _image.Margin = new Thickness(finalSize.Width + 2, 0, 0, 0);
-
- return base.ArrangeOverride(finalSize);
- }
- }
- }
6.RequiredRule类,继承ValidationRule,此处可以写多个验证数据的类
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Controls;
-
- namespace WpfApp2
- {
- public class RequiredRule : ValidationRule
- {
- public override ValidationResult Validate(object value, CultureInfo cultureInfo)
- {
- if (value == null || string.IsNullOrEmpty(value.ToString()))
- {
- return new ValidationResult(false, "不能为空!");
- }
-
- return new ValidationResult(true, null);
- }
- }
- }
7.ValidationExceptionBehavior类,验证行为类,可以获得附加到的对象
- using System;
- using System.Collections.Generic;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Interactivity;
-
- namespace WpfApp2
- {
- ///
- /// 验证行为类,可以获得附加到的对象
- ///
- public class ValidationExceptionBehavior : Behavior<FrameworkElement>
- {
-
- #region 字段
- ///
- /// 错误计数器
- ///
- private int _validationExceptionCount = 0;
-
- private Dictionary
_adornerCache; - #endregion
-
- #region 方法
-
- #region 重写方法
- ///
- /// 附加对象时
- ///
- protected override void OnAttached()
- {
- _adornerCache = new Dictionary
(); -
- //附加对象时,给对象增加一个监听验证错误事件的能力,注意该事件是冒泡的
- this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler
(this.OnValidationError)); - }
-
- #endregion
-
- #region 私有方法
-
- #region 获取实现接口的对象
-
- ///
- /// 获取对象
- ///
- ///
- private IValidationExceptionHandler GetValidationExceptionHandler()
- {
- if (this.AssociatedObject.DataContext is IValidationExceptionHandler)
- {
- var handler = this.AssociatedObject.DataContext as IValidationExceptionHandler;
-
- return handler;
- }
-
- return null;
- }
-
- #endregion
-
- #region 显示Adorner
-
- ///
- /// 显示Adorner
- ///
- ///
- ///
- private void ShowAdorner(UIElement element, string errorMessage)
- {
- NotifyAdorner adorner = null;
-
- //先去缓存找
- if (_adornerCache.ContainsKey(element))
- {
- adorner = _adornerCache[element];
-
- //找到了,修改提示信息
- adorner.ChangeToolTip(errorMessage);
- }
- //没有找到,那就New一个,加入到缓存
- else
- {
- adorner = new NotifyAdorner(element, errorMessage);
-
- _adornerCache.Add(element, adorner);
- }
-
- //将Adorner加入到
- if (adorner != null)
- {
- var adornerLayer = AdornerLayer.GetAdornerLayer(element);
-
- adornerLayer.Add(adorner);
- }
- }
-
- #endregion
-
- #region 移除Adorner
-
- ///
- /// 移除Adorner
- ///
- ///
- private void HideAdorner(UIElement element)
- {
- //移除Adorner
- if (_adornerCache.ContainsKey(element))
- {
- var adorner = _adornerCache[element];
-
- var adornerLayer = AdornerLayer.GetAdornerLayer(element);
-
- adornerLayer.Remove(adorner);
- }
- }
-
- #endregion
-
- #region 验证事件方法
-
- ///
- /// 验证事件
- ///
- ///
- ///
- private void OnValidationError(object sender, ValidationErrorEventArgs e)
- {
- try
- {
- var handler = GetValidationExceptionHandler();
-
- var element = e.OriginalSource as UIElement;
-
- if (handler == null || element == null)
- return;
-
- if (e.Action == ValidationErrorEventAction.Added)
- {
- _validationExceptionCount++;
-
- ShowAdorner(element, e.Error.ErrorContent.ToString());
- }
- else if (e.Action == ValidationErrorEventAction.Removed)
- {
- _validationExceptionCount--;
-
- HideAdorner(element);
- }
-
- handler.IsValid = _validationExceptionCount == 0;
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
-
- #endregion
-
- #endregion
-
- #endregion
-
-
- }
- }
8.xaml界面
"WpfApp2.MainWindow" - 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"
- xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
- xmlns:local="clr-namespace:WpfApp2"
- mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800">
-
-
-
-
-
-
-
-
-
-
-
"25" Width="150"> -
-
"Number" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True"> -
-
"True"> -
-
-
-
-
-
"25" Width="150" Grid.Row="1"> -
-
"Name" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True"> -
-
"True"> -
-
-
-
-
-
-
最后别忘记进行关联
this.DataContext = new MainViewModel();
9.效果


源码: