1

When looking at the WPF view model code samples on the web/books almost all of them show same approach for the bind-able properties that are used for the binding and input in the UI and that often smells like primitive obsession to me.

Influenced by such articles - this is how my view models often look like:

public class ClassicViewModel : BaseViewModel
{
    private string productName;
    public string ProductName
    {
        get => productName;
        set => SetProperty(ref productName, value);
    }
    public InputMessage ProductNameMessage { get; } = new InputMessage();

    private decimal productPrice;
    public decimal ProductPrice
    {
        get => productPrice;
        set => SetProperty(ref productPrice, value);
    }
    public InputMessage ProductPriceMessage { get; } = new InputMessage();

    public ObservableCollection<GroupSelectItem> ProductGroups { get; } = new ObservableCollection<GroupSelectItem>();

    private GroupSelectItem productGroup;
    public GroupSelectItem ProductGroup
    {
        get => productGroup;
        set => SetProperty(ref productGroup, value);
    }
    public InputMessage ProductGroupMessage { get; } = new InputMessage();

    private bool Validate()
    {
        var success = true;

        if (string.IsNullOrWhiteSpace(ProductName))
        {
            success = false;
            ProductNameMessage.Error("Value is required");
        }
        else ProductNameMessage.Clear();

        if (ProductPrice < 0)
        {
            success = false;
            ProductPriceMessage.Error("Cannot be negative");
        }
        else ProductPriceMessage.Clear();

        if (ProductGroup == null)
        {
            success = false;
            ProductGroupMessage.Error("Must be selected");
        }
        else ProductGroupMessage.Clear();

        return success;
    }
}

As you can see in this sample, for each input, there are at least 2 properties:

  • Property for value - used for binding to input control
  • Property for message - used for binding in order to display error/warning/info related to input
  • Property for items source - only for select, used to bind the source of combo/list control

In my opinion - this approach looks "simple" on the surface, easy understandable, but it is also very raw and creates a lot of noise due to the property boilerplate, especially when there are many properties.

These 2-3 properties per input always work and act together, and all logic related to such behavior then gets copy-pasted in code base for each input and each view model (e.g. Validate method), thus violates DRY and seems really bad to me.

Using tuples of properties that work in conjunction without enclosing them in class greatly reminds me of primitive obsession.

Now, I considering refactoring to more sane solution, which incorporates small components for each type of the input:

public class StringInput : BindableBase
{
    private string _value;
    public string Value
    {
        get => _value;
        set => SetProperty(ref _value, value);
    }

    private bool required;
    public bool Required
    {
        get => required;
        set => SetProperty(ref required, value);
    }

    public InputMessage Message { get; } = new InputMessage();

    public bool Validate()
    {
        if (Required && string.IsNullOrWhiteSpace(Value))
        {
            Message.Error("Value is required");
            return false;
        }
        else
        {
            Message.Clear();
            return true;
        }
    }
}

public class GroupSelect : BindableBase
{
    public ObservableCollection<GroupSelectItem> Source { get; } = new ObservableCollection<GroupSelectItem>();

    private GroupSelectItem _value;
    public GroupSelectItem Value
    {
        get => _value;
        set => SetProperty(ref _value, value);
    }

    private bool required;
    public bool Required
    {
        get => required;
        set => SetProperty(ref required, value);
    }

    public InputMessage Message { get; } = new InputMessage();

    public async Task LoadSource()
    {
        // ...
    }

    public bool Validate()
    {
        if (Required && Value == null)
        {
            Message.Error("Value is required");
            return false;
        }
        else
        {
            Message.Clear();
            return true;
        }
    }
}

And finally, my new view model would look like this:

public class NewViewModel : BaseViewModel
{
    public StringInput ProductName { get; } = new StringInput();
    public DecimalInput ProductPrice { get; } = new DecimalInput();
    public GroupSelect ProductGroup { get; } = new GroupSelect();

    private bool Validate()
    {
        return ProductName.Validate() &
               ProductPrice.Validate() &
               ProductGroup.Validate();
    }
}

Why would I then use classic approach with tuples of properties instead of new approach that uses small components?

Are there any significant drawback with the new design?

Am I complicating or doing something wrong with the new design?

closed as too broad by Robert Harvey May 11 at 19:44

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • It looks like you are using all these extra properties for validation. Have a look at INotifyDataErrorInfo, consider implementing it in your view model base class and using it instead. – Dave M May 11 at 14:26
  • Thanks Dave, but that code is a simplified illustration of the approach. The INotifyDataErrorInfo (which is simple I agree) is not even close to satisfy the requested behavior for the real world scenario - like displaying warnings/info instead of error, animating the presentation of the messages, bringing the inputs into the view/focus and so on... That is why I am using the InputMessage component that enables me to do such things, instead of the bare-bone INotifyDataErrorInfo. – Dusan May 11 at 14:36
  • You can get rid of a lot of the "property noise" by using PropertyChanged.Fody. It is a code weaver that will inject your INotifyPropertyChanged implementation for you, without having to manually call those Get and Set methods. – Robert Harvey May 11 at 17:38
  • I only see one problem with your NewViewModel: how do you intend to do data binding? – Robert Harvey May 11 at 17:41
  • @Robert, for example {Binding ProductName.Value} (instead of {Binding ProductName} like in classic approach) – Dusan May 11 at 17:49

Browse other questions tagged or ask your own question.