03 - 表单验证示例
演示如何使用 Sharp.Wpf 的表单验证功能,包括 DataAnnotations 验证、实时错误提示和自定义验证。
🎯 学习目标
- 使用
ValidatableViewModelBase实现表单验证 - 掌握 DataAnnotations 验证特性
- 实现实时验证反馈
- 自定义验证规则
- 显示验证错误消息
📦 使用的模块
- Sharp.Wpf - ValidatableViewModelBase, 验证支持
🚀 快速运行
cd examples/03-FormValidation
dotnet run
📝 核心代码讲解
1. UserViewModel.cs - 带验证的 ViewModel
using System.ComponentModel.DataAnnotations;
using Wpf.Validation;
public class UserViewModel : ValidatableViewModelBase
{
private string _username = "";
private string _email = "";
private string _password = "";
private string _confirmPassword = "";
private int _age;
[Required(ErrorMessage = "用户名不能为空")]
[StringLength(20, MinimumLength = 3, ErrorMessage = "用户名长度必须在3-20个字符之间")]
public string Username
{
get => _username;
set => SetProperty(ref _username, value, validate: true); // 启用验证
}
[Required(ErrorMessage = "邮箱不能为空")]
[EmailAddress(ErrorMessage = "邮箱格式不正确")]
public string Email
{
get => _email;
set => SetProperty(ref _email, value, validate: true);
}
[Required(ErrorMessage = "密码不能为空")]
[StringLength(50, MinimumLength = 6, ErrorMessage = "密码长度必须在6-50个字符之间")]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$",
ErrorMessage = "密码必须包含大小写字母和数字")]
public string Password
{
get => _password;
set
{
SetProperty(ref _password, value, validate: true);
// 密码变化时,重新验证确认密码
ValidateProperty(ConfirmPassword, nameof(ConfirmPassword));
}
}
[Required(ErrorMessage = "请确认密码")]
public string ConfirmPassword
{
get => _confirmPassword;
set
{
SetProperty(ref _confirmPassword, value, validate: false);
// 自定义验证:确认密码必须与密码一致
if (value != Password)
{
AddError(nameof(ConfirmPassword), "两次密码输入不一致");
}
else
{
ClearErrors(nameof(ConfirmPassword));
}
}
}
[Range(18, 120, ErrorMessage = "年龄必须在18-120之间")]
public int Age
{
get => _age;
set => SetProperty(ref _age, value, validate: true);
}
// 提交命令
public ICommand SubmitCommand { get; }
public UserViewModel(IDialogService dialogService)
{
SubmitCommand = new RelayCommand(() =>
{
// 验证所有属性
if (ValidateAll())
{
dialogService.ShowMessage(
$"注册成功!\n用户名: {Username}\n邮箱: {Email}\n年龄: {Age}",
"成功");
}
else
{
dialogService.ShowMessage("请修正表单错误后再提交", "验证失败");
}
});
}
}
2. MainWindow.xaml - 验证错误显示
<Window x:Class="FormValidationExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="表单验证示例" Height="500" Width="400">
<Window.Resources>
<!-- 错误模板 -->
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel>
<Border BorderBrush="Red" BorderThickness="1" CornerRadius="2">
<AdornedElementPlaceholder/>
</Border>
<TextBlock Foreground="Red" FontSize="12" Margin="5,0,0,0"
Text="{Binding [0].ErrorContent}"
DockPanel.Dock="Right"/>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<StackPanel Margin="20">
<TextBlock Text="用户注册表单" FontSize="24" FontWeight="Bold" Margin="0,0,0,20"/>
<!-- 用户名 -->
<TextBlock Text="用户名:"/>
<TextBox Text="{Binding Username, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
Margin="0,5,0,15"/>
<!-- 邮箱 -->
<TextBlock Text="邮箱:"/>
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
Margin="0,5,0,15"/>
<!-- 密码 -->
<TextBlock Text="密码:"/>
<PasswordBox x:Name="PasswordBox" Margin="0,5,0,5"/>
<TextBlock Text="* 密码必须包含大小写字母和数字" FontSize="10" Foreground="Gray" Margin="0,0,0,15"/>
<!-- 确认密码 -->
<TextBlock Text="确认密码:"/>
<PasswordBox x:Name="ConfirmPasswordBox" Margin="0,5,0,15"/>
<!-- 年龄 -->
<TextBlock Text="年龄:"/>
<TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}"
Margin="0,5,0,20"/>
<!-- 提交按钮 -->
<Button Content="提交注册" Command="{Binding SubmitCommand}"
Height="40" FontSize="16"/>
<!-- 验证状态 -->
<TextBlock Margin="0,10,0,0" Foreground="Red"
Text="表单包含错误,请修正后提交"
Visibility="{Binding HasErrors, Converter={StaticResource BoolToVisibilityConverter}}"/>
</StackPanel>
</Window>
3. 关键点说明
ValidatableViewModelBase 的关键特性:
- 实时验证 -
SetProperty(ref field, value, validate: true) - 验证所有属性 -
ValidateAll()返回 bool - 自定义错误 -
AddError(propertyName, errorMessage) - 清除错误 -
ClearErrors(propertyName) - 错误状态 -
HasErrors属性
XAML 绑定要点:
- UpdateSourceTrigger=PropertyChanged - 实时触发验证
- ValidatesOnNotifyDataErrors=True - 启用错误显示
- Validation.ErrorTemplate - 自定义错误样式
🎓 练习建议
练习1:添加更多验证规则
添加电话号码字段,使用正则表达式验证手机号格式。
练习2:异步验证
实现用户名唯一性检查(模拟调用 API)。
练习3:条件验证
当用户勾选"我是学生"时,年龄必须小于25岁。
练习4:自定义验证特性
创建 UniqueUsernameAttribute 自定义验证特性。
💡 常见问题
Q: PasswordBox 无法绑定,怎么办? A: PasswordBox 的 Password 属性不是依赖属性,不能直接绑定。可以使用附加属性或在代码隐藏中处理。
Q: 为什么有时验证不触发? A: 确保:
- 绑定使用了
UpdateSourceTrigger=PropertyChanged - SetProperty 的 validate 参数设为 true
- XAML 中设置了
ValidatesOnNotifyDataErrors=True
Q: 如何自定义错误消息样式?
A: 修改 Validation.ErrorTemplate,可以自定义边框颜色、图标等。
📚 延伸阅读
掌握表单验证,构建高质量的用户输入体验! ✅