tags:

views:

102

answers:

2

Hi,

I am writing a PRISM/MVVM/WPF application. It's a LOB application, so there are a lot of complicated rules. I've noticed the View Model is starting to get bloated. There are two main issues.

One is that to maintain MVVM, I'm doing a lot of things that feel hacky like adding a bunch of properties to my VM. The view binds to those properties to keep track of what feels like view specific information. For example, a boolean keeping track of the status of a long running process in the VM, so the view can disable some of its controls while the long running process is working. I've read that this issue could be solved with Attached Behaviors. I'll look more into that. In the example MVVM apps you see online, this isn't a big deal because they are over-simplified.

The other issue is the number of commands in my VM. Right now there are four commands. I'm defining the commands in the VM using Josh Smith's RelayCommand (basically the DelegateCommand in PRISM) so all the business logic lives in the VM. I considered moving each command into separate unit of works. I'm not sure the best way to do this.

Which patterns are you guys using to keep your VMs clean? I can already feel someone responding with "your view and VM is too complicated, you should break them into many view/VMs". It is certainly not too complicated from a Ux perspective - there are 2 buttons, a combobox, and a listbox. Also, from a logical perspective, it is one cohesive domain. Having said that, I'm very interested in hearing how others are dealing with this type of issue.

Thanks for your input.

+1  A: 

I feel your pain. I wrestle with these kinds of questions a lot when working on MVVM apps. One of these days I'll post a laundry list of questions to get input from others like you've done.

I tend to worry very much about "bloat" in my ViewModel base class but not so much in the concrete ViewModel subclasses. It's often tempting to throw a dependency used by 2-3 ViewModels into the base class but it should be avoided.

While I can't presume to know what your idea of bloated is, I can say that I don't think having the "busy" property or commands handled in the VM are bad. One thing you might consider is whether or not the ViewModel can be busy doing more than one thing at once. If so, you might want to go ahead and think about ways to break it up. While I haven't personally seen it in practice, you could probably have your single, cohesive view and a couple ViewModels bound to it.

If your commands are long, or if they can be executed on different targets, I think making the commands self-executing units is a good idea. But it's probably best to be consistent with this approach as to avoid confusing anyone who works on it later. For example if you have a SaveCustomerCommand class that is about 10 lines of code, you probably don't want to use a RelayCommand for everything else.

It'd be nice if there were hard and fast rules for this kinda stuff but I think both the framework and the pattern are still in the evolutionary stage right now.

Josh Einstein
Thanks for your response Josh. Yea, I agree that the framework is still young. Bloated = more than one way of changing, thus violating SRP.
Holy Christ
Oh, well then in that case ... good luck! :) I think SRP is totally overrated. In the real world it basically prescribes one method per class. While that may be fine for command classes, I'm just not sold on applying it to models and viewmodels.
Josh Einstein
A: 

Without knowing specifics of the ViewModel it's hard to say where the bloat is coming from. It could be non-UI related, like too many lines to query the model. Or it could be UI features that are better implemented all in the UI with triggers or something.

Could you possibly elaborate about your ViewModel?

EDIT Based on comments.

SaveCustomer and DeleteCustomer sound like Model or Service methods, so I'd put them in some sort of Persistence Layer.

Upload/Download Customer Video: Again, these are not ViewModel specific (you might want to do these in another ViewModel) so I'd put them in a Web Service and have the ViewModel command call it.

In general it's worth putting common code (that multiple ViewModels might want to use) into a Service and then throwing that service into your IOC. That way your ViewModels end up being lightweight "directors" of information from the View to either the Model or a Service.

Cameron MacFarland
The bloat is coming from the 4 commands and the view specific properties as described above. My question is an abstract one: What are the patterns people are using to abstract the logic into units of work outside the ViewModel? Where do you draw the line between view and VM specific logic?As an example, let's use these four commands: SaveCustomer, DeleteCustomer, Upload Customer Video (long running), Download Customer Video (long running). The view needs to be aware the status of the long running video commands.
Holy Christ
and thanks for the response :)
Holy Christ
The RelayCommands do call different components and services in other layers of the application, but I still need the RelayCommands defined in my VM so that my view can bind to them. So for the download customer video command, the VM calls a webservice to get the video, does some byte level manipulation on the response, then opens the video using a different component.Maybe I could extract the meat of the RelayCommand/DelegateCommand logic into a different class, and make the RelayCommands a thin veneer that merely delegates to the new class.
Holy Christ
If you extract the command into its own class, you can derive from it. Since you said you're using Prism, you can derive from DelegateCommand. Example: public sealed class SaveCustomerCommand : DelegateCommand<Customer> { public SaveCustomerCommand() : base(InternalExecute) {} }
Josh Einstein
Putting the meat of the command in a service and having the actual command be just a single call with a few other ViewModel specific bits is what I meant.
Cameron MacFarland