After finding it very easy to get MVVM working in WPF with IronPython, I thought it would be trivial to achieve the same thing in Silverlight. Unfortunately, my bindings didn’t work at all after porting a simple game to Silverlight. The problem may have something to do with the issues described in this blog post, but since I needed to demo my code for a local user group event, I abandoned MVVM and went back to just talking directly to the controls from within my ViewModel, which worked perfectly, and simplified my code considerably. It made me wonder why I was using MVVM in the first place.
The Pain of MVVM
I jumped on the bandwagon fairly early and have been writing the majority of my WPF and Silverlight code using MVVM. But to be honest, it has not been plain sailing. There is a real elegance to it that I like, and I love the testability, but there have been plenty of pain points.
For example, triggering animations and discovering when they have finished from the ViewModel has been a nightmare. I’ve tried various solutions, finding one that works with Silverlight and one with WPF but none that work with both, and have managed to crash Visual Studio dozens of times in the process.
Even simple things like setting the focus to the appropriate controls requires a rather convoluted system of “behaviours”, to do something that is a single line of code with access to the controls themselves. And every time I create a behaviour I seem to spend ages debugging my dependency properties, trying to work out why they aren’t doing what I expected.
The Purpose of MVVM
So if MVVM is so painful for non-trivial applications, why use it at all? What’s the point of MVVM? I think there are three answers.
1. Testability
The first driver for using MVVM is testability. If we put our logic in the C# “code-behind” of a XAML class, then running unit tests on that logic becomes very difficult. But with IronPython, that consideration is irrelevant. Since it is a dynamic language, and we are not using “code-behind” in any case, my non-MVVM version of my ViewModel is just as testable as the one that used data binding exclusively.
2. Data Binding
The second motivation for using MVVM is that the pattern encourages the use of data binding, and simplifies it considerably by removing the need for data converters or complicated binding expressions. But why is data-binding to a TextBox better than just getting or setting its Text property?
One of the key benefits of data-binding comes when we want to bind the same thing to more than one property. A Command object is a good example. One Command object could be bound to a drop-down menu and a toolbar button. So setting the Command’s IsEnabled state can be used to enable and disable both views. But most of the time, binding is a one-to-one relationship.
Data binding also saves a bunch of code in CRUD type applications where you are binding directly to properties on an existing model type, but I don’t tend to write applications like that anyway. I think you can use it to make magical things happen with input validation too, but again, it’s not something I’ve ever needed.
3. Designability
The third driver behind MVVM is the goal of designer / developer separation. The idea is that a designer can create a XAML file and bind it to test data, and then the developer can come along and create the real DataContext at a later date. The idea certainly sound impressive, but I can’t help but think that the designer will run into all the problems mentioned above if he actually wants to give his XAML GUI a real workout. And the truth is that I’ve never worked with a designer writing XAML and will not be doing so in the foreseeable future. The graphic designers we use send us bitmaps and Flash mockups.
A Hybrid Approach
I am now wondering whether I should think of my ViewModel more as a “Presenter”. It could still be the DataContext of the object defined in XAML to enable data-binding where required, but it would also have access directly to any properties on that XAML object. For testability in statically typed languages, you could define an interface with methods like StartAnimation or SetFocusToTextBox – a little cumbersome perhaps, but probably no more so than the hoops you have to jump through to make these things happen from your ViewModel using nothing but data binding.
Anyway, looks like I’m not alone in questioning MVVM. Here’s a good post from K Scott Allen. What do you think? Does MVVM fail a cost-benefit analysis for the types of application you work on?
13 comments:
I wanted to provide a perspective from "the real world" having worked on several LARGE Silverlight projects and even ones that used code-behind and talked to controls vs. ones that were more traditional MVVM.
Triggering animations - this is straightforward with a delegate that has a callback parameter. The animation can fire and then callback when done, and in testing you can mock your own callback. Typically we just use a visual state transition for this.
I think you struggling with behaviors is hardly a valid reason to condemn them - we use tons of them without issue and from a developers pespective it might be "the behavior is code" but from a designer it's, "I can drag and drop and I'm done."
As for testing, I've worked with several projects that were a nightmare to test because the views had too much logic in the code-behind so the only way to effectively test was to spin up a view and do stuff with it. When refactored to cleanly separate to the view model, the view model unit tests were straightforward and now could be used anywhere regardless of the view.
Data-binding - the point here is that you can build your entire business logic without knowing how the view is implemented. In small projects it might not matter, but for large ones this is huge. I literally just expose a boolean or a string and I'm done - whether the designer implements as TextBlock or TextBox or CheckBox or button doesn't matter to me.
Probably the most important item here is designability. On small projects you're right, probably doesn't matter. On large projects this is key and yes, there ARE designers who write XAML - very good designers. We've built huge Silverlight applications with completely independent developer and designer workflows and they converge nicely because of proper use of the pattern.
Again, I'm not a fan of MVVM just because I choose to be or because I want to make life hard. I've done it with, and without, and we do it faster, better, and have a much better designer/developer workflow when using MVVM.
If you are writing small applications or are the only developer (or one of a few) then I can see where it might not seem valuable, but when you work on larger line of business applications it becomes the key to running a scalable, successful project.
Again, that's not an "here is my opinion because I think it's better" but a fact were encountered based on experience working with MVVM and non-MVVM projects and comparing speed to production, stability, and maintainability of the appplications.
hi Jeremy,
Thanks for responding. Seems like your comment got marked as spam - shall I delete the first?
After posting this I realised I had been overly harsh on data-binding. I completely forgot about how useful it is for filling up collections. In any case, I don't plan to stop using it wherever I can. It's just that in some places it has felt like it is more hassle than its worth.
I'd love to see your solution for animations. The key for me is that I must get notified on storyboard completion - most solutions I have found overlook this need. Here's where I'm at currently.
(and like I said, the number of times I crashed Visual Studio while developing this was horrible - at one stage I couldn't open my XAML in design view without crashing VS). Even now, I get wierd errors about can't unfreeze a frozen storyboard when I attempt to reuse the UserControl (although this is a WPF-only issue - Silverlight works just fine).
Really what I needed was a ready-made reliable, robust Animation behaviour that worked for both WPF and Silverlight, rather than wasting days simply trying to make something work with MVVM, that I could achieve quite simply without MVVM.
As for having graphic designers that give you XAML - I'm very jealous!
Sure, you can delete the first one. :)
As for the animations ... I'll have to create an example when I get time, but here's the short and long.
1. Attached property (can be a behavior if you like) that gives a story board a "tag" you want to use to for it. This puts the storyboard in a static dictionary with tag/storyboard and wires into the completed
2. On the storyboard completed, an event aggregator message is published with the tag. For example, create a "StoryboardCompleted" message and the value in that message is the tag.
So now you fire the storyboard however you like - a click, a behavior, whatever. The attached property registered it and when the story board finishes, it raises the event aggregator message.
Whatever ViewModel needs to respond simply registers for that message and when it receives it, does what it needs to do.
Does that make sense?
But I guess the question is also - what is the purpose for waiting for it to complete? If it's to chain other animations, I'd use the visual state aggregator instead:
http://csharperimage.jeremylikness.com/2010/03/introducing-visual-state-aggregator.html
If it's to delay an action, for example, I "click" and then an animation happens and THEN I fire a comcmand, I'd use a behavior that simply chains the command to the click. Basically, behavior has three dependency properties: a storyboard (you can bind by {ElementName=storyboardname}), a command, and a commandparameter. When fired, it kicks of the storyboard and waits for completed. When completed, it calls the command.
i sometimes struggle with some points specially things like focus a textbox on the view after something happens on the vm. for that i simply create a event , and subscribe to it in the view. when it's fire i set the focus.
remember that the thing that's improtat is that you should not have conditional code in code behind just strait forward things.
Assumign that you can't have any code on .cs of your view is wrong, if u need it put it there.. just try that is always silpy straight with if or cases or anything like that.
For things liek storyboards i normally use states to contol data, and bind the states to propertys in my VM.. this was trickry at first but after you get the feeling is easy.
I have seen performance recommendations for WP7 that have said that data binding can hurt performance, so I think there are definite instances where MVVM doesn't apply. I write LOB applications myself, not games, and that is where I think MVVM makes the most sense.
One correction though, a ViewModel already is a presenter, MVVM and MVP are synonymous. I think the term you are looking for is Controller.
Another note from the field, MVVM is to me like saving up and paying for something outright versus going on credit and building a lot of technical debt. It's painful at first but over the long term it is more extensible and maintainable.
I will admit that our application is not "testable" or "test driven" in an automated way so this is the smallest factor - we hope to get there one day but at present it's not a factor. It's the maintainability and stability that we like.
With that said, I think MVVM has been victim of too much hype (and consultingware e.g. selling people on a new way to do things that will change their life, but that's another story). People have made it seem like if you aren't using it you're somehow doing things "wrong" even if your application is not data binding heavy or a part of some test heavy process.
I also think it would be nice for Microsoft to push really hard at bringing some of the MVVM functionality right into the main Silverlight SDK. I understand that there's Prism but there's a lot of stuff there for a person simply trying to use some MVVM concepts - I think it's interesting that most people use MVVM Light (e.g. John Papa) because it's so much more focused with less cruft. (We have really enjoyed using MVVM light on my current project).
@Jeremy - thanks, that's very helpful - it's the latter - do something, then fire a command. What you suggest is quite close to what I have ended up with. I'll have to revisit.
@Rui - I assume you mean you are using an event aggregator? You wouldn't want the view to know the concrete type of the ViewModel would you?
@David - yes, I am using MVVM Light on a lot of apps too. I like how it is lightweight. As you say, it would be nice to have a few MVVM helper behaviours baked into the framework to stop us all reinventing the wheel badly.
I've been doing Silverlight for a little while and decided to start with MVVM early on. I knew when I started with Silverlight that I didn't want to make all the mistakes I make with C#, VB, etc. I too love the elegance of it, but it's difficult to get others on-board when I haven't really used simpler methods (like direct text binding). To be honest, despite the fact that testability is big in the industry, I haven't really worked in many places where they didn't see it to be a waste of time.
I like this post because it makes me think of whether I rushed into MVVM or not. But the big selling point for me is dividing the code into manageable areas. The xaml (and code behind) only worries about user-interaction while the viewmodel is concerned with building collections, communicating with the host, etc.
I think the interesting part of your question is whether it's a pain or not for small straight forward applications. I haven't used MVVM but the symptoms you are describing are the same symptoms I had when using the CAB (composite application block) from Enterprise Library.
The amount of things you get solved by these patterns when used in small applications, can sometimes not be worth the restrictions/limitations these patterns impose.
That's why I can totally see Jeremy being happy when using MVVM in LARGE projects.
@Gustavo - good point. Over time, I have no doubt that I will build up enough useful helper classes, techniques and behaviours to make MVVM really quick and productive for small projects. But it does take a while to get to that point.
The only problem I see with using MVVM on large-scale projects is that on the user control level it becomes burdensome to maintain the pattern, which means collapsing the separation between business logic (ViewModel) and UI (View). We've gone back and forth between keeping "control-specific" business logic in the ViewModel and having a lot of repeated code, and putting said logic in the usercontrol itself.
There is also a particular issue with the DataGrid control in Silverlight, in that none of the properties on the DataGridColumns (most importantly, Header and Visibility) can be bound, meaning that the ViewModel has to interact directly with the View if any of these properties need to be dynamic.
Despite these problems, using MVVM with a DI framework makes testability and scalability a breeze. Also, like Jeremy said, we've been able to have a designer work in parallel with development without any conflicts for the most part. Overall I'm happy we chose to use the MVVM pattern on my current project.
We develop most applications in MVVM (although we just stick to plain desktop WPF at the moment) and it works quite well. I think most people are taking the "ViewModel" name to literally. I tend to see the VM more as the glue between the view and the model whereas the VM only has an abstracted knowledge of the view (if any).
Usually the pattern we use is to have a presenter as the datacontext which then exposes the view models for data binding. If the presenter needs to interact with the view then the view becomes an interface which gets passed into the presenter.
So essentially it more like a MVPVM pattern. From experience when you munch around directly with the controls from you view models then it's very likely to go and bite you later (at least it has bitten me everytime I went down that path as quick hack). Just not worth it.
You may like to read Davy Brion's "MVP in Silverlight/WPF Series". http://davybrion.com/blog/2010/08/mvp-in-silverlightwpf-series/
It promotes a greater separation of concerns and therefore maintainability that I think will work better than MVVM, at least for larger projects. I'm going to try on my next one.
Post a Comment