U:调整调用方式和兼容性
This commit is contained in:
1 parent
433e206d45
commit
4ec76b7855
50 files changed
+5592
-47
No files matched your search
@@ -0,0 +1,233 @@
|
||||
# 03 - 表单验证示例
|
||||
|
||||
演示如何使用 Sharp.Wpf 的表单验证功能,包括 DataAnnotations 验证、实时错误提示和自定义验证。
|
||||
|
||||
## 🎯 学习目标
|
||||
|
||||
- 使用 `ValidatableViewModelBase` 实现表单验证
|
||||
- 掌握 DataAnnotations 验证特性
|
||||
- 实现实时验证反馈
|
||||
- 自定义验证规则
|
||||
- 显示验证错误消息
|
||||
|
||||
## 📦 使用的模块
|
||||
|
||||
- **Sharp.Wpf** - ValidatableViewModelBase, 验证支持
|
||||
|
||||
## 🚀 快速运行
|
||||
|
||||
```bash
|
||||
cd examples/03-FormValidation
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## 📝 核心代码讲解
|
||||
|
||||
### 1. UserViewModel.cs - 带验证的 ViewModel
|
||||
|
||||
```csharp
|
||||
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 - 验证错误显示
|
||||
|
||||
```xml
|
||||
<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 的关键特性**:
|
||||
1. **实时验证** - `SetProperty(ref field, value, validate: true)`
|
||||
2. **验证所有属性** - `ValidateAll()` 返回 bool
|
||||
3. **自定义错误** - `AddError(propertyName, errorMessage)`
|
||||
4. **清除错误** - `ClearErrors(propertyName)`
|
||||
5. **错误状态** - `HasErrors` 属性
|
||||
|
||||
**XAML 绑定要点**:
|
||||
1. **UpdateSourceTrigger=PropertyChanged** - 实时触发验证
|
||||
2. **ValidatesOnNotifyDataErrors=True** - 启用错误显示
|
||||
3. **Validation.ErrorTemplate** - 自定义错误样式
|
||||
|
||||
## 🎓 练习建议
|
||||
|
||||
### 练习1:添加更多验证规则
|
||||
添加电话号码字段,使用正则表达式验证手机号格式。
|
||||
|
||||
### 练习2:异步验证
|
||||
实现用户名唯一性检查(模拟调用 API)。
|
||||
|
||||
### 练习3:条件验证
|
||||
当用户勾选"我是学生"时,年龄必须小于25岁。
|
||||
|
||||
### 练习4:自定义验证特性
|
||||
创建 `UniqueUsernameAttribute` 自定义验证特性。
|
||||
|
||||
## 💡 常见问题
|
||||
|
||||
**Q: PasswordBox 无法绑定,怎么办?**
|
||||
A: PasswordBox 的 Password 属性不是依赖属性,不能直接绑定。可以使用附加属性或在代码隐藏中处理。
|
||||
|
||||
**Q: 为什么有时验证不触发?**
|
||||
A: 确保:
|
||||
1. 绑定使用了 `UpdateSourceTrigger=PropertyChanged`
|
||||
2. SetProperty 的 validate 参数设为 true
|
||||
3. XAML 中设置了 `ValidatesOnNotifyDataErrors=True`
|
||||
|
||||
**Q: 如何自定义错误消息样式?**
|
||||
A: 修改 `Validation.ErrorTemplate`,可以自定义边框颜色、图标等。
|
||||
|
||||
## 📚 延伸阅读
|
||||
|
||||
- [DataAnnotations 验证特性](https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations)
|
||||
- [INotifyDataErrorInfo 接口](https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.inotifydataerrorinfo)
|
||||
- 下一个示例:[04-Navigation](../04-Navigation/README.md)
|
||||
|
||||
---
|
||||
|
||||
**掌握表单验证,构建高质量的用户输入体验!** ✅
|
||||
Reference in new issue
Block a user