views:

939

answers:

4

Hi

We're just getting into MVVM in WPF.

We have implemented our ViewModels with 'strongly typed' properties (int, double? etc.) that we bind to in the view.

Type conversion works OK, mostly, and so entering data is simple enough. But we run into problems with validation.

If, say, a non-numeric value is entered in a text box bound to a numeric property, the conversion fails, the property is never set, and we never get a chance to provide proper feedback to the user. Worse, the property retains its current value, leading to a mismatch between what's displayed in the view and what's actually in the ViewModel.

All this can be handled with value converters, I know. But I have seen several opinions to the effect that conversion should not be the view's responsibility at all. What is entered in the view are strings, and the conversion, validation etc. should be the ViewModel's responsibility (so the argument goes).

If so, we should rewrite most of the properties on our ViewModels to string, and provide error information through the IErrorInfo interface, for example. It would surely make for simpler, leaner XAML in the view. On the other hand, conversion, validation etc. will be less declarative, explicit and flexible, from the point of view of the View designer.

It seems to us these two approaches are fundamentally different, so before we decide, we'd like some informed SO opinions on the matter.

So: should ViewModels expose a simplified, 'text-based' interface to the view and handle conversion internally? Or should ViewModel properties expose the actual data types, leaving such chores to the view to handle?

Update:

Hard to pick a winner here, but I finally landed on one of you who concludes more or less like myself.

Specifically, we have decided to keep the ViewModel properties typed. The main reason for this is the flexibility it affords us in the design of the view, and the power of explicit, declarative conversion/formatting in XAML.

I noticed an assumption with you who will disagree with us in this, that the design of the view is fixed and ready. Hence, no decisions about conversion, formatting etc. need be made in the view. But ours is an agile process, and we haven't got all the nitty gritty detail of the UI figured out on beforehand.

In fact, leaving the details of the UI to be worked out along the way leaves room for creativity and besides, in my opinion, even a meticulously worked out design will always end up morphing throughout the implementation process.

The point of all this is that whereas business rules enforcement certainly belongs in the ViewModel, it seems to us that simple conversion and formatting is a view-thing. It may sound like heresy, but I don't actually think type conversion in the view needs unit testing at all (so long av we unit test the actual type converters).

All in all a great discussion, folks, with well formulated, informed opinions. Thanks.

+3  A: 

This is a good question, and I can certainly see both sides of the discussion.

My thought is that what you're really looking for is a proper NumericInputControl that you can use in your xaml. This will provide a better user experience because your users won't be able to accidentally enter text in a number field and, because the control constrains input without validating it, you can maintain the more strongly-typed ViewModel.

I'm not sure how you'd want to go about implementing one, I know that the classic spinner/NumericUpDown controls are falling out of favor because they aren't touch-friendly, but I don't believe that the introduction of such a control will violate the purity of the design approach or your ViewModels. You'll receive a number that you can then range-validate in the appropriate place, supply feedback via IDataErrorInfo as usual, and so forth. :) This technique lets you get the best of both worlds without any real drawbacks (except the creation of a numeric control).

Greg D
Hm. You're proposing a division of labor - the view converts to and from the correct type, and thew viewmodel validates the converted data. That does make sense.
Tor Haugen
+6  A: 

This is a very interesting question and one that I don't feel has a definitive answer, but I'll do my best to throw my thoughts out there.

Looking at the MVVM pattern as I understand it, the point of the ViewModel is to expose the data in a way the View can understand without any assumptions about the way the view is going to use it. For an example let's pretend that we are modelling the speed of a car:

public class CarModel
{
    public int MilesPerHour { get; set; }
}

public class CarViewModel
{
    private CarModel _model;

    public int MilesPerHour
    {
        get { return _model.MilesPerHour; }
        set { _model.MilesPerHour = value; }
    }
}

In the example above I've exposed the property as an int since that is what it is in the model. The disadvantages of this you have listed in your question, but the main advantage is that it gives the creator of the view a valuable piece of information about how to display that data. Remember that we (as the authors of the ViewModel) don't know what the View looks like. By committing to the idea of the data being an int the View can use a textbox or some other control that only accepts numbers (a dial, for example) to display the information. If we say that we are going to format the data in a way that we assume is helpful to the View it takes that important power away from it.

On the other hand we work in real world. We tend to know what the view is. We rarely plug and play different views on top of the same ViewModel and adding the conversion code into the ViewModel is simply easier. I don't think it is right, but that doesn't mean you won't find my production code using it...

Finally (and I'm sure you know this, but for completions sake...) business logic should be done in the ViewModel. If we decide that the car shouldn't go above 70mph then it isn't the responsibility of the view to enforce that. So you'll still end up with some kind of error provider, but at a business rather than display level.


Okay, maybe that wasn't finally....

I wanted to address the comments made by Kent, and my thoughts didn't fit into a comment.

Obviously the primary difference between my and Kent's point of view (as I understand it) is he reads ViewModel to be a Model of the View and I read it to be the thing that exposes the Model to the View. A subtle difference I'll admit, but I think the upshot is that I don't want to remove information that the model provides, even if it makes it easier for the specific view I'm using.

My point of view is based on the assumption that you should be able to swap views out, they should be fleeting things that may change depending on the requirements of screen size, hardware, platform, latency and environment. The interesting twist is that I have never actually needed this functionality, nor seen anything (beyond proof of concept applications) that have ever used it, but if we accept that we won't use it now or at any point in the future, and that each ViewModel will work with one, and only one, View then we may as well go back to putting all the code in the code-behind file and throw the ViewModel out completely - after all, it's so tightly coupled that it may as well be the same class.

Ideally I would like a situation where the ViewModel can say "this value is an int, it will always be an int, and you can display it in anyway that you like. But you can give anything back to me and I'll do my best to make it fit, and if I can't I'll let you know". Basically my MilesPerHour property should have an int getter, but an object setter. That way the views keep all the information I feel they need, but don't have to worry about conversions or validation.

Martin Harris
Very well put, Martin. Thanks.
Tor Haugen
I sure hope you know what the view looks like. If not, there's something seriously broken with your collaboration between devs and designers. Taking on responsibility for certain assets doesn't imply that we operate in a vaccuum - we still need to both understand what the other is doing.
Kent Boogaart
I don't know what the view looks like if I'm working in an application that can be skinned by third parties. But even outside of that scenario I was referring to the MVVM pattern implying that, in an ideal world, the ViewModel shouldn't know anything about the View (even whether it is WPF, Webforms or something else).
Martin Harris
That's a common and unfortunate misconception, IMHO. The VM is a model of the view. Therefore, it absolutely has to know about the view, else it can't effectively model it. Skinning is just that: a skin. You'll still be making decisions in your VM that affect how flexible the skin can be. For example, choosing how you expose data.
Kent Boogaart
@Kent Sorry, I think my replay came out a little abrupt. I agree with you (in my current project *I* am writing the views, so I know them inside out). But I was trying to answer an abstract question about a design pattern in an idealised case.
Martin Harris
@Martin: np, I didn't think it was abrupt. Maybe we should discuss over a beer - I'm in Canary Wharf, too :)
Kent Boogaart
+5  A: 

Absolutely it belongs in the view model, for all the usual reasons, including:

  • Designers own the XAML. Do you want the designers to have to understand and implement the requisite type conversion and validation logic?
  • Testability. Don't you want to validate that your conversion and validation logic is working correctly? It's much harder if it's embedded in the view.

On the other hand, conversion, validation etc. will be less declarative, explicit and flexible, from the point of view of the View designer

I think this is a moot point because the view designer should be responsible for these things. The designer is trying to make the UI look and feel a certain way; it is the developer who implements the business logic, including conversion and validation logic.

HTH, Kent

Kent Boogaart
+2  A: 

Should the MVVM ViewModel perform type conversion/validation?

Yes.

The view model is an abstraction layer between the view and the model - the perfect spot to perform any type conversions (instead of cumbersome value converters). Validation should absolutely occur as part of the view model.

We use our View Model to handle the conversions as much as possible for data types. This reduces the need for a value converter to some very specific circumstances. You want to expose whatever type is easiest for the view to consume. This has been working well.

The one specific question you raised:

If, say, a non-numeric value is entered in a text box bound to a numeric property, the conversion fails, the property is never set, and we never get a chance to provide proper feedback to the user. Worse, the property retains its current value, leading to a mismatch between what's displayed in the view and what's actually in the ViewModel.

might be handled by exposing your view model type as a nullable type. This should still allow the underlying source to be updated, even if invalid data is entered, and trigger validation. This worked in a similar situation we had with DateTime and a date time picker.

Keep the view as dumb. We don't have official designers, our developers are our designers, so keeping the view dumb has some benefits:

  • We (developers) get to keep our sanity (XAML is somewhat less verbose)
  • Business logic (including validation) stays in the view model and can enable testing

Good Luck!

-Z

Zach Bonham