WPF 数据验证

ExceptionValidationRule 验证规则

ExceptionValidationRule 是预先构建的验证规则,它向WPF报告所以的异常。要使用 ExceptionValidationRule 验证规则,必须将它添加到 Binding.ValidationRules 集合中

<Window x:Class="WpfApplication1.ValidationRuleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="ValidationRuleWindow" Height="300" Width="300">
    <Window.DataContext>
        <local:Product />
    </Window.DataContext>
    <StackPanel>
        <TextBox Margin="5">
            <TextBox.Text>
                <Binding Path="UnitCost" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>
</Window>

>

namespace WpfApplication1
{

    public class Product : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private decimal unitCost;

        public decimal UnitCost
        {
            get { return unitCost; }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("单价不能为负数");
                }
                else
                {
                    unitCost = value;
                    this.RaisePropertyChanged("UnitCost");
                }
            }
        }

    }
}

结果是,当试图输入一个非整数时,则引发异常。 这个示例同时使用了值转换和验证规则。通常是在转换值之前执行验证,但 ExceptionValidationRule 验证是个例外。它捕获任何位置发生的异常,包括当编辑的值不能转换成正确数据类型时发生异常、由属性设置器抛出的异常以及由值转换器抛出的异常。

DataErrorValidationRule 验证规则

有时候我们可能不喜欢通过引发异常来指示用户输入错误。这可能是因为一下几个原因:异常不是由用户输入错误造成的,而可能是由于多个值之间的交互造成的,并且有时为了进行进一步处理,保存不正确的数值是值得的,而不是完全拒绝它们。

namespace WpfApplication1
{

    public class Product : INotifyPropertyChanged, IDataErrorInfo
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private decimal unitCost;

        public decimal UnitCost
        {
            get { return unitCost; }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("单价不能为负数");
                }
                else
                {
                    unitCost = value;
                    this.RaisePropertyChanged("UnitCost");
                }
            }
        }

        //WPF 不使用该属性
        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get {
                if (columnName == "UnitCost")
                {
                    if (unitCost < 0)
                        return "单价不能小于 0";
                }

                return string.Empty;
            }
        }
    }
}

为了告知WPF使用 IDataErrorInfo 接口,并且当修改了一个属性后使用该接口检查错误,必须为 Binding.ValidationRules 集合添加 DataErrorValidationRule 验证规则

<Window x:Class="WpfApplication1.ValidationRuleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="ValidationRuleWindow" Height="300" Width="300">
    <Window.DataContext>
        <local:Product />
    </Window.DataContext>
    <StackPanel>
        <TextBox Margin="5">
            <TextBox.Text>
                <Binding Path="UnitCost" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <!--<ExceptionValidationRule />-->
                        <DataErrorValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>
</Window>

自定义验证规则

应用自定义验证规则的方法和应用自定义转换器的方法类似。该方法定义了一个 ValidationRule 的类,并且为了执行验证重写 Validate 方法。

public class PositivePriceRule:ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        decimal price = 0;
        decimal maxPrice = Decimal.MaxValue;

        if (decimal.TryParse(value.ToString(), out price))
        {
            if (price >= 0 && price < maxPrice)
            {
                return new ValidationResult(true, null);
            } 
        }

        return new ValidationResult(false, "单价不能为负值");
    }
}

public class Product : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private decimal unitCost;

    public decimal UnitCost
    {
        get { return unitCost; }
        set
        {
            if (value < 0)
            {
                throw new ArgumentException("单价不能为负数");
            }
            else
            {
                unitCost = value;
                this.RaisePropertyChanged("UnitCost");
            }
        }
    }

}

>

<Window x:Class="WpfApplication1.ValidationRuleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="ValidationRuleWindow" Height="300" Width="300">
    <Window.DataContext>
        <local:Product />
    </Window.DataContext>
    <StackPanel>
        <TextBox Margin="5">
            <TextBox.Text>
                <Binding Path="UnitCost" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <!--<ExceptionValidationRule />-->
                        <!--<DataErrorValidationRule />-->
                        <local:PositivePriceRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>
</Window>

Bindding.ValidationRules 集合可包含任何数量的验证规则。将值提交时,WPF将按顺序检查每个验证规则。

当使用PositivePriceRule 验证规则执行验证时,其行为和使用 ExceptionValidationRule验证规则的行为相同——文本框使用黑色轮廓 ,设置HasError 和 Error 属性,并引发 Error 事件。给用户提供一些更有意义的帮助,需要添加一些代码或自己定义 ErrorTemplate 模板。

响应与获取验证错误

前面的示例中,有关用户接受到错误的唯一指示是在违反规则的文本框周围的红色轮廓。为了提供更多信息,可以处理 Error 事件,但存储或清除错误时会引发该事件,但前提是必须确保已将 Binding.NotifyOnValidationError 属性设置为 True。

<Binding Path="UnitCost" NotifyOnValidationError="True"> 

Error 事件是一个使用冒泡策略的路由事件,所以可以通过在父容器中关联事件处理程序为多个控件处理 Error 事件

<StackPanel x:Name="stackProductDetails" Validation.Error="stackProductDetails_Error">

事件代码

private void stackProductDetails_Error(object sender, ValidationErrorEventArgs e)
    {
        if (e.Action == ValidationErrorEventAction.Added)
        {
            MessageBox.Show(e.Error.ErrorContent.ToString());
        }  
    }

在某些情况下,可能希望获取当前窗口(或窗口中的给定容器)中所有未处理的列表。这项任务较简单——就是遍历元素树,测试每个元素的 Validation.HasError 属性。

下面代码演示了一个专门查找 TextBox 对象中非法数据的示例:

private void GetErrors(StringBuilder sb, DependencyObject obj){
        foreach (object child in LogicalTreeHelper.GetChildren(obj)){
            TextBox element = child as TextBox;
            if (element == null) 
                continue;
            if (Validation.GetHasError(element)){
                sb.Append(element.Text + " has errors:\r\n");
                foreach (ValidationError error in Validation.GetErrors(element)){
                    sb.Append(" " + error.ErrorContent.ToString());
                    sb.Append("\r\n");
                }
            }
        }
    }

    private bool FromHasErrors(out string message){
        StringBuilder sb = new StringBuilder();
        GetErrors(sb, stackProductDetails);
        message = sb.ToString();
        return message != "";
    }

    private void cmdOK_Click(object sender, RoutedEventArgs e){
        string message;
        if (FromHasErrors(out message)){
            MessageBox.Show(message);
        }
        else{
            // ...
        }
    }
}

错误模板

<Window x:Class="WpfApplication1.ValidationRuleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="ValidationRuleWindow" Height="300" Width="300">
    <Window.DataContext>
        <local:Product />
    </Window.DataContext>
    <Window.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock DockPanel.Dock="Bottom" Foreground="Red"
                                       Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
                            <Border BorderBrush="Red" BorderThickness="1">
                                <AdornedElementPlaceholder x:Name="adorned"/>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel x:Name="stackProductDetails" Validation.Error="stackProductDetails_Error">
        <TextBox Margin="5">
            <TextBox.Text>
                <Binding Path="UnitCost" Mode="TwoWay" NotifyOnValidationError="False" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <!--<ExceptionValidationRule />-->
                        <!--<DataErrorValidationRule />-->
                        <local:PositivePriceRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>
</Window>

AdornedElementPlaceholder 是这种技术能够工作的粘合剂。她代表控件自身,位于元素层中。通过使用 AdornedElementPlaceholder 元素,能够在文本框背后安排自己的内容。

站内公告

A PHP Error was encountered

Severity: Core Warning

Message: PHP Startup: zip: Unable to initialize module Module compiled with module API=20060613 PHP compiled with module API=20090626 These options need to match

Filename: Unknown

Line Number: 0

Backtrace: