WPF 数据验证
MyTextBox.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="{x:Type TextBoxBase}" TargetType="{x:Type TextBoxBase}" BasedOn="{x:Null}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="2,1,1,1"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="SelectionBrush" Value="{DynamicResource Accent}" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderThickness="1" BorderBrush="#FFdc000c" VerticalAlignment="Top">
<Grid>
<AdornedElementPlaceholder x:Name="adorner" Margin="-1"/>
</Grid>
</Border>
<Border x:Name="errorBorder" Background="#FFdc000c" Margin="8,0,0,0"
Opacity="0" CornerRadius="0"
IsHitTestVisible="False"
MinHeight="24" >
<TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
Foreground="White" Margin="8,2,8,3" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
</DataTrigger.Binding>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="fadeInStoryboard">
<Storyboard>
<DoubleAnimation Duration="00:00:00.15"
Storyboard.TargetName="errorBorder"
Storyboard.TargetProperty="Opacity"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="fadeInStoryboard"/>
<BeginStoryboard x:Name="fadeOutStoryBoard">
<Storyboard>
<DoubleAnimation Duration="00:00:00"
Storyboard.TargetName="errorBorder"
Storyboard.TargetProperty="Opacity"
To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border x:Name="Bd"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" RenderOptions.ClearTypeHint="Enabled"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource InputTextDisabled}"/>
</Trigger>
<Trigger Property="IsReadOnly" Value="true">
<Setter Property="Foreground" Value="{DynamicResource InputTextDisabled}"/>
</Trigger>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Accent}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsReadOnly" Value="False"/>
<Condition Property="IsEnabled" Value="True"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource InputBackgroundHover}"/>
<Setter Property="BorderBrush" Value="{DynamicResource InputBorderHover}"/>
<Setter Property="Foreground" Value="{DynamicResource InputTextHover}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource {x:Type TextBoxBase}}" TargetType="{x:Type TextBox}">
</Style>
<Style BasedOn="{StaticResource {x:Type TextBoxBase}}" TargetType="{x:Type RichTextBox}">
</Style>
</ResourceDictionary>
App.xaml 加载:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Template/MyTextBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
ValidateModelBase.cs 基类
public class ValidateModelBase : ObservableObject, IDataErrorInfo
{
public ValidateModelBase()
{
}
#region 属性
/// <summary>
/// 表当验证错误集合
/// </summary>
private Dictionary<String, String> dataErrors = new Dictionary<String, String>();
/// <summary>
/// 是否验证通过
/// </summary>
public Boolean IsValidated
{
get
{
if (dataErrors != null && dataErrors.Count > 0)
{
return false;
}
return true;
}
}
#endregion
public string this[string columnName]
{
get
{
ValidationContext vc = new ValidationContext(this, null, null);
vc.MemberName = columnName;
var res = new List<ValidationResult>();
var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
if (res.Count > 0)
{
AddDic(dataErrors, vc.MemberName);
return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
}
RemoveDic(dataErrors, vc.MemberName);
return null;
}
}
public string Error
{
get
{
return null;
}
}
#region 附属方法
/// <summary>
/// 移除字典
/// </summary>
/// <param name="dics"></param>
/// <param name="dicKey"></param>
private void RemoveDic(Dictionary<String, String> dics, String dicKey)
{
dics.Remove(dicKey);
}
/// <summary>
/// 添加字典
/// </summary>
/// <param name="dics"></param>
/// <param name="dicKey"></param>
private void AddDic(Dictionary<String, String> dics, String dicKey)
{
if (!dics.ContainsKey(dicKey)) dics.Add(dicKey, "");
}
#endregion
}
VM 继承:
[MetadataType(typeof(UserInfo))]
public class UserInfo: ValidateModelBase
{
#region 属性
private String userName;
/// <summary>
/// 用户名
/// </summary>
[Required(ErrorMessage = "姓名是必须的")]
public String UserName
{
get { return userName; }
set { userName = value; RaisePropertyChanged(() => UserName); }
}
private String userPhone;
/// <summary>
/// 用户电话
/// </summary>
[Required(ErrorMessage ="电话是必须的")]
[RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用户电话必须为8-11位的数值.")]
public String UserPhone
{
get { return userPhone; }
set { userPhone = value; RaisePropertyChanged(() => UserPhone); }
}
private String userEmail ;
/// <summary>
/// 用户邮件
/// </summary>
[Required(ErrorMessage ="邮件是必须的")]
[StringLength(100, MinimumLength = 2)]
[RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "请填写正确的邮箱地址.")]
public String UserEmail
{
get { return userEmail; }
set { userEmail = value; RaisePropertyChanged(() => UserEmail); }
}
private int _age;
[Required(ErrorMessage ="请填入年龄")]
//[Range(0.0, 100.0, ErrorMessage = "数据超出范围")]
[Range(double.NegativeInfinity, 100.0, ErrorMessage = "年龄需小于 100")] // NegativeInfinity 负无穷
public int Age
{
get { return _age; }
set { _age = value; RaisePropertyChanged(nameof(Age)); }
}
private string _note = "无";
public string Note
{
get { return _note; }
set { _note = value; RaisePropertyChanged(nameof(Note)); }
}
#endregion
}
页面绑定:
<Window x:Class="WpfApp.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:WpfApp"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
DataContext="{Binding Source={StaticResource Locator}, Path=UserInfoModel}"
Title="MainWindow" Height="300" Width="500">
<Grid>
<GroupBox Header="数据验证测试" Margin="10" >
<StackPanel Margin="10">
<StackPanel.Resources>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<TextBlock Text="用户名" Width="100"/>
<TextBox Width="150" Text="{Binding User.UserName, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<TextBlock Text="用户邮箱" Width="100"/>
<TextBox Width="150" Text="{Binding User.UserEmail, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<TextBlock Text="用户电话" Width="100"/>
<TextBox Width="150" Text="{Binding User.UserPhone, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<TextBlock Text="用户年龄" Width="100"/>
<TextBox Width="150" Text="{Binding User.Age, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<TextBlock Text="备注" Width="100"/>
<TextBox Width="150" Text="{Binding User.Note, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
</StackPanel>
<Button Content="提交" Width="150" Margin="100 10 0 0" HorizontalAlignment="Left" Command="{Binding SubmitCmd}"/>
</StackPanel>
</GroupBox>
</Grid>
</Window>
参考: