BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Rob Eisenberg on Caliburn.Micro and MVVM

Rob Eisenberg on Caliburn.Micro and MVVM

Bookmarks

InfoQ: Let’s start from the beginning. What originally inspired you to create Caliburn?

Rob Eisenberg: When I began working with WPF, back in the Beta time period, I quickly came up against barriers to productivity on the platform. Before MVVM was widely known in the Xaml world, many early adopters were looking at how to leverage Databinding and Commands effectively. There were a number of issues in the implementation of Commands that made them tedious or not quite capable of meeting the real world need. Out of this frustration came the first feature of Caliburn: Actions. Actions were an attempt to do away with the ceremony of creating things like DelegateCommands and similar constructs solely for the purpose of triggering a ViewModel method from a View's action. Once this feature was in place, we realized we could do a lot more with it, such as introducing Coroutines for better asynchronous programming.

As we explored the MVVM pattern more, along with other UI modeling ideas, we realized the critical importance of "composition over inheritance." As we broke complex application UI down into smaller more manageable pieces, we needed a way to "re-assemble" things at runtime. This gave birth to many of the view location capabilities which allow a composite view to transparently compose itself to match a dynamically composing set of view models. To me, this is a very powerful feature and changes the way you think about UI development.

Eventually we generalized and packaged up these concepts, along with a few others, into a re-usable form and Caliburn was born.

InfoQ: Can you describe what a Coroutine is in the context of Caliburn and Xaml technologies?

Rob Eisenberg: Coroutines are a CS term which describes the ability to create a subroutine that can have multiple entry points. In Caliburn.Micro, we implement this functionality on top of C#'s iterators. Since the compiler generates a state machine for us for each iterator function, you can essentially call the method multiple times with different states and effectively "enter" the method at a different location each time. Practically speaking, this is very useful for asynchronous programming since we can "enter" the method and execute code to set up an async request, then "re-enter" it when the async request is complete. This functionality enables us to write our code in a sequential fashion, but have it execute asynchronously, which greatly improves the understandability of the code. Caliburn.Micro makes this available transparently from any Action method since it can set up all the necessary machinery to enable this feature without the developer needing to do any extra work. Additionally, all Actions in Caliburn.Micro have access to a special execution context which can be used to discover additional information useful in setting up view-oriented coroutines, such as playing animations. If you are using an MVP or an MVC architecture, this can be a great way to set up a general mechanism for your Controller to talk back to the View, while still remaining loosely coupled, easily testable and avoiding interface explosion.

InfoQ: When you created Caliburn Micro you found that you could reduce the project size by roughly 90%. What do you most attribute the most to the reduction in size?

Rob Eisenberg: Caliburn was my first attempt at building a generic presentation framework, and I made some mistakes. One of the key mistakes was over-abstraction and default mechanics which were sometimes too complex or too difficult to modify. With Caliburn.Micro, we cut a lot of that out. In addition to that, the Xaml world had grown up a bit since Caliburn was written. For example, the Blend team's creation of a standard Behavior mechanism allowed me to remove an entire subsystem from the framework. Then there were lots of little things, smarter implementations that only occur to you after you've built something like this several times.

InfoQ: Are there any features you particularly miss from the full version of Caliburn?

Rob Eisenberg: Personally, I haven't missed anything. However, I was aware that many Caliburn users would want to make the switch and might miss some features, such as Action filters, for example. So I worked closely with several of our contributors to make sure we had the right extensibility points to enable those features to be plugged in by those who wanted to use them.

InfoQ: What prompted you to develop a convention-based way to bind methods and properties to a view?

Rob Eisenberg: Some of it was just a natural progression. As we looked at the patterns we were already following and saw other community members doing similar things, we realized we could push some of these ideas down into the infrastructure and remove some manual work. The first instance of this was with pairing Views and ViewModels. It seemed fairly common for developers to use a naming convention such as CustomerView and CustomerViewModel. So, we taught the system to understand that, making it possible to map a View to a ViewModel or vice versa. From there we just continued to find ways of reducing our work. It's not too big of a leap to conceive of naming a Button control Save and automatically triggering a Save method invocation. The jump to full databinding support happened after doing a code review for Oren Eini. He was writing an MSDN article, which had nothing to do with UI, but nevertheless required some UI work. He had written a simple MVP framework to power his sample, which he wanted code-reviewed. In that framework he did some simple wire-ups of controls based on naming patterns. It was very minimal and only what he needed to get that UI working, but it immediately dawned on me that we had all the infrastructure already in place in Caliburn.Micro to do this and take it to the next level.

InfoQ: In the MVVM pattern, the term view-model means a lot of different things depending on who you ask. What does it mean to you and what should, or should not be, in a view-model?

Rob Eisenberg: I'd like to preface my discussion of the view-model with a few thoughts related to the community that has grown up around this pattern and the Microsoft Xaml community in general. I think, for the first time in Windows development, design patterns have actually made inroads with the average developer. I'm not entirely sure why that is. Perhaps developers realized, at some point, that they had a better chance of getting a job if they plastered MVVM all over their resume or blog like the many other acronyms in our profession. Regardless, almost every Xaml developer has heard of MVVM and often has some ideas about it. Despite the broad awareness of the pattern, it's actually been very difficult to have any sort of reasoned or academic discussion about it. I've encountered a significant number of developers who hold their opinions so strongly and so personally, that any sort of critical analysis is often impossible or is taken as a personal attack on them. I wanted to bring this up because when I'm saying X *is* a ViewModel and Y is *not* a ViewModel, I'm not making a value judgment about the code. I'm simply trying to accurately apply a taxonomy to help us all communicate more effectively with one another about our software designs. Nothing more. Once we have a shared language to communicate, only then can we dig deeper and ask questions about whether or not, in a specific context, one design choice is better than another. Otherwise, we're just talking right past one another. Now, on to answering the question that was actually asked...

Rather than looking at the ViewModel on its own, let's look at it alongside its brethren. If we take the three most popular UI architecture patterns: MVC, MVP and MVVM together, then I think things become clearer. Let's throw out the View and Model part of these patterns for the sake of this discussion, because those are essentially the same across the three. Instead, let's focus on the Controller, Presenter and ViewModel. One way I like to think of these patterns is in terms of how they treat state. If we imagine a line between two points, with the left most point being completely stateless and event driven and the right most point being completely stateful and eventless, then we would put a Controller to the far left (completely stateless) and a ViewModel to the far right (completely stateful). We'd probably put a Presenter (what Fowler would classify as a Supervising Controller in this case) directly in the middle. Let's dig a little deeper into this and think about the three. The Controller concept emerged in the Smalltalk world where it's job was primarily to interpret user interface events. To contrast, in the modern web's version of MVC, the Controller is functional in nature. It merely responds to HTTP requests: request in, response out, but no state is held. Both cases involve an object who has no interest in state whatsoever. All functionality is essentially geared around responding to events or "handling requests" which are much the same thing, particularly in frameworks like node.js.

As we move forward in time from the original Smalltalk, we start to see GUI frameworks offering more and more advanced features. Many of these frameworks had "controls" which handled the input events directly and obviated the need for a "controller." But along with this change came new challenges. Now developers needed a way to shuffle data out of controls and into their Models. So, the Presenter emerged. In early implementations, the Presenter would wire to events from the view, shuffle data to and from the model and invoke operations on the View and/or Model. It didn't necessarily hold its own state, which is why it's sometimes been called the "twisting" of the MVC pattern. But, recognizing the problem of state synchronization inherent in using the new GUI toolkits, the vendors followed up with another shift: the introduction of databinding.

Early databinding frameworks were very primitive and only enabled very simple scenarios. Nevertheless, using databinding instead of writing code to manually synchronize state resulted in huge productivity gains and less code in general, which is a maintenance win. Thus was born the Supervising Controller variant of MVP. In this pattern developers would take a blended approach, using databinding for simple state synchronization and falling back to manual synchronization for more complex scenarios.

With the emergence of platforms like WPF and Flex, we start to see rich and mature databinding functionality enter the scene. As developers discover and understand these features, their Supervising Controllers start to transform again. With rich databinding support, many scenarios can be implemented without the need to wire for any view events or manually shuffle any data back and forth. This results in a ViewModel, an object that purely represents state to the View. The databinding mechanism takes care to always keep the View and ViewModel completely in sync.

There are other axes on which we could place these patterns to see their differences, most notably performance. But I like the state axis because it also corresponds to the history of the technologies. So, a ViewModel is an object that purely represents state to the View. It assumes some platform capability which automatically synchronizes state. It doesn't wire to View events, nor does it even have a need to hold a reference to the View. Again, I'm not saying you are a bad programmer if you have a reference to the view. But I am saying that your object isn't a ViewModel. Once you start holding view references, either explicitly or through an interface, you are starting to describe something that "quacks like" a Presenter. If your object has no state, then you might even have a Controller. Real applications very rarely use "pure" MVVM. That's another reason I like to think of these patterns as being on a line or continuum. In most projects I've worked on, I've used some amount of all the above....with many subtle gradations. Whatever design you are using though, it's important that you made the choice intentionally and for good reasons, specific to the project and platform you are working on.

InfoQ: Are there other patterns besides MVVM that you think should get more attention from Xaml developers?

Rob Eisenberg: Absolutely. Nearly the entire GoF book on patterns can be applied directly to the presentation tier. Generally speaking, these are not high level architectural patterns, like MVVM, but they are of no less value in practically working out the complexities of a UI. I think the MVP pattern, particularly what Fowler calls Supervising Controller, is of special interest to Xaml developers. Because this pattern makes use of both databinding and explicit synchronization of state, it fits well into Xaml platforms and can enable a really simple solution especially at points where databinding breaks down. I see a lot of Xaml developers who are overly concerned with staying within the MVVM paradigm, so much so that they greatly increase the complexity of certain parts of their code when the simplest solution would be to leverage a different pattern. Because it's much easier to understand the "letter" of the law than the "spirit" of it, many developers, in general, get stuck using patterns incorrectly or for the wrong reasons, simply because it's been broadcast through certain channels that this is what they "should" do. But, in reality, it's not the pattern that matters, but the principle behind the pattern. So, developers who understand things like separated presentation, SOLID, responsibility-driven-design, etc. and are more widely versed in the history of these things and their varying implementations across different platforms (Mac, PC, Web, Smalltalk)....they are going to have a stronger foundation on which to base their design decisions and a better shot at deriving the simplest possible solution. Yes, in many cases for Xaml developers, it will be MVVM. But I've scarcely worked on a project where I didn't employ other techniques, and Caliburn.Micro has been designed to make this simple and painless.

InfoQ: How did Caliburn get started in the first place? Were you looking to create a framework for general consumption or did it just happen by chance?

Rob Eisenberg: I wasn't looking to create a framework for the community at first. I have a pretty low threshold for pain and friction during development, so most of Caliburn's initial features came out of an attempt to improve that experience for my own projects. As time passed, I added more pieces I found to be common between projects and generalizable. Eventually, I realized this was something others could benefit from, and I decided to publish it.

InfoQ: Has supporting a well known framework changed your professional life in any way?

Rob Eisenberg: Yes. On the positive side, it has brought me almost continuous and varied consulting work. I've also had the opportunity to speak publicly on UI architecture at several conferences and influence the products and even frameworks of others, even on different platforms. However, as popular as the framework is for use by developers, there have been very few willing to contribute to its continued development and maintenance. So, that has resulted in a pretty large time burden on me. I think for the average framework, this wouldn't be too bad, but in the case of Caliburn.Micro, it's worsened by the continuous release of new Xaml-based platforms. With every new Xaml platform that Microsoft releases, currently four (with rumors of more), a significant amount of work has to go into porting and testing the code. Unfortunately, the platforms are quite inconsistent in a number of key areas for Caliburn.Micro. There are several very painful cases where the APIs are the same, but the underlying behavior has been different, resulting in a lot of painful debugging and "clever" workarounds, which I'm not fond of. Other times APIs appear to have been arbitrarily changed or moved. So, this has definitely had a negative impact on me, as it's taken a significant time away from life. Much of this time would have been given back to the community in the form of new features, tooling, etc. but the time I've had to spend constantly porting has made those things impossible and drained a lot of my desire to work on them in the future.

About the Library Author

Rob Eisenberg is a .NET architect and developer working out of Tallahassee, FL and he is the President of Blue Spire Consulting. Rob got his start with computer programming at the age of nine, when he thoroughly fell in love with his family's new Commodore 64. His fascination with programming started with the Commodore Basic language, then moved to Q and QuickBasic and quickly continued on to C, C++ and presently C# and the .NET Framework. Rob publishes technical articles regularly at devlicio.us and has spoken at regional events and to companies concerning .NET technologies, Agile software practices and UI engineering. He is coauthor of Sam's Teach Yourself WPF in 24 Hours and is the architect and lead developer of the Caliburn and Caliburn.Micro Frameworks for WPF, Silverlight and Windows Phone 7. When not coding, Rob enjoys swing dancing, making artisan cheese and playing drums. Follow him on Twitter with @EisenbergEffect

Rate this Article

Adoption
Style

BT