BusinessLayerValidation业务层逻辑验证
练习:
- 如何在业务层数据视图类编写验证代码
- Xaml中使用DataErrorValidationRule
样式中工具提示显示验证错误信息,与之前代码一样
<!--The tool tip for the TextBox to display the validation error message.-->
<Style x:Key="TextBoxInError" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
此处多了ValidatesOnExceptions="True"
- 通过设置此属性,可以以另一种方式显式使用 ExceptionValidationRule 元素。
- ExceptionValidationRule 类是内置的验证规则,它检查在源属性更新过程中引发的异常。 如果引发异常,则绑定引擎将对异常创建 ValidationError 并将其添加到绑定元素的 Validation.Errors 集合中。
<TextBox Style="{StaticResource TextBoxInError}">
<TextBox.Text>
<!--By setting ValidatesOnExceptions to True, it checks for exceptions
that are thrown during the update of the source property.
An alternative syntax is to add <ExceptionValidationRule/> within
the <Binding.ValidationRules> section.-->
<Binding Path="Age" Source="{StaticResource Data}"
ValidatesOnExceptions="True"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<!--DataErrorValidationRule checks for validation
errors raised by the IDataErrorInfo object.-->
<!--Alternatively, you can set ValidationOnDataErrors="True" on the Binding.-->
<DataErrorValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
DataErrorValidationRule:表示一个规则,该规则检查由源对象的 IDataErrorInfo 实现所引发的错误。
使用 WPF 数据绑定模型可以将 ValidationRules 与 Binding 对象相关联。 如果源对象实现 IDataErrorInfo 接口,则可以使用内置的规则 DataErrorValidationRule 来检查由 IDataErrorInfo 实现引发的错误。
再来看实现IDataErrorInfo接口类
public class Person : IDataErrorInfo
{
public int Age { get; set; }
public string Error => null;
public string this[string name]
{
get
{
string result = null;
if (name == "Age")
{
if (Age < 0 || Age > 150)
{
result = "Age must not be less than 0 or greater than 150.";
}
}
return result;
}
}
}
ValidationItemsInItemsControl在一组项中的多项验证
练习:
- 价格为正整数的验证类
- 大于现在的日期验证类
- BindingGroup的事务验证结果
HeaderedContentControl样式:
<Style TargetType="HeaderedContentControl">
<Setter Property="Margin" Value="2"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<DockPanel LastChildFill="False">
<ContentPresenter ContentSource="Header" DockPanel.Dock="Left" Focusable="False" VerticalAlignment="Center"/>
<ContentPresenter ContentSource="Content" Margin="5,0,0,0" DockPanel.Dock="Right" VerticalAlignment="Center"/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
正整数验证xaml
<HeaderedContentControl Header="Price">
<TextBox Name="priceField" Width="150">
<TextBox.Text>
<Binding Path="Price" Mode="TwoWay" >
<Binding.ValidationRules>
<local:PriceIsAPositiveNumber/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</HeaderedContentControl>
大于现在日期xaml
<HeaderedContentControl Header="Date Offer Ends">
<TextBox Name="dateField" Width="150" >
<TextBox.Text>
<Binding Path="OfferExpires" StringFormat="d" >
<Binding.ValidationRules>
<local:FutureDateRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</HeaderedContentControl>
容器的验证错误控件及绑定源加载
<StackPanel Name="stackPanel1" Margin="10"
Loaded="StackPanel1_Loaded"
Validation.Error="ItemError">
绑定组xaml
<StackPanel.BindingGroup>
<BindingGroup NotifyOnValidationError="True">
<BindingGroup.ValidationRules>
<local:ValidateDateAndPrice ValidationStep="ConvertedProposedValue" />
</BindingGroup.ValidationRules>
</BindingGroup>
</StackPanel.BindingGroup>
BindingGroup.NotifyOnValidationError:获取或设置在 ValidationRule 的状态更改时是否发生 Validation.Error 事件.
示例创建 BindingGroup 并将 NotifyOnValidationError 设置为 true,以便应用程序可以在 ValidationRule 失败时处理 Validation.Error 事件。
public class ValidateDateAndPrice : ValidationRule
{
// Ensure that an item over $100 is available for at least 7 days.
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bg = value as BindingGroup;
// Get the source object.
var item = bg?.Items[0] as PurchaseItem;
object doubleValue;
object dateTimeValue;
// Get the proposed values for Price and OfferExpires.
var priceResult = bg.TryGetValue(item, "Price", out doubleValue);
var dateResult = bg.TryGetValue(item, "OfferExpires", out dateTimeValue);
if (!priceResult || !dateResult)
{
return new ValidationResult(false, "Properties not found");
}
var price = (double) doubleValue;
var offerExpires = (DateTime) dateTimeValue;
// Check that an item over $100 is available for at least 7 days.
if (price > 100)
{
if (offerExpires < DateTime.Today + new TimeSpan(7, 0, 0, 0))
{
return new ValidationResult(false, "Items over $100 must be available for at least 7 days.");
}
}
return ValidationResult.ValidResult;
}
}
- 将 ValidationStep 设置为 ConvertedProposedValue,这样,当该验证规则运行时, Validate 方法便可访问属于源属性类型的值。
- 当 PriceIsAPositiveNumber 和 FutureDateRule 规则运行时,每个 Validate 方法中的值均为字符串,原因是先运行规则,然后才将这些值转换为各自的类型。
public class PriceIsAPositiveNumber : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
try
{
var price = Convert.ToDouble(value);
if (price < 0)
{
return new ValidationResult(false, "Price must be positive.");
}
return ValidationResult.ValidResult;
}
catch (Exception)
{
// Exception thrown by Conversion - value is not a number.
return new ValidationResult(false, "Price must be a number.");
}
}
}
internal class FutureDateRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
DateTime date;
try
{
date = DateTime.Parse(value?.ToString());
}
catch (FormatException)
{
return new ValidationResult(false, "Value is not a valid date.");
}
return DateTime.Now.Date > date
? new ValidationResult(false, "Please enter a date in the future.")
: ValidationResult.ValidResult;
}
}
关键执行组验证代码:
private void Submit_Click(object sender, RoutedEventArgs e)
{
if (stackPanel1.BindingGroup.CommitEdit())
{
MessageBox.Show("Item submitted");
stackPanel1.BindingGroup.BeginEdit();
}
}
BindingGroup.CommitEdit :运行所有 ValidationRule 对象,并且在所有验证规则都成功时,更新绑定源。
BindingGroup.BeginEdit :开始 BindingGroup 中源上的编辑事务。
以下为放弃验证按钮
private void Cancel_Click(object sender, RoutedEventArgs e)
{
// Cancel the pending changes and begin a new edit transaction.
stackPanel1.BindingGroup.CancelEdit();
stackPanel1.BindingGroup.BeginEdit();
}
如果 BindingGroup 中的源支持放弃挂起的更改,则可以调用 BeginEdit 以开始编辑事务,调用 CommitEdit 以保存挂起的更改,调用 CancelEdit 以放弃挂起的更改。
最后,容器加载Validation.Error 附加事件:当所绑定的元素遇到验证错误时发生,但只适用于将 NotifyOnValidationError 值设置为 true 的绑定。
// This event occurs when a ValidationRule in the BindingGroup
// or in a Binding fails.
private void ItemError(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
{
MessageBox.Show(e.Error.ErrorContent.ToString());
}
}
扩展知识:ValidationStep 枚举:指定 ValidationRule 何时运行。
- 此规则的 ValidationStep 设置为 ConvertedProposedValue,这样,当其运行时, Validate 方法将能访问具有源属性类型的值。
- 当 PriceIsAPositiveNumber 和 FutureDateRule 规则运行时,每个 Validate 方法中的值均为字符串,原因是 ValidationStep 的默认值为 RawProposedValue。 因此,规则会在将值转换为其各自的类型之前运行。
- 在 Validate 方法中,Price 属性的类型为 Double,OfferExpires 属性的类型为 DateTime。 在 ValidationRule 运行之前,字符串已转换为其各自的类型。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。