15

I struggle to understand the purpose of the ViewModel sometimes, especially with respect to what it should and shouldn't do.

So... I find myself wanting to expose a display string from my ViewModel. Ok, actually it's a Date string, where I want to display something like "Unknown" if the date isn't set. Really I want a solution to cover the general case, so I don't want a discussion about using null and fallback values. It could equally be a status message like "Awaiting Dispatch" or "Launch Aborted".

So the quesion is, how should the ViewModel expose display strings to the View. The App will need to be localized, so we can't have hard coded strings in the ViewModel.

Is it ok to access the app's resources from the ViewModel, and return the display string?

As an aside, should I be using a resx string resource, or a Xaml resource dictionary? I'm leaning toward the Xaml. Advantages/Disadvantages?

Thanks, Mark

Mark
  • 1,784
  • 16
  • 26

5 Answers5

9

What you are doing is taking data that is inside the system and converting it to a format that makes it more useful and clear to the user. This is the responsibility of the view. It would be the responsibility of the view to format a valid date into the correct culture and it has the same responsibility for any text that you wish to display.

In the same respect the status examples you give would probably be stored as an enum in the viewmodel (since that would make it easier to apply business logic to them, which is the role of the viewmodel) and it would be the responsibility of the view to display the values in a way that works for the user in all respects. This would include size, colour, font, position and culture.

That being said though I have been known to put display logic in my viewmodel (concatenating firstname and surname for example) simply because it is easier. I've even been known to use code-behind (shock, horror!) where it suits my purpose. There is a sliding scale between purity and pragmatism, and it is up to you where you sit on that line.

Martin Harris
  • 28,277
  • 7
  • 90
  • 101
  • 2
    You use... *CODE BEHIND*? Such depraved activity has no place on StackOverflow (Oh, and +1 ;) – Adam Robinson Nov 18 '11 at 14:59
  • so if I have to display a DialogViewModel (that I use as messagebox) to show a question or an error, where should I write the text of the question (or error)? the only solution I can think i to create views for each different dialog text, but that's madness!! – Sergio Jan 07 '14 at 20:35
  • 1
    @Daedalus The "pure" solution would be for the view model to contain a non-localized identifier for the message and the view to algorithmically display the culture specific version. For example you may use an enum in the viewmodel - MessageToDisplay = Message.UserUnauthorized; - and a value converter in the view that looks up that string in a localized resource file and displays it on screen. The responsibilities break down into viewmodel - holds the data, view - displays the data, valueconvertor - localizes the data. – Martin Harris Jan 08 '14 at 09:40
  • @Martin Harris Thank you for your rely. I'd like to follow the purist way, but I hate magic strings, and now on the fly I can't think to a solution that doesn't use magic strings. Do you know any? – Sergio Jan 08 '14 at 12:38
9

As mentioned in the other answers, this is clearly the view's responsibility. The view model should expose the data property (a status enum or something similar) for the view to inspect in order to determine the particulars of the display message. From a purist's perspective, generating display properties from the VM or code behind is right out.

I'm surprised to hear no suggestions of datatriggers, though. Use them in your TextBlock to bind to the exposed status enum value and update the text appropriately. This question compares their performance against binding to a converter and suggests that triggers will perform better in this scenario. I haven't done any testing myself, so I'm not sure, but it seems intuitively reasonable to me, provided you aren't listening for an outrageous number of different enum values. I also think it's the more appropriate method in this case, and I'll cite this question asking for a comparison of VC vs. DT use cases, though I don't think it's terribly contentious. To put it another way, if one holds that a VM is a value converter on steroids (a statement which I'm very leery of and will not agree or disagree with at the moment), then this argument holds: a VM shouldn't be exposing display properties; a VM's purpose and abilities are a super-set of those of a VC; therefore, a VC should not be exposing display properties.

I'm doing my best to give the purist's answer (or what I think it is, at least), but there are certainly easier ways to code it than writing a lot of datatriggers, especially if you have many different values to address.

Regarding your resource question, have a look at this MSDN article on resource files, which is, in certain ways, easier to work with than a resx file. Which approach you should use depends on your use cases.

Community
  • 1
  • 1
Esoteric Screen Name
  • 6,082
  • 4
  • 29
  • 38
  • 1
    If it is left to the view to decide how the data should be displayed to the user, e.g., the view decides how strings should be concatenated, then how do you test that the correct string is being displayed to the user? If the 'display logic' is in the VM (a poco) then you can easily unit test that the correct string is being exposed to the view. Any logic in the view brought about by DataTriggers becomes very hard to test, so should we not be avoiding display logic in the view at all costs? – James B Apr 18 '16 at 12:26
3

To display the string Unknown when the date is not set is a display issue and therefore has to be solved in the view. Even "Awaiting dispatch" is a presentation of a value.

So the answer is: The view model should never expose display strings. The view model has to expose a value that leads the view/a converter/whatever object from the presentation layer to choose a display string.

Even if this answer is not the answer you want to read, it is the answer an MVVM purist has to give. And as you asked for MMVM purists, here it is.

PVitt
  • 11,500
  • 5
  • 51
  • 85
  • 2
    I take it from this that the oft-used definition of a ViewModel as a "Value Converter on Steroids" is fatally incorrect. I've also seen people decry value converters as unnecessary in the new age of MVVM. (Although I can imagine doing something with Xaml style setters instead, I'm thinking that the performance of this when used in a list box would be frightening.) I ask for the purists because, the words of Fox Mulder, _I want to believe_. – Mark Nov 18 '11 at 16:20
  • 1
    @Mark: While I do think this might stray a little far into the obsessive camp, just because it's a "value converter on steroids" doesn't necessarily mean that the value it produces must be the *final* value that the user sees. – Adam Robinson Nov 18 '11 at 16:49
1

For the date, I would have the ViewModel export a nullable DateTimeOffset, where the null value means "not set". The in the View (via a converter or similar), the null value is displayed as whatever you need.

As to the aside: I would use resx. The reason is that there is a defined fallback mechanism when strings aren't available in the language.

Daniel Rose
  • 17,233
  • 9
  • 65
  • 88
1

An approach I have taken was creating Culture specific Resources and adding instances (singletons?) of these to the ViewModel.

Then the View can simply Bind to the ViewModel.Resource.DisplayString

See this article for Windows Phone 7 which easily translates to WPF.

If you dislike this because you do not want to tie the culture specific resource to the ViewModel and want the View to solve it you could use/write a ValueConverter that turns a value from a VM property into a display string.

Emond
  • 50,210
  • 11
  • 84
  • 115