【发布时间】:2019-03-02 00:50:27
【问题描述】:
我在 TextBox 上使用验证规则来验证用户输入字符串。 Text 绑定到视图模型上的浮点属性,并且 WPF 绑定引擎非常热衷于为我自动将字符串转换为浮点数。
但是,当验证失败时,绑定似乎会读回旧值。这导致文本框周围出现红色边框,即使文本已恢复为最后一个可接受的浮点值。
问题:如何确保验证失败时绑定引擎不会自动覆盖错误的输入文本? 绑定必须是双向的。
我应该提到我在我的 ValidationRule 中做了一个小技巧,我让它从视图模型定位器中找到当前视图模型,并在视图模型上使用 INotifyDataErrorInfo 方法。我发现这是一个很好的解决方案,因为这意味着 ViewModel HasError 将为我收集所有验证错误(它让我在设置属性时在验证规则或视图模型中应用验证)。让验证的好处规则在视图模型上使用 INotifyDataErrorInfo 应用验证是可以在从字符串自动转换为浮点数之前应用验证,确保即使用户键入“Hello World”导致异常也执行验证(被绑定引擎)在自动转换为浮动期间。这让我可以在虚拟机上保持浮动的属性类型,并且仍然可以执行验证。
XAML
<TextBox Grid.Row="2" Grid.Column="2" x:Name="txtPreHeight"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Center"
Template="{DynamicResource TextBoxBaseControlTemplateMainScreen}">
<TextBox.Text>
<Binding
Path="PreHeight"
ValidatesOnExceptions="False"
NotifyOnValidationError="True"
ValidatesOnNotifyDataErrors="True"
UpdateSourceTrigger="LostFocus"
>
<Binding.ValidationRules>
<validationrules:PreHeightValidationRule ViewModelType="GotoPositionViewModel" Min="0" Max="100" ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<i:Interaction.Triggers>
<helper:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}">
<cmd:EventToCommand Command="{Binding SetFocusOnValidationErrorCommand}"
PassEventArgsToCommand="True" />
</helper:RoutedEventTrigger>
</i:Interaction.Triggers>
</TextBox>
验证规则
class PreHeightValidationRule : ValidationRule
{
private ValidationService validationService_;
private Int32 min_ = Int32.MaxValue;
private Int32 max_ = Int32.MinValue;
private string viewModelType_ = null;
public PreHeightValidationRule()
{
validationService_ = ServiceLocator.Current.GetInstance<Validation.ValidationService>();
}
public Int32 Min
{
get { return min_; }
set { min_ = value; }
}
public Int32 Max
{
get { return max_; }
set { max_ = value; }
}
public string ViewModelType
{
get { return viewModelType_; }
set { viewModelType_ = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingExpressionBase owner)
{
ValidationResult result = base.Validate(value, cultureInfo, owner);
ViewModel.ViewModelBaseWithNavigation vm;
System.Reflection.Assembly asm = typeof(ViewModelLocator).Assembly;
Type type = null;
if (type == null)
type = asm.GetType(ViewModelType);
if (type == null)
type = asm.GetType("TeachpendantControl.ViewModel." + ViewModelType);
vm = (ViewModel.ViewModelBaseWithNavigation)ServiceLocator.Current.GetInstance(type);
ICollection<string> validationErrors = new List<string>();
try
{
validationService_.ValidatePreHeight(value.ToString(), ref validationErrors, Min, Max);
}
catch (Exception e)
{
validationErrors.Add("Failed to validate, Exception thrown " + e.Message);
}
finally
{
vm.UpdateValidationForProperty(((BindingExpression)owner).ResolvedSourcePropertyName, validationErrors, validationErrors.Count == 0);
}
return new ValidationResult(validationErrors.Count == 0, validationErrors);
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return new ValidationResult(false, null);
}
}
【问题讨论】:
-
@mm8 请在下面查看我的回答。我设法解决了它。非常值得看看这种方法。与以“正常”方式使用 ValidationRule 或仅在 VM 属性设置器中使用验证相比,它具有相当多的优点,并且它允许 VM 属性成为请求的类型(无需使用额外的字符串层来强制对可能导致绑定异常的输入进行验证)
标签: c# .net wpf mvvm-light validationrules