验证原理
当数据在页面上被用户输入时,数据的流向是页面->验证->viewmodel,也就是说如果验证不通过,数据不会到viewmodel中
使用ValidationRule方式
**实现效果:**当规则验证为不通过时,页面上输入非法,框会变红,并且显示错误原因
1.创建类,继承ValidationRule
2.重载Validate方法
cs
internal class RestrictNegativeValues : ValidationRule
{
//方法的重载
// value 就是 TextBox 里用户输入的内容。
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
// 因为 TextBox 的 Text 属性是字符串,所以这里把它强转为 string 类型
string str = value as string;
// 检查字符串是否为空、null 或者全是空格
if (string.IsNullOrWhiteSpace(str))
{
// 如果为空,返回一个"验证失败"的结果
// false代表失败,后面是提示文字
return new ValidationResult(false, "不能为空");
}
// --- 第三步:检查是否为数字 ---
double result; // 定义一个变量用来存放转换后的数字
// double.TryParse 尝试将字符串转换为数字。
// 如果转换成功(比如输入"123"),返回 true;
// 如果转换失败(比如输入"abc"),返回 false。
if (!double.TryParse(str, out result))
{
// 如果不是数字,返回失败结果
return new ValidationResult(false, "必须是数字");
}
// --- 第四步:检查是否小于 0 ---
// 此时 result 已经是合法的数字了,我们可以直接比较大小
if (result < 0)
{
// 如果是负数,返回失败结果
return new ValidationResult(false, "不能为负数");
}
// --- 第五步:验证通过 ---
// 如果代码运行到了这里,说明既不是空,也是数字,也不是负数
// 返回 ValidationResult.ValidResult 表示一切正常,允许数据通过
return ValidationResult.ValidResult;
}
}
3.xaml页面引用命名空间+绑定
- 要实现验证,在text属性中除了绑定viewmodel中属性还要绑定验证规则,也就是1,2继承ValidationRule的类
- 验证规则不止可以绑定一条
- (Validation.Errors)[0]是验证规则生成的错误列表的第一条,只有没有通过的验证规则的才会进入这个列表,并且列表中错误的顺序和xaml中规则的顺序相同,但是不一一对应,也就是正确的不进入
XML
<TextBox
Name="AngleFactorTextBox"
>
<TextBox.Text>
<Binding Mode="TwoWay" Path="AngleFactor">
<Binding.ValidationRules>
<Validation:RestrictNegativeValues />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock
Margin="5,0,0,0"
VerticalAlignment="Center"
FontSize="12"
Foreground="Red">
<!-- 绑定逻辑:绑定到上面 TextBox 的错误信息 -->
<TextBlock.Text>
<Binding
ElementName="AngleFactorTextBox"
Mode="OneWay"
Path="(Validation.Errors)[0].ErrorContent" />
</TextBlock.Text>
</TextBlock>
使用模板设置统一验证
使用场景:多个输入框,验证规则相同
步骤一.资源字典中设置
XML
//实际上是把"输入框"和"错误提示"打包成了一个临时的组合控件
<ControlTemplate x:Key="ValidationTextBoxTemplate">
<DockPanel>
<!-- 原来的 TextBox (用 AdornedElementPlaceholder 占位) -->
<AdornedElementPlaceholder DockPanel.Dock="Top"/>
<!-- 错误提示文字 (TextBlock) -->
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Bottom"
FontSize="12"
Foreground="Red"
Text="{Binding [0].ErrorContent}"
Visibility="Visible" />
</DockPanel>
</ControlTemplate>
这里Text中内容,不需要写(Validation.Errors)不需要指定elementname?
1. 关于 ElementName
解释: "那一个控件发生变化,触发器触发,就直接知道是哪一个控件,无需 ElementName。"
- 原理 :
ErrorTemplate是依附于控件的"寄生"层(Adorner)。 - 过程 :
- WPF 看到
Trigger触发(HasError=True)。 - WPF 会在当前这个 TextBox 的上方创建一个"覆盖层"(Adorner)。
- 既然是这个 TextBox 召唤出来的覆盖层,覆盖层里的内容自然就是为这个 TextBox 服务的。
AdornedElementPlaceholder这个标签的意思就是:"把那个召唤我的 TextBox 放在这里"。
- WPF 看到
- 结论 :不需要
ElementName,因为它们是"母子关系",孩子天然知道母亲是谁。
2. 关于 (Validation.Errors)
你的解释: "模板上下文被强制指定为 Validation.Errors。"
点评: 完全正确。
- 原理 :这是 WPF 的
Validation.ErrorTemplate特有的"特殊待遇"。 - 过程 :
- 普通控件 :
DataContext是你的ViewModel。你想看错误,得写长路径去"找"错误。 - 错误模板 :WPF 在渲染模板时,做了一个**"偷梁换柱"** 的操作,它把模板的
DataContext强行切换成了Validation.Errors集合。
- 普通控件 :
- 结论 :因为"人"已经站在了"错误列表"里,所以不需要写
Path=(Validation.Errors),直接写[0](取第一个)即可。
步骤二.为需要设置模板的Textbox设置统一样式
XML
<Style x:Key="SensorValueStyle" TargetType="TextBox">
<!-- 默认不显示错误模板,{x:null}代表空引用 -->
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Style.Triggers>
<!-- 当验证出错时 (HasError = true) ,HasError属性是错误集合中有没有值 -->
<Trigger Property="Validation.HasError" Value="true">
<!-- 启用我们上面定义的错误模板 -->
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTextBoxTemplate}" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2" />
</Trigger>
</Style.Triggers>
</Style>
步骤三:需要使用输入验证的控件要做
XML
<TextBox Style="{StaticResource SensorValueStyle}">
<TextBox.Text>
<Binding Path="WeatherStationParametersExample.AirTemperature.Value">
//指定了验证规则,并且开启了业务逻辑验证,也就是ValidationRules
<Binding.ValidationRules>
<Validation:RestrictNegativeValues />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>