【问题标题】:Propagate validation from Model to View with Catel使用 Catel 将验证从模型传播到视图
【发布时间】:2014-12-01 10:14:39
【问题描述】:

我正在开发一个使用 Catel 的 MVVM (4.0.0) 实现的程序。我有一个模型类,它作为一些业务逻辑 - 它必须遵守关于它的属性值之一的规则。

例如,假设我有一个 Trap 类,它有一个 TargetValue 和一个 CurrentValue 属性。对于这个类,有一个规则必须强制 TargetValue 属性只能小于或等于 (CurrentValue。 (CurrentValue 是通过系统中的一些事件更新的)。

陷阱向用户公开(我有一个 VM,其中包含此类陷阱的列表并绑定到 UI 中的数据网格),她可以在其中更改 TargetValue

最初我想创建一个虚拟机来包装Trap 对象实例并在那里执行规则,但由于该规则实际上与模型(或业务逻辑,也就是说,我可能在任何地方都需要相同的规则)使用Trap 类)我认为最好在TargetValue 的属性设置器中实现它(我将检查新值的有效性并重置或不更改基础字段值)。

所以我应该在 setter 中执行此操作(这甚至是正确的实现吗?) - 有没有办法通知 VM(或 UI 直接)存在违规(例如通过设置单元格边框颜色或其他方式) ?

我想也许我可以使用RaisePropertyChangingRaisePropertyChanged 合作,ObservableObject 暴露虽然看起来很麻烦。

这种模式有合适的方法吗?

谢谢,

汤姆

【问题讨论】:

标签: c# wpf mvvm catel


【解决方案1】:

您不应该在 setter 中进行验证,因为 setter 负责设置对象的值。假设模型使用 INotifyPropertyChanged 和 IDataErrorInfo(或 INotifyDataErrorInfo),你可以用 Catel 做大事。

例如,在 ModelBase 中,您可以覆盖 ValidateFields 或 ValidateBusinessRules,甚至可以将自定义验证器应用于您的模型。然后ModelBase会自动为你实现变更通知和验证接口。

接下来,VM 包含陷阱,例如下面的 vm(请注意,此示例使用 Catel.Fody):

public class MyTrapViewModel : ViewModelBase
{
    public MyTrapViewModel(Trap trap)
    {
        Argument.IsNotNull(() => trap);

        Trap = trap;
    }

    [Model]
    [Expose("CurrentValue")]
    [Expose("TargetValue")]
    private Trap Trap { get; set; }
}

在本例中,您将Trap 属性定义为模型。这意味着 Catel 将观察它的通知并将视图模型上的属性映射到模型,反之亦然。现在很酷的事情来了:它不仅映射属性值,还映射验证规则。这意味着这个视图模型对于您的视图来说已经足够了,您可以在模型中实现验证。

【讨论】:

  • 我使用ObservableObject 作为模型类的基础,以保持轻松。据我所知,这个类不支持 require 机制。我可能会更改它(尽管由于“开销”而我不情愿,但我认为(如果我错了,请纠正我)验证可能会标记错误但仍然会更改基础值 - 或者它是否会完全“取消”更新? 如果它不取消,这个解决方案对我不起作用,因为其他线程我“监视”这个(陷阱)对象并且可能使用非法值。
  • 你仍然可以使用 ObservableObject,但是如果你想要验证,你 a) 需要自己编写或者 b) 使用经过多年优化的 Catel。这取决于你。
  • 我毫不怀疑Catel 的代码比我编写的任何代码都要好。我担心的是事件的实际顺序——使用内在验证会解决我的问题——如果存在验证错误——它会阻止实际更新属性吗?假设我使用 ValidateFields - 我在函数中检查条件的属性值,我可以引发错误/警告 - 但已经为时已晚,因为违反了条件......或者我错过了什么?
  • 只有在可以实际设置属性的情况下才能验证(并保持验证状态)。所以你可以设置属性,但是模型变得无效(这是正常行为)。
  • 感谢@Geert 反应迅速且信息丰富。您的意见(和 Catel)对开发 MVVM 非常有帮助!在任何情况下,对于这个用例,这个解决方案都不会起作用,因为陷阱实例永远不应该是无效的。它也被后台服务“监控”,如果在时间属性设置和验证之间激活将导致错误。这可能是我的设计......我认为在这种情况下我不应该使用属性而是一个函数来设置值,在这种情况下我可以进行验证并实现IDataErrorInfo
【解决方案2】:

在讨论了 Catel 的功能之后,由于操作顺序的原因,通过“常规”验证流程似乎无法解决问题:

  • 属性设置器
  • 验证字段
  • 错误信息已为 VM 准备就绪

解决办法应该是这样的:

  • CurrentValue 属性设置器应该是私有的,因此它不能被绑定到(直接或通过使用装饰等通过虚拟机)
  • 添加到TrapIDataErrorInfo(或INotifyDataErrorInfo)实现
  • 实现一个SetCurrentValue 函数,该函数验证数据并仅在合法时设置它。如果没有,请调用IDataErrorInfo 机制。
  • 在 UI 中 - 绑定到函数而不是属性(例如 http://msdn.microsoft.com/en-us/library/aa348824(v=vs.110).aspx

这种方法应该满足上述要求 - 属性值在任何用例中都不会无效,但仍然可以与 UI 协调。

【讨论】:

  • 什么是“常规验证流程”?如果没有错误,调用 Model.Validate(true) 然后将更改保存到 db/file?
猜你喜欢
  • 1970-01-01
  • 2017-10-04
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多