【问题标题】:Handle Convert exception with INotifyDataErrorInfo使用 INotifyDataErrorInfo 处理转换异常
【发布时间】:2019-02-18 07:24:32
【问题描述】:

我的视图中有一个文本框,用户可以在其中输入 TCP 端口值。 文本框直接绑定到模型的端口属性(类型为 Int32),因为 ViewModel 提供了完整的绑定模型。模型继承自 ObservableObject,因此直接绑定到它运行良好。

我在我的模型中使用 INotifyDataErrorInfo,并且 Port 属性验证属性设置器中的值(它必须大于 0)。

我的视图中有一个按钮,它绑定到模型的 HasError() 方法并根据 HasError() 的布尔返回值设置其 IsEnabled 状态

现在问题来了,如果用户键入“Hello world”,WPF 绑定引擎会抛出异常。在这种情况下,永远不会执行属性设置器,因此 HasError() 永远不会针对这种无效输入更改为 true。

我当然可以为视图中的文本框设置“ValidatesOnExceptions=True”,以便在这种情况下至少让文本框显示其验证错误模板,但按钮仍然不会被禁用。

问题,如何处理这些自动转换失败导致 ViewModel / Model 的验证永远不会执行的情况的推荐解决方案是什么?

我可以接受这样的解决方案,即验证在文本框字符串而不是 Int32 类型上执行,在 WPF 绑定引擎执行失败的自动类型转换为 Int32 之前执行。

我真正想避免的一个解决方案是让我的 ViewModel / Model 属性始终为字符串类型,在 setter 中验证字符串,然后尝试手动将其转换为正确的类型(在本例中为 Int32)。必须有更好的解决方案来避免所有此类手动强制转换。

使用 ValidationRules 时,可以告诉验证引擎在自动转换之前执行 ValidationRule。我真正想要的是一种在自动转换完成之前执行 INotifyDataErrorInfo 验证的方法。

【问题讨论】:

  • 什么问题的推荐解决方案是什么?显然,您的视图模型无法处理这种异常,因为它从未涉及。当您尝试将字符串转换为 int 时发生的异常已由框架为您处理,那么您的实际问题是什么?
  • 是的,如何在自动转换之前执行验证。我更新了问题以更清楚。
  • 在视图中使用 ValidationRule。请参考我的回答。

标签: c# .net wpf validation data-binding


【解决方案1】:

您遇到的困难说明了为什么您应该从您的视图直接绑定到您的模型。视图模型的重点是在视图和与底层模型交互的代理之间来回转换表示关注点。

例如:如果您的视图为端口提供了一个字符串,那么视图模型将对其进行验证并将其转换(如果有效)为模型的整数,或者通过任何错误通知机制通知视图无效输入。

【讨论】:

  • 好的,所以如果没有类型之间的所有此类手动翻译,就无法解决这个问题。当模型已经支持绑定(ObservableObject)时,似乎有很多不必要的代码
  • 您不应该将视图模型属性的类型从 int 更改为 string 如果这是您的想法。视图模型不负责处理视图设置其属性的方式。
  • @mm8 请解释一下,如果我不将 VM 属性类型更改为字符串,则永远不会执行在 VM 属性中执行的验证。我现在在 VM 中使用字符串,然后在模型中设置值之前将其转换为 Int。但它也有一个缺点。如果模型更改了 Int 值,除非我明确订阅 VM 中的模型事件,否则绑定不会更新。不是一个干净的方法来解决它,但似乎没有更好的方法。
  • @Johan:您可以使用附加行为绑定到 Validation.HasError 属性:stackoverflow.com/questions/22827437/…
【解决方案2】:

int 属性不能设置为 int 值以外的任何值。当您尝试将其设置为“Hello world”时,绑定引擎会捕获异常并为您处理它。

如果您想自己执行验证,或自定义错误消息,您可以定义自定义ValidationRule 并将其ValidationStep 属性设置为RawProposedValue,以便在值转换发生之前应用它:

public class StringToIntValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        int i;
        if (int.TryParse(value.ToString(), out i))
            return new ValidationResult(true, null);

        return new ValidationResult(false, "Please enter a valid integer value.");
    }
}

XAML:

<TextBox>
    <TextBox.Text>
        <Binding Path="Age" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:StringToIntValidationRule ValidationStep="RawProposedValue"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

请注意,这种验证或自定义不涉及视图模型。这是一个框架/视图/控制的东西。

【讨论】:

  • 是的,我知道 ValidationRule。问题是正如您所说的,VM 不涉及,如果视图中存在任何验证错误并且还基于其他对象的某些状态,我希望禁用其他元素(例如按钮)。我没有看到使用 ValidationRule 解决该问题的好方法。
  • 目前按钮命令绑定到 RelayCommand。而relay命令根据INotifyDataErrorInfo的HasError和其他对象的状态来设置按钮的启用状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 2017-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多