• WPF-数据验证《十一》


    目录

    第一部分,IDataErrorInfo

    第二部分,ValidationRule


    最完美的是最后一个。

    在输入数据的时候,数据验证是必不可少的一部分,提高用户的交互性和使用性。

    看了很多数据验证的,而且官网的demo也有问题(切换点击的时候,会产生白色的点),本人进行一一整理和对比。

    首先验证有3种,本文只说2种,另一种性能比较低,就不说了。

    分别是IDataErrorInfo和ValidationRule。

    第一部分,IDataErrorInfo

    1.建立一个wpf项目

    2. Person类代码

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7. namespace WpfApp2
    8. {
    9. public class Person : IDataErrorInfo
    10. {
    11. private int age;
    12. public int Age
    13. {
    14. get { return age; }
    15. set { age = value; }
    16. }
    17. public string Error
    18. {
    19. get
    20. {
    21. return null;
    22. }
    23. }
    24. public string this[string name]
    25. {
    26. get
    27. {
    28. string result = string.Empty;
    29. if (name == "Age")
    30. {
    31. if (this.age < 0 || this.age > 150)
    32. {
    33. result = "年龄大于0或者小于150";
    34. }
    35. }
    36. return result;
    37. }
    38. }
    39. }
    40. }

    3.xaml界面

    数据源是:

    "data"/>

    style是:

    其中(Validation.Errors)/ErrorContent}可以修改成(Validation.Errors)[0].ErrorContent},都会报错。

     整体代码

    1. "WpfApp2.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:WpfApp2"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. "data"/>
    10. "20">
    11. 输入age:
    12. "{StaticResource textBoxInError}">
    13. "Age" Source="{StaticResource data}"
    14. ValidatesOnExceptions="True"
    15. UpdateSourceTrigger="PropertyChanged">
    16. 鼠标悬停查看验证错误消息。

     4.效果

    清空的时候会报错

    不要使用这个方法  

    第二部分,ValidationRule

    ValidationRule验证有3种。

    第一种:

    1.建立一个程序

    2.AgeRangeRule类

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Globalization;
    5. using System.Linq;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. using System.Windows.Controls;
    9. namespace WpfApp2
    10. {
    11. public class AgeRangeRule : ValidationRule
    12. {
    13. public int Min { get; set; }
    14. public int Max { get; set; }
    15. public AgeRangeRule()
    16. {
    17. }
    18. public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    19. {
    20. int age = 0;
    21. try
    22. {
    23. if (((string)value).Length > 0)
    24. age = Int32.Parse((String)value);
    25. }
    26. catch (Exception e)
    27. {
    28. return new ValidationResult(false, $"非法字符或{e.Message}");
    29. }
    30. if ((age < Min) || (age > Max))
    31. {
    32. return new ValidationResult(false, $"请输入年龄范围: {Min}-{Max}.");
    33. }
    34. return ValidationResult.ValidResult;
    35. }
    36. }
    37. }

    3.实现属性通知的功能,在输入数据的时候,可以进行提示

    BindingBase类

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Runtime.CompilerServices;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace WpfApp2
    9. {
    10. public class BindingBase : INotifyPropertyChanged
    11. {
    12. public event PropertyChangedEventHandler PropertyChanged;
    13. //protected virtual void OnPropertyChanged(string propertyName)
    14. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
    15. {
    16. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    17. }
    18. }
    19. }

    4.建立VM,MainViewModel类,继承BindingBase

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace WpfApp2
    7. {
    8. public class MainViewModel : BindingBase
    9. {
    10. public MainViewModel()
    11. {
    12. }
    13. private string name;
    14. public string Name
    15. {
    16. get { return name; }
    17. set
    18. {
    19. name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    20. }
    21. }
    22. private int number;
    23. public int Number
    24. {
    25. get { return number; }
    26. set
    27. {
    28. number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    29. }
    30. }
    31. }
    32. }

    5.xaml界面

    1. "WpfApp2.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:WpfApp2"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. "validationTemplate">
    10. "Red" FontSize="20">!
    11. "textBox1" Width="50" FontSize="15"
    12. Validation.ErrorTemplate="{StaticResource validationTemplate}"
    13. Style="{StaticResource textBoxInError}">
    14. "Number" UpdateSourceTrigger="PropertyChanged" >
    15. "21" Max="130"/>

    别忘记进行绑定

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Windows;
    7. using System.Windows.Controls;
    8. using System.Windows.Data;
    9. using System.Windows.Documents;
    10. using System.Windows.Input;
    11. using System.Windows.Media;
    12. using System.Windows.Media.Imaging;
    13. using System.Windows.Navigation;
    14. using System.Windows.Shapes;
    15. namespace WpfApp2
    16. {
    17. ///
    18. /// MainWindow.xaml 的交互逻辑
    19. ///
    20. public partial class MainWindow : Window
    21. {
    22. public MainWindow()
    23. {
    24. InitializeComponent();
    25. this.DataContext = new MainViewModel();
    26. }
    27. }
    28. }

    6.效果

    这个可以使用,切换的瞬间有白色的点。

    第二种:

    1.建立一个wpf程序

      

    2.BindingBase类

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Runtime.CompilerServices;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace WpfApp1
    9. {
    10. public class BindingBase : INotifyPropertyChanged
    11. {
    12. public event PropertyChangedEventHandler PropertyChanged;
    13. //protected virtual void OnPropertyChanged(string propertyName)
    14. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
    15. {
    16. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    17. }
    18. }
    19. }

    3.建立MV,MainViewModel代码,继承BindingBase,NumberValidationRule是规则,自己可以进行修改

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Globalization;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Text.RegularExpressions;
    7. using System.Threading.Tasks;
    8. using System.Windows.Controls;
    9. namespace WpfApp1
    10. {
    11. public class MainViewModel : BindingBase
    12. {
    13. public MainViewModel()
    14. {
    15. }
    16. private int name;
    17. public int Name
    18. {
    19. get { return name; }
    20. set
    21. {
    22. name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    23. }
    24. }
    25. private string number;
    26. public string Number
    27. {
    28. get { return number; }
    29. set
    30. {
    31. number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    32. }
    33. }
    34. }
    35. //继承一个ValidationRule,重写Validate方法
    36. public class NumberValidationRule : ValidationRule
    37. {
    38. public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    39. {
    40. double myValue = 0;
    41. if (double.TryParse(value.ToString(), out myValue))
    42. {
    43. if (myValue >= 0 && myValue <= 100)
    44. {
    45. return new ValidationResult(true, null);
    46. }
    47. }
    48. return new ValidationResult(false, "请输入 0 至 100的数字");
    49. }
    50. }
    51. }

    4.xaml界面,其中资源中是弹框的信息的模板

    1. "WpfApp1.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:WpfApp1"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. "validationTemplate">
    10. "Horizontal" x:Name="stack">
    11. "adorner"/>
    12. "popup" AllowsTransparency="True" Placement="Right" >
    13. "errorBorder" Background="#ffdc000c" Opacity="0" MinHeight="30" >
    14. "0" Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)/ErrorContent}" Foreground="White" TextWrapping="Wrap" VerticalAlignment="Center"/>
    15. "True" Binding="{Binding ElementName=adorner,Path=AdornedElement.IsKeyboardFocused}">
    16. "IsOpen" TargetName="popup" Value="true"/>
    17. "fadeInStoryboard">
    18. "00:00:00.15" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="1"/>
    19. "fadeInStoryboard"/>
    20. "fadeOutStoryBoard">
    21. "00:00:00" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="0"/>
    22. "textbox" Width="200" Height="40" Validation.ErrorTemplate="{StaticResource validationTemplate }">
    23. "Name" UpdateSourceTrigger="PropertyChanged">
    24. "True" />
    25. "100" Height="50" HorizontalAlignment="Right">

    5.效果

    这个可以使用,不会出现白点,但是界面变化的时候,输入框不会跟着走。

    第三种:

    这是最完美的,也是最复杂的。

    1.首先建立一个wpf程序

    2.BindingBase类,用于属性通知

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Runtime.CompilerServices;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace WpfApp2
    9. {
    10. public class BindingBase : INotifyPropertyChanged
    11. {
    12. public event PropertyChangedEventHandler PropertyChanged;
    13. //protected virtual void OnPropertyChanged(string propertyName)
    14. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")//此处使用特性
    15. {
    16. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    17. }
    18. }
    19. }

    3.IValidationExceptionHandler类,用于界面继承

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace WpfApp2
    7. {
    8. public interface IValidationExceptionHandler
    9. {
    10. ///
    11. /// 是否有效
    12. ///
    13. bool IsValid
    14. {
    15. get;
    16. set;
    17. }
    18. }
    19. }

    4.VM,MainViewModel类,继承BindingBase和IValidationExceptionHandler

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace WpfApp2
    7. {
    8. public class MainViewModel : BindingBase , IValidationExceptionHandler
    9. {
    10. public MainViewModel()
    11. {
    12. }
    13. private int name;
    14. public int Name
    15. {
    16. get { return name; }
    17. set
    18. {
    19. name = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    20. }
    21. }
    22. private string number;
    23. public string Number
    24. {
    25. get { return number; }
    26. set
    27. {
    28. number = value; OnPropertyChanged();//OnPropertyChanged(nameof(name),使用特性,去掉括号的值
    29. }
    30. }
    31. private bool isValid = true;
    32. public bool IsValid
    33. {
    34. get
    35. {
    36. return isValid;
    37. }
    38. set
    39. {
    40. if (value == isValid)
    41. return;
    42. isValid = value;
    43. OnPropertyChanged();
    44. }
    45. }
    46. }
    47. }

    5.NotifyAdorner类,用于提示

    1. using System;
    2. using System.Windows;
    3. using System.Windows.Controls;
    4. using System.Windows.Documents;
    5. using System.Windows.Media;
    6. using System.Windows.Media.Imaging;
    7. namespace WpfApp2
    8. {
    9. public class NotifyAdorner : Adorner
    10. {
    11. private VisualCollection _visuals;
    12. private Canvas _canvas;
    13. private Image _image;
    14. private TextBlock _toolTip;
    15. ///
    16. /// 构造
    17. ///
    18. ///
    19. ///
    20. public NotifyAdorner(UIElement adornedElement, string errorMessage)
    21. : base(adornedElement)
    22. {
    23. _visuals = new VisualCollection(this);
    24. BuildNotifyStyle(errorMessage);
    25. _canvas = new Canvas();
    26. _canvas.Children.Add(_image);
    27. _visuals.Add(_canvas);
    28. }
    29. private void BuildNotifyStyle(string errorMessage)
    30. {
    31. _image = new Image()
    32. {
    33. Width = 24,
    34. Height = 24,
    35. Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "/img/error.png", UriKind.Absolute))
    36. };
    37. _toolTip = new TextBlock()
    38. {
    39. FontSize = 14,
    40. Text = errorMessage
    41. };
    42. _image.ToolTip = _toolTip;
    43. }
    44. protected override int VisualChildrenCount
    45. {
    46. get
    47. {
    48. return _visuals.Count;
    49. }
    50. }
    51. protected override Visual GetVisualChild(int index)
    52. {
    53. return _visuals[index];
    54. }
    55. public void ChangeToolTip(string errorMessage)
    56. {
    57. _toolTip.Text = errorMessage;
    58. }
    59. protected override Size MeasureOverride(Size constraint)
    60. {
    61. return base.MeasureOverride(constraint);
    62. }
    63. protected override Size ArrangeOverride(Size finalSize)
    64. {
    65. _canvas.Arrange(new Rect(finalSize));
    66. _image.Margin = new Thickness(finalSize.Width + 2, 0, 0, 0);
    67. return base.ArrangeOverride(finalSize);
    68. }
    69. }
    70. }

    6.RequiredRule类,继承ValidationRule,此处可以写多个验证数据的类

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Globalization;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7. using System.Windows.Controls;
    8. namespace WpfApp2
    9. {
    10. public class RequiredRule : ValidationRule
    11. {
    12. public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    13. {
    14. if (value == null || string.IsNullOrEmpty(value.ToString()))
    15. {
    16. return new ValidationResult(false, "不能为空!");
    17. }
    18. return new ValidationResult(true, null);
    19. }
    20. }
    21. }

    7.ValidationExceptionBehavior类,验证行为类,可以获得附加到的对象 

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Windows;
    4. using System.Windows.Controls;
    5. using System.Windows.Documents;
    6. using System.Windows.Interactivity;
    7. namespace WpfApp2
    8. {
    9. ///
    10. /// 验证行为类,可以获得附加到的对象
    11. ///
    12. public class ValidationExceptionBehavior : Behavior<FrameworkElement>
    13. {
    14. #region 字段
    15. ///
    16. /// 错误计数器
    17. ///
    18. private int _validationExceptionCount = 0;
    19. private Dictionary _adornerCache;
    20. #endregion
    21. #region 方法
    22. #region 重写方法
    23. ///
    24. /// 附加对象时
    25. ///
    26. protected override void OnAttached()
    27. {
    28. _adornerCache = new Dictionary();
    29. //附加对象时,给对象增加一个监听验证错误事件的能力,注意该事件是冒泡的
    30. this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler(this.OnValidationError));
    31. }
    32. #endregion
    33. #region 私有方法
    34. #region 获取实现接口的对象
    35. ///
    36. /// 获取对象
    37. ///
    38. ///
    39. private IValidationExceptionHandler GetValidationExceptionHandler()
    40. {
    41. if (this.AssociatedObject.DataContext is IValidationExceptionHandler)
    42. {
    43. var handler = this.AssociatedObject.DataContext as IValidationExceptionHandler;
    44. return handler;
    45. }
    46. return null;
    47. }
    48. #endregion
    49. #region 显示Adorner
    50. ///
    51. /// 显示Adorner
    52. ///
    53. ///
    54. ///
    55. private void ShowAdorner(UIElement element, string errorMessage)
    56. {
    57. NotifyAdorner adorner = null;
    58. //先去缓存找
    59. if (_adornerCache.ContainsKey(element))
    60. {
    61. adorner = _adornerCache[element];
    62. //找到了,修改提示信息
    63. adorner.ChangeToolTip(errorMessage);
    64. }
    65. //没有找到,那就New一个,加入到缓存
    66. else
    67. {
    68. adorner = new NotifyAdorner(element, errorMessage);
    69. _adornerCache.Add(element, adorner);
    70. }
    71. //将Adorner加入到
    72. if (adorner != null)
    73. {
    74. var adornerLayer = AdornerLayer.GetAdornerLayer(element);
    75. adornerLayer.Add(adorner);
    76. }
    77. }
    78. #endregion
    79. #region 移除Adorner
    80. ///
    81. /// 移除Adorner
    82. ///
    83. ///
    84. private void HideAdorner(UIElement element)
    85. {
    86. //移除Adorner
    87. if (_adornerCache.ContainsKey(element))
    88. {
    89. var adorner = _adornerCache[element];
    90. var adornerLayer = AdornerLayer.GetAdornerLayer(element);
    91. adornerLayer.Remove(adorner);
    92. }
    93. }
    94. #endregion
    95. #region 验证事件方法
    96. ///
    97. /// 验证事件
    98. ///
    99. ///
    100. ///
    101. private void OnValidationError(object sender, ValidationErrorEventArgs e)
    102. {
    103. try
    104. {
    105. var handler = GetValidationExceptionHandler();
    106. var element = e.OriginalSource as UIElement;
    107. if (handler == null || element == null)
    108. return;
    109. if (e.Action == ValidationErrorEventAction.Added)
    110. {
    111. _validationExceptionCount++;
    112. ShowAdorner(element, e.Error.ErrorContent.ToString());
    113. }
    114. else if (e.Action == ValidationErrorEventAction.Removed)
    115. {
    116. _validationExceptionCount--;
    117. HideAdorner(element);
    118. }
    119. handler.IsValid = _validationExceptionCount == 0;
    120. }
    121. catch (Exception ex)
    122. {
    123. throw ex;
    124. }
    125. }
    126. #endregion
    127. #endregion
    128. #endregion
    129. }
    130. }

    8.xaml界面

    1. "WpfApp2.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    7. xmlns:local="clr-namespace:WpfApp2"
    8. mc:Ignorable="d"
    9. Title="MainWindow" Height="450" Width="800">
    10. "25" Width="150">
    11. "Number" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
    12. "True">
    13. "25" Width="150" Grid.Row="1">
    14. "Name" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
    15. "True">

    最后别忘记进行关联

      this.DataContext = new MainViewModel();

    9.效果

     源码:

    https://download.csdn.net/download/u012563853/86522132

    来源:WPF-数据验证《十一》_wpf notifyadorner-CSDN博客

  • 相关阅读:
    香港写字楼等级如何划分?从3A到C级一文讲明白
    光伏组件机器视觉新突破!维视智造上线汇流带引线焊接检测新方案 “误检率”低至0.01%
    Vue 官方文档2.x教程学习笔记 1 基础 1.4 模板语法 1.4.1 插值
    前端——Vuex状态管理
    相似性和距离度量
    计算机考研自命题(1)
    QLabel中显示图片,让图片保持比例进行放大缩小
    python 使用curl_cffi 绕过jax3指纹-Cloudflare 5s盾
    数据存储方式——KVELL:快速持续键值存储的设计与实现
    MySQL基础知识总结
  • 原文地址:https://blog.csdn.net/u012563853/article/details/126848786