引言
我创建了一个DecimalTextBox
用户控件,它附加了ValidationRule
以防止空值,具有最小和最大范围,并且它具有事件处理程序以防止非十进制值。我用过
ValidatesOnTargetUpdated="True"
关于绑定,因为我希望立即激活验证(在最小值和最大值发生变化之前我遇到了问题,但验证没有被重新评估)。
我所做的空验证取决于“AllowNull”依赖项属性的值:如果控件指定为true,则即使值为null,控件也是有效的。如果为false,则不允许为null。此属性的默认值为False
问题
在某个UserControl中使用它时,我将AllowNull设置为true
。不幸的是,因为ValidatesOnTargetUpdated
设置为true
,所以在xaml将AllowNull设置为true
之前验证控件,同时它仍然处于默认false
设置。
这会在加载前导致错误,因为对TextBox
文本的绑定尚未解析,因此在加载之前它不允许为null,并且文本的值为null。
这一切都很好,花花公子,因为在加载后,使用新的AllowNull值(true)重新评估验证,并删除错误。
然而红色验证装饰仍然存在。不完全确定如何摆脱它。
代码 textbox usercontrol的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>
控件背后的代码:
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);
}
验证规则(#Note:它们在ValidationRules命名空间内):
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() { }
}
使用FrameworkElement绑定,因此我的ValidationRules可以具有要绑定的依赖项属性。这允许我指定控件之外的最小值和最大值。
概要
我在加载后使用HasError
(对于两者控件本身以及它的内部Validation.GetHasError(DecimalBox)
)检查TextBox
并生成假的。
我知道如果我删除ValidatesOnTargetUpdated="True"
红色不会出现,但我需要它。那么为什么重新评估验证,但红色边框装饰没有消失?
我对Validation类或其静态方法知之甚少,但是有什么东西要删除装饰。 ClearInvalid方法不会有帮助,因为我没有提供错误。
有什么想法吗?
u_u
修改
我做了更多调查,发现了以下内容:
我还是很难过。我尝试了UpdateLayout()
之类的方法,并尝试将装饰器移动到不同的控件并使用Validation.SetValidationAdornerSite
将其移回。我一直在努力,但我不知道该怎么做。
u_u
2ND EDIT
好的,我在此期间所做的是在TextBox周围放置一个AdornerDecorator,然后在文本框中加载事件将最大值更改为1,将值更改为2,然后将其更改回来以使文本框刷新。 / p>
这是有效的,但我讨厌这个想法导致其可怕的代码。
然而,这种解决方案已不再适用。我有一些代码正在对属性的一些属性进行更改,这些属性绑定到这些DecimalTextBox之一。然后因为属性在load事件中被更改并更改回来,其他代码也在运行并导致错误。我必须找到一个更好的解决方案。
有谁知道如何刷新验证装饰器?
u_u
答案 0 :(得分:2)
以下是我遇到问题并且无法在其上找到任何内容的问题的几种解决方法,我怀疑它是由于竞争条件导致的框架中的某个地方的错误但是不能#&# 39;找不到任何东西支持它。
因为您知道您的字段没有错误,所以可以执行此操作来迭代控件并杀死广告
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);
}
或者你可以通过反射来调用Validation.ShowAdornerHelper
方法,我没有直接尝试过,所以无法为编写代码而烦恼。
我们可以利用您的发现,即使绑定无效然后再次生效将为我们清除装饰。
这是我决定采用的解决方案,并且恰好非常有效。
因为我在我的基本视图模型中使用了IDataErrorInfo,所以我可以根据你的验证处理方式做一些事情,你可能会更难以重新验证它们。
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);
}
答案 1 :(得分:0)
尝试删除控件的事件LayoutUpdated上的错误(在事件上放置一个标志只执行一次)
Validation.ClearInvalid(SystemCode.GetBindingExpression(TextBox.TextProperty));
然后重新评估您的验证规则(刷新绑定)。
var dp = SystemCode.GetBindingExpression(TextBox.TextProperty);
dp.UpdateSource();
答案 2 :(得分:0)
我有一个类似的问题,即使根本的错误发生,装饰器也不会消失。
我发现的最终解决方法是通过调用
来强制更新布局Control.UpdateLayout()
以某种方式迫使WPF同步备份。我在Control_Loaded
事件处理程序上进行了更新,但它有时也可以解决此问题。