简体   繁体   English

Caliburn.Micro实现INotifyDataErrorInfo

[英]Caliburn.Micro implementing INotifyDataErrorInfo

I'm trying to find an elegant solution to implement INotifiyDataErrorInfo with Caliburn.Micro MVVM framework. 我正在尝试找到一种优雅的解决方案,以利用Caliburn.Micro MVVM框架实现INotifiyDataErrorInfo

I want to limit the amount of code that will be repeated in each VM that needs to implement the validation. 我想限制在每个需要实施验证的VM中重复的代码量。 I started by writing a class that inherits Screen and implements INotifiyDataErrorInfo . 我从编写一个继承Screen并实现INotifiyDataErrorInfo的类开始。 It works correctly and all is fine, until I need validation on a VM that is not a Screen , but a Conductor . 它可以正常工作,并且一切都很好,直到我需要在不是Screen而是Conductor的VM上进行验证为止。

Of course, I could make a class that inherits Conductor and implements INotifyDataErrorInfo but that's quite annoying as I would have to basically make my own version of all "base" classes of Caliburn.Micro. 当然,我可以制作一个继承Conductor并实现INotifyDataErrorInfo的类,但是这很烦人,因为我必须基本上自己INotifyDataErrorInfo Caliburn.Micro所有“基本”类的版本。

One solution I had was to keep the Screen base class and create a IValidator interface that I would inject into my VM, something like this: 我的一个解决方案是保留Screen基类,并创建一个IValidator接口,将其注入到我的VM中,如下所示:

public interface IValidator<T> where T : INotifyDataErrorInfo
{
    void Validates(T instance);

    IEnumerable GetErrors(string propertyName);
    bool HasErrors { get; }

    void Validate();
    void ValidateProperty<TValue>(TValue value, string propertyName = null);
    void ValidateProperty<TValue, TProperty>(TValue value, Expression<Func<TProperty>> property);
}

It will then used in the VM in this way. 然后,它将以这种方式在VM中使用。

public class CreateCarViewModel : Conductor<CreateCarViewModel>.Collection.OneActive, INotifyDataErrorInfo
{
   private readonly IValidator<CreateCarViewModel> validator;

   public CreateExperimentViewModel(IValidator<CreateCarViewModel> validator)
   {
       this.DisplayName = "Select a car";

       this.validator = validator;
       this.validator.Validates(this);
   }

   [Required]
   public string CarName
   {
       get
       {
           return this.carName;
       }
       set
       {
           if (this.carName != value)
           {
               this.carName = value;

               this.validator.ValidateProperty(value, () => this.CarName);
               this.NotifyOfPropertyChange(() => CarName);
           }
       }
   }

   public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

   public System.Collections.IEnumerable GetErrors(string propertyName)
   {
       return this.validator.GetErrors(propertyName);
   }

   public bool HasErrors
   {
       get { return this.validator.HasErrors; }
   }

   ...

}

This works pretty nicely, as it is very simple to implement the INotifyDataErrorInfo in the VMs, but the issue I have is triggering the ErrorChanged event. 这非常INotifyDataErrorInfo ,因为在VM中实现INotifyDataErrorInfo非常简单,但是我遇到的问题是触发ErrorChanged事件。 It must be triggered by the implementation of the IValidator as he is the one who knows when the errors have changed, and of course he cannot trigger directly. 它必须由IValidator的实现来触发,因为他是知道错误何时已更改的人,当然他不能直接触发。

One idea I have was to have an event in the IValidator and subscribe to it in the VM so that it can trigger its own event, but I find that it makes a lot of code for nothing. 我的一个想法是在IValidator有一个事件,然后在VM中订阅它,以便它可以触发自己的事件,但是我发现它无济于事地编写了很多代码。

Does anyone have a better idea? 有谁有更好的主意吗?

Thanks 谢谢

I wrote a small plugin for CM to enable fluent builder-style validation. 我为CM写了一个小插件,以启用流畅的生成器样式验证。 Maybe it will help you. 也许会对您有帮助。 Feel free to use it: https://github.com/AIexandr/Caliburn.Micro.Validation Example of usage: 随时使用它: https : //github.com/AIexandr/Caliburn.Micro.Validation使用示例:

public class PaymentEditorViewModel() : ValidatingScreen
{
  public PaymentEditorViewModel()
  {
    AddValidationRule(() => PaymentSum).Condition(() => PaymentSum <= 0).Message("Please enter payment sum");
  }

  #region PaymentSum property
  decimal _PaymentSum;
  public decimal PaymentSum
  {
    get
    {
      return _PaymentSum;
    }
    set
    {
      _PaymentSum = value;
      NotifyOfPropertyChange(() => PaymentSum);
    }
  }
  #endregion
}

The wireup code is not excessive is it, if you have the IValidator expose the same event as the VM, eg: 如果您让IValidator公开了与VM相同的事件,则联结代码是否过多,例如:

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public void Validate()
    {
        if (ErrorsChanged != null)
            ErrorsChanged(instance, new DataErrorsChangedEventArgs("someProperty"));
    }

and in the VM: 并在VM中:

 validator.ErrorsChanged += (sender, args) => ErrorsChanged(sender, args);

But I guess you already answered your own question without telling us ;) 但是我想你已经回答了自己的问题了,没有告诉我们;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM