介绍
我创建了一个DecimalTextBox
用户控件,它ValidationRule
附加了 s 以防止空值,具有最小和最大范围,并且它具有事件处理程序以防止非十进制值。我用过
ValidatesOnTargetUpdated="True"
在绑定上,因为我希望立即激活验证(并且在最小值和最大值发生变化但没有重新评估验证之前我遇到了一个问题)。
我所做的 null 验证取决于“AllowNull”依赖属性的值:如果控件指定 true,则即使值为 null,该控件也是有效的。如果为 false,则不允许为 null。该属性的默认值为False
问题
true
在某个 UserControl 中使用它时,我将 AllowNull 设置为。不幸的是,由于ValidatesOnTargetUpdated
设置为true
,因此在 xaml 将 AllowNull 设置为 之前验证了控件true
,而它仍处于默认false
设置。
这会在加载之前导致错误,因为与文本的绑定TextBox
还没有解决,所以在加载之前它不允许为空,并且文本的值为空。
这一切都很好而且很花哨,因为在加载后使用新的 AllowNull 值(为真)重新评估验证并删除错误。
然而,红色的验证装饰仍然存在。不完全确定如何摆脱它。
代码 文本框用户控件的 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);
}
和验证规则(#注意:它们在 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
编辑
我做了一些更多的调查,发现了以下几点:
- 如果我在加载后将文本更改为大于最大值然后将其改回错误装饰器消失
- 如果我以编程方式将控件加载事件中的 Text 依赖属性的值更改为大于最大值并将其更改回来,则装饰器仍然存在。
- 如果我在加载后将文本更改为空值,然后将其改回,则装饰器仍然存在。
- 如果我在视图模型的构造函数中更改绑定到文本的视图模型属性的值,装饰器仍然存在
- 如果我将绑定到视图模型的构造函数内的文本的视图模型属性的值更改为大于最大值的值,然后将其更改回来,装饰器仍然存在。
- 如果我使用按钮将绑定到文本的视图模型属性的值更改为不同的值,然后将其更改回来,装饰器就会消失
- 如果我使用按钮将绑定到文本的视图模型属性的值更改为大于最大值的值,然后将其更改回来,装饰器就会消失
我仍然相当难过。我尝试过类似的方法,UpdateLayout()
并尝试将装饰器移动到不同的控件并使用Validation.SetValidationAdornerSite
. 我一直在努力,但我真的不知道该怎么做。
u_u
第二次编辑
好的,我在此期间所做的是在 TextBox 周围放置一个 AdornerDecorator,然后在文本框加载事件中将最大值更改为 1,将值更改为 2,然后将其更改回来以使文本框刷新。
这是可行的,但我讨厌这个想法,因为它的代码很糟糕。
然而,这种解决方案不再可行。我有一些代码正在对绑定到这些 DecimalTextBox 之一的属性之一的属性进行更改。然后因为在加载事件中属性被更改和更改回来,其他代码也在运行并导致错误。我必须找到一个更好的解决方案然后这个。
有谁知道如何刷新验证装饰器?
u_u