C# HasError为false时未清除验证错误装饰

C# HasError为false时未清除验证错误装饰,c#,wpf,validation,xaml,adorner,C#,Wpf,Validation,Xaml,Adorner,导言 我创建了一个DecimalTextBox用户控件,该控件附加了ValidationRules以防止为空,具有最小和最大范围,并具有事件处理程序以防止非十进制值。我用过 ValidatesOnTargetUpdated="True" 在绑定上,因为我希望立即激活验证(之前我遇到过一个问题,最小值和最大值都在更改,但验证没有重新评估) 我所做的空验证依赖于“AllowNull”依赖属性的值:如果控件指定true,那么即使该值为空,该控件也是有效的。如果为false,则不允许为null。此属性

导言

我创建了一个
DecimalTextBox
用户控件,该控件附加了
ValidationRule
s以防止为空,具有最小和最大范围,并具有事件处理程序以防止非十进制值。我用过

ValidatesOnTargetUpdated="True"
在绑定上,因为我希望立即激活验证(之前我遇到过一个问题,最小值和最大值都在更改,但验证没有重新评估)

我所做的空验证依赖于“AllowNull”依赖属性的值:如果控件指定true,那么即使该值为空,该控件也是有效的。如果为false,则不允许为null。此属性的默认值为
False


问题

在某个用户控件中使用AllowNull时,我将其设置为
true
。不幸的是,由于
validateOnTargetUpdated
设置为
true
,因此在xaml将AllowFull设置为
true
之前,控件将被验证,而它仍然处于默认的
false
设置中

这会在加载之前导致错误,因为与
文本框
文本的绑定也尚未解析,因此在加载之前不允许null,并且文本的值为null

这一切都很好,因为加载后,将使用新的AllowFull值(为true)重新评估验证,并消除错误

然而红色的装饰仍然存在。不完全确定如何摆脱它


代码 textbox用户控件的xaml:

<UserControl x:Class="WPFTest.DecimalTextBox"
         x:Name="DecimalBox"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:v="clr-namespace:ValidationRules"
         mc:Ignorable="d" 
         d:DesignHeight="25" d:DesignWidth="100" Initialized="DecimalBox_Initialized" >

    <TextBox x:Name="textbox">
        <TextBox.Text>
            <Binding ElementName="DecimalBox" TargetNullValue="" Path="Text" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" NotifyOnValidationError="True">
                <Binding.ValidationRules>
                    <v:DecimalRangeRule  ValidatesOnTargetUpdated="True">
                        <v:DecimalRangeRule.MinMaxRange>
                            <v:MinMaxValidationBindings x:Name="minMaxValidationBindings"/>
                        </v:DecimalRangeRule.MinMaxRange> 
                    </v:DecimalRangeRule>
                    <v:NotEmptyRule  ValidatesOnTargetUpdated="True">
                        <v:NotEmptyRule.AllowNull>
                            <v:AllowNullValidationBinding x:Name="allowNullValidationBindings"></v:AllowNullValidationBinding>
                        </v:NotEmptyRule.AllowNull>
                    </v:NotEmptyRule>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
</UserControl>

以及验证规则(#注意:它们位于ValidationRules命名空间中):

public类NotEmptyRule:ValidationRule
{
公共NotEmptyRule()
{
}
私有AllowNullValidationBinding_allowNullBinding;
公共AllowNullValidationBinding AllowNull
{
获取{return\u allowNullBinding;}
设置{u allowNullBinding=value;}
}
公共覆盖验证结果验证(对象值,CultureInfo CultureInfo)
{
如果(!\u allowNullBinding.AllowNull)
if(string.IsNullOrEmpty((string)值))
返回新的ValidationResult(false,
“值不能为null或空。”);
其他的
返回新的ValidationResult(true,null);
其他的
返回新的ValidationResult(true,null);
}
}
公共类小数范围规则:ValidationRule
{
私有MinMaxValidationBindings_bindableMinMax;
公共MinMaxValidationBindings MinMaxRange
{
获取{return\u bindableMinMax;}
设置
{
_bindableMinMax=值;
}
}
公共小数范围规则()
{
}
公共覆盖验证结果验证(对象值,CultureInfo CultureInfo)
{
十进制数=0;
if(十进制三分位((字符串)值,输出编号))
如果(_bindableMinMax.Min!=_bindableMinMax.Max | | _bindableMinMax.Min!=0)
{
如果((数字<_bindableMinMax.Min)| |(数字>_bindableMinMax.Max))
{
返回新的ValidationResult(false,
请在以下范围内输入小数:“+\u bindableMinMax.Min+”-“+\u bindableMinMax.Max+”;
}
其他的
{
返回新的ValidationResult(true,null);
}
}
其他的
返回新的ValidationResult(true,null);
其他的
返回新的ValidationResult(true,null);
}
}
公共类AllowNullValidationBinding:FrameworkElement
{
公共静态只读DependencyProperty allowNullProperty=DependencyProperty.Register(
“AllowNull”、typeof(bool)、typeof(AllowNullValidationBinding)、新UIPropertyMetadata(false));
公共布尔AllowNull
{
获取{return(bool)GetValue(allowNullProperty);}
set{SetValue(allowNullProperty,value);}
}
公共AllowNullValidationBinding()
{}
}
公共类MinMaxValidationBindings:FrameworkElement
{
公共静态只读DependencyProperty minProperty=DependencyProperty.Register(
“Min”、typeof(十进制)、typeof(MinMaxValidationBindings)、新UIPropertyMetadata(0.0m));
公共静态只读DependencyProperty maxProperty=DependencyProperty.Register(
“Max”、typeof(十进制)、typeof(MinMaxValidationBindings)、新UIPropertyMetadata(0.0m));
公共十进制最小值
{
获取{return(decimal)GetValue(minProperty);}
set{SetValue(minProperty,value);}
}
公共十进制最大值
{
get{return(十进制)GetValue(maxProperty);}
set{SetValue(maxProperty,value);}
}
公共MinMaxValidationBindings(){}
}
使用FrameworkElement绑定可以使我的ValidationRules具有要绑定的依赖项属性。这允许我在控件之外指定最小值和最大值


摘要

加载后,我使用
验证.GetHasError(DecimalBox)
(对于控件本身以及控件内部的
文本框
)检查了
HasError
,并生成false

我知道,如果我删除
validateOnTargetUpdated=“True”
将不会显示红色,但我需要它。那么,为什么验证被重新评估,但红色边框装饰没有消失呢

我不太了解Validation类或它的静态方法,但是其中是否有一些东西可以去除装饰。ClearInvalid方法不会有帮助,因为我没有提供它的错误

有什么想法吗

u_


编辑

我做了更多的调查,发现了以下几点:

  • 如果我将加载后的文本更改为大于最大值的量,然后
        public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(textboxcontrol), new PropertyMetadata());
        public static DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(decimal), typeof(DecimalTextBox), new PropertyMetadata(0M));
        public static DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(decimal), typeof(DecimalTextBox), new PropertyMetadata(0M));
        public static DependencyProperty AllowNullProperty = DependencyProperty.Register("AllowNull", typeof(bool), typeof(DecimalTextBox), new UIPropertyMetadata(false));
    
        public bool AllowNull
        {
            get { return (bool)GetValue(AllowNullProperty); }
            set { SetValue(AllowNullProperty, value); }
        }
        public decimal Minimum
        {
            get { return (decimal)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }
        public decimal Maximum
        {
            get { return (decimal)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }
        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
    
    
    
        private void DecimalBox_Initialized(object sender, EventArgs e)
        {
            Binding minBinding = new Binding("Minimum");
            minBinding.Source = this;
            Binding maxBinding = new Binding("Maximum");
            maxBinding.Source = this;
            Binding allownullBinding = new Binding("AllowNull");
            allownullBinding.Source = this;
    
            minMaxValidationBindings.SetBinding(ValidationRules.MinMaxValidationBindings.minProperty, minBinding);
            BindingOperations.SetBinding(minMaxValidationBindings, ValidationRules.MinMaxValidationBindings.maxProperty, maxBinding);
            BindingOperations.SetBinding(allowNullValidationBindings, ValidationRules.AllowNullValidationBinding.allowNullProperty, allownullBinding);
        }
    
    public class NotEmptyRule : ValidationRule
    {
    
        public NotEmptyRule()
        {
        }
        private AllowNullValidationBinding _allowNullBinding;
    
        public AllowNullValidationBinding AllowNull
        {
            get { return _allowNullBinding; }
            set { _allowNullBinding = value; }
        }
    
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (!_allowNullBinding.AllowNull)
                if (string.IsNullOrEmpty((string)value))
                    return new ValidationResult(false,
                      "Value cannot be null or empty.");
                else
                    return new ValidationResult(true, null);
    
            else
               return new ValidationResult(true, null);
    
        }
    }
    
    public class DecimalRangeRule : ValidationRule
    {
        private MinMaxValidationBindings _bindableMinMax;
        public MinMaxValidationBindings MinMaxRange
        {
            get { return _bindableMinMax; }
            set
            {
                _bindableMinMax = value;
    
            }
        }
    
    
        public DecimalRangeRule()
        {
    
        }
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
    
            decimal number = 0;
    
            if(decimal.TryParse((string)value,out number))
                if (_bindableMinMax.Min != _bindableMinMax.Max || _bindableMinMax.Min != 0)
                {
                    if ((number < _bindableMinMax.Min) || (number > _bindableMinMax.Max))
                    {
                        return new ValidationResult(false,
                          "Please enter an decimal in the range: " + _bindableMinMax.Min + " - " + _bindableMinMax.Max + ".");
                    }
                    else
                    {
                        return new ValidationResult(true, null);
                    }
                }
                else
                    return new ValidationResult(true, null);
            else
                return new ValidationResult(true, null);
        }
    }
    
    public class AllowNullValidationBinding:FrameworkElement
    {
         public static readonly DependencyProperty allowNullProperty = DependencyProperty.Register(
            "AllowNull", typeof(bool), typeof(AllowNullValidationBinding), new UIPropertyMetadata(false));
    
        public bool AllowNull
        {
            get{return (bool)GetValue(allowNullProperty);}
            set{SetValue(allowNullProperty,value);}
        }
        public AllowNullValidationBinding()
        {}
    }
    
    public class MinMaxValidationBindings : FrameworkElement
    {
        public static readonly DependencyProperty minProperty = DependencyProperty.Register(
            "Min", typeof(decimal), typeof(MinMaxValidationBindings), new UIPropertyMetadata(0.0m));
    
        public static readonly DependencyProperty maxProperty = DependencyProperty.Register(
            "Max", typeof(decimal), typeof(MinMaxValidationBindings), new UIPropertyMetadata(0.0m));
    
        public decimal Min
        {
            get { return (decimal)GetValue(minProperty); }
            set { SetValue(minProperty, value); }
        }
    
        public decimal Max
        {
            get { return (decimal)GetValue(maxProperty); }
            set { SetValue(maxProperty, value); }
        }
    
        public MinMaxValidationBindings() { }
    
    }
    
    Validation.ClearInvalid(SystemCode.GetBindingExpression(TextBox.TextProperty));
    
    var dp = SystemCode.GetBindingExpression(TextBox.TextProperty);
    dp.UpdateSource();
    
    var depPropGetter = typeof (Validation).GetField("ValidationAdornerProperty", BindingFlags.Static | BindingFlags.NonPublic);
    var validationAdornerProperty = (DependencyProperty)depPropGetter.GetValue(null);
    var adorner = (Adorner)DateActionDone.GetValue(validationAdornerProperty);
    
    if (adorner != null && Validation.GetHasError(MyControl))
    {
        var adorners = AdornerLayer.GetAdornerLayer(MyControl).GetAdorners(MyControl);
        if (adorners.Contains(adorner))
            AdornerLayer.GetAdornerLayer(MyControl).Remove(adorner);
    }
    
        string IDataErrorInfo.this[string columnName]
        {
            get
            {
                if (_refreshing) return "Refreshing";
                return ValidationEngine.For(this.GetType()).GetError(this, columnName);
            }
        }
    
        bool _refreshing = false;
        public void RefreshValidation()
        {
            _refreshing = true;
            this.NotifyOfPropertyChange(string.Empty);
            _refreshing = false;
            this.NotifyOfPropertyChange(string.Empty);
        }
    
    Control.UpdateLayout()