BusinessLayerValidation业务层逻辑验证

练习:

  1. 如何在业务层数据视图类编写验证代码
  2. Xaml中使用DataErrorValidationRule

clipboard.png

clipboard.png
样式中工具提示显示验证错误信息,与之前代码一样

<!--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"

  1. 通过设置此属性,可以以另一种方式显式使用 ExceptionValidationRule 元素。
  2. 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在一组项中的多项验证

练习:

  1. 价格为正整数的验证类
  2. 大于现在的日期验证类
  3. BindingGroup的事务验证结果

clipboard.png

clipboard.png

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;
    }
}
  1. 将 ValidationStep 设置为 ConvertedProposedValue,这样,当该验证规则运行时, Validate 方法便可访问属于源属性类型的值。
  2. 当 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 何时运行。

  1. 此规则的 ValidationStep 设置为 ConvertedProposedValue,这样,当其运行时, Validate 方法将能访问具有源属性类型的值。
  2. 当 PriceIsAPositiveNumber 和 FutureDateRule 规则运行时,每个 Validate 方法中的值均为字符串,原因是 ValidationStep 的默认值为 RawProposedValue。 因此,规则会在将值转换为其各自的类型之前运行。
  3. 在 Validate 方法中,Price 属性的类型为 Double,OfferExpires 属性的类型为 DateTime。 在 ValidationRule 运行之前,字符串已转换为其各自的类型。

李志玮
22 声望34 粉丝

求索~~


引用和评论

0 条评论