Reputation: 1188
I am working on a program using Catel's MVVM (4.0.0) implementation. I have a model class which as some business logic - it must observe a rule regarding one of it's properties values.
For example, let say I have a Trap
class which has a TargetValue
and a CurrentValue
properties. For this class there is a rule that must enforce that the TargetValue
property can only be less then or equal (<=) CurrentValue
. (The CurrentValue
is updated via some events in the system).
The traps are exposed to the user (I have a VM which holds a list of such traps and is bound to a data grid in the UI) where she can change TargetValue
.
Initially I though to create a VM to wrap the Trap
object instance and enforce the rule there but since the rule is actually related more to the model (or business logic, that is, I may need the same rule everywhere I use Trap
class) I thought it better to implement it in the property setter for TargetValue
(where I will check the validity of the new value and either reset or not change the underlying field value).
So supposedly I do it in the setter (is that even a correct implementation?) - is there a way to notify the VM (or UI directly) that there was a violation (for example by setting cell border color or something)?
I thought that maybe I can use the RaisePropertyChanging
in collaboration with RaisePropertyChanged
that ObservableObject
exposes though it seems cumbersome.
Is there a proper way for such a pattern?
Thanks,
Tomer
Upvotes: 1
Views: 655
Reputation: 1188
Following the discussion regarding Catel's capabilities it seems that going through "regular" validation flow would not do the trick here due to the order of operations:
What the solution should be is as follows:
CurrentValue
property setter should be private so that it cannot be bound to (either directly or through the VM by using decorations etcTrap
class IDataErrorInfo
(or INotifyDataErrorInfo
) implementationSetCurrentValue
function which validates data and only sets it if legal. In case not, invoke IDataErrorInfo
mechanism.This approach should answer the requirements above - the property value is never invalid in any use case but still it is possible to coordinate with the UI.
Upvotes: 2
Reputation: 5724
You should not validate in the setter because the setter is responsible for setting the value of the object. Assuming that the model uses INotifyPropertyChanged and IDataErrorInfo (or INotifyDataErrorInfo), you can do big things with Catel.
For example, in the ModelBase you can override the ValidateFields or ValidateBusinessRules or even apply a custom validator to your models. Then the ModelBase will automatically implement the change notifications and validation interfaces for you.
Next, the VM contains the trap, for example the vm below (note that this example uses 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; }
}
In this example, you define the Trap
property as model. This means that Catel will watch it for notifications and map the properties on the view model to the model and vice versa. Now comes the cool thing: it doesn't only map the property values, it maps the validation rules as well. This means that this view model would be sufficient for your views and you can implement the validation in the models.
Upvotes: 2