Search code examples
wpfvalidationmvvmcaliburn.microidataerrorinfo

MVVM pattern: IDataErrorInfo validation doesn't work if we have wrapped model inside another model


The problem is that the red rectangle is not shown on the UI.

I use Caliburn.Micro and this is is what I have in my View:

<StackPanel Grid.Row="0">
     <Label>Customer name:</Label>
     <TextBox x:Name="txtName" Text="{Binding Target.Model.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="136" HorizontalAlignment="Left"/>
</StackPanel>

Here is the ViewModel:

internal class ShellViewModel : Screen
{
    private EditableCustomer _Target;
    public EditableCustomer Target
    {
        get { return _Target; }
        set { if (_Target != value) { _Target = value; NotifyOfPropertyChange(nameof(Target)); } }
    }
}

This is what I have in the Model:

internal class EditableCustomer : PropertyChangedBase, IDataErrorInfo
{
    private CustomerInfo _Model;
    public CustomerInfo Model
    {
        get { return _Model; }
        set { if (_Model != value) { _Model = value; NotifyOfPropertyChange(nameof(Model)); } } 
    }

}

And this is another Model:

internal class CustomerInfo : PropertyChangedBase
{
    private string _Name;
    public string Name
    {
        get { return _Name; }
        set { _Name = value; NotifyOfPropertyChange(nameof(Name)); }
    }
}

I know I can move IDataErrorInfo interface from EditableCustomer to CustomerInfo model and everything will work perfectly if I do so, but the problem is that I'm not allowed to make any change in the CustomerInfo model,

Does anybody have any idea what else I can do?


Solution

  • Does anybody have any idea what else I can do?

    Since it is the class of the data bound property that should implement the IDataErrorInfo interface you will then need to bind to a property of the EditableCustomer class that wraps the Name property of the CustomerInfo model:

    <TextBox x:Name="txtName" Text="{Binding Target.NameWrapper, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="136" HorizontalAlignment="Left"/>
    

    internal class EditableCustomer : PropertyChangedBase, IDataErrorInfo
    {
        private CustomerInfo _Model;
        public CustomerInfo Model
        {
            get { return _Model; }
            set { if (_Model != value) { _Model = value; NotifyOfPropertyChange(nameof(Model)); } }
        }
    
        //bind to this one instead of binding directly to the Name property of the model:
        public string NameWrapper
        {
            get { return _Model.Name; }
            set { _Model.Name = value; NotifyOfPropertyChange(nameof(NameWrapper)); }
        }
    
    }
    

    You will either have to do this or make the CustomerInfo class implement the IDataErrorInfo interface. Pick and choose.