Yes, this is overdue. Here is an introduction and table of contents to my “Build Your Own CAB” series of blog posts on designing WinForms applications. You’ll see nothing here about user experience and not much WinForms technology. That stuff is covered quite well in a hundred different places. This series is about code. How to write it, how to write less, how to test it, and how to structure it. And there is code in user interfaces, no doubt about that. Code that’s complex. Code that changes every time you’re foolish enough to ask an end user how they like it. Code that’s hard to test if you’re not careful.
I’m still working on the series, so check back here occasionally for updates and new downloads. After I finish posting the menu state series I’m going to take a break for a while, but it’s for a good cause. The most persistent complaint throughout the series is that it’s not clear how all the pieces fit together. Understandable. At the same time that I’m writing this series I’m also working on my OSS StoryTeller tool that includes a sizable WinForms client. Last week I scrapped about half of the StoryTeller UI code so I could go back and incorporate the ideas that I’ve explored through “Build a CAB.” I can say without a doubt that I’m the one person who’s learned the most from “Build a CAB.” What I’m hoping to do is use the StoryTeller UI code to demonstrate fully working screens from end to end. The beauty of using OSS code for examples is that the code is automatically available for download.
I’ll finish before the end of the summer, I promise. Just set a bookmark to this page and I’ll keep updating it as the series expands. At the end I’ve committed to gathering all this stuff up into a single, printable PDF for download.
Table of Contents
There’s an extended introduction below.
- How I got into this mess – Look for the section “Microsoft, OSS, and the Patterns and Practices Team”
- The Humble Dialog Box
- Supervising Controller
- Passive View
- Presentation Model
- View to Presenter Communication
- Answering some questions
- What’s the Model?
- Assigning Responsibilities in a Model View Presenter Architecture
- Domain Centric Validation with the Notification Pattern
- Unit Testing the UI with NUnitForms
- Event Aggregator
- Rein in runaway events with the “Latch”
- Embedded Controllers with a Dash of DSL
- Managing Menu State with MicroController’s, Command’s, a Layer SuperType, some StructureMap Pixie Dust, and a Dollop of Fluent Interface
- Boil down the “wiring” to a Registry
- The Command Executor
- The Main Players
- Testing through the UI – Forthcoming
- Subcutaneous Testing – Forthcoming
- Creating the Application Shell – probably a couple posts
- Wiring the Components with an IoC tool – Forthcoming, but I may push this off into late summer
- A Day in the Life of a Screen – by popular demand (gripe), let’s look at a couple of complete screens through their entire lifecycle
In building enterprise software systems I’ve often observed a feeling that building the actual User Interface is less demanding or important in terms of design compared to the backend. We worry quite a bit about the appearance and the usability of the User Interface, but how about the code structure of the UI itself? In my experience, UI behavior changes more often than anything else and generates a much higher defect rate. Several years ago I briefly used Use Case Points as an estimation technique. One of the features of this technique that caught my eye at the time was how it treated user interface features. Any feature that included an interactive user interface was automatically given a higher modifier of effort than the same size feature that only dealt with other computers. In that technique it was a given that interfacing with a human was more complex than interfacing with other code. My point being here that user interface development is complex and we need to take it seriously.
A couple years ago heavy clients were out of style due to well-founded concerns over deployment and support issues. Lately, or at least for me anyway, heavy clients seem to have made a comeback as the newer .Net technologies have alleviated the deployment concerns somewhat. Instead of dying away, we’re building more complex WinForms user interfaces than ever before. There’s a lot of talk about building “composite” applications that can quickly host multiple, logical applications or applets in the same application shell. We’re trying to build applications that can be easily extended without ripping up the existing code. We’re sold on Test Driven Development, but the user interface has remained one of the last uncharted territories for automated testing. The traditional approach towards building user interfaces in the Microsoft is to fire up the designer and pump energy and effort into it until it works. That has to change.
At DevTeach 2007 I gave a talk called “Put Down that Wizard!” with a subtitle “Design Patterns for Maintainable WinForms.” By and large, the talk was about how to code and design a WinForms application. I discussed design patterns for assigning screen responsibilities, screen synchronization, and issues that effect testability of user interface code. I was fairly happy with the way the talk went and the feedback overall, but a particular comment from an attendee caught my eye: “the content was obscure.” The criticism itself wasn’t bothersome in and of itself, but the fact that these patterns were considered “obscure” definitely bothered me. Designing software is largely an exercise in determining the responsibilities of the code and assigning these same responsibilities to different modules of the code. How you assign, or fail to assign, responsibilities has a great impact on the maintainability of a codebase. Patterns like Model View Presenter are a reoccurring recipe for assigning responsibilities in a user interface. These patterns weren’t created or invented, they were discovered by practitioners who began to describe the structures that worked (or didn’t work). If we’re going to build and evolve more complex user interfaces, we can’t blow off the internal structure of the user interface code.
At DevTeach 2007 I was part of a panel on the tenuous relationship between Microsoft and the Open Source community. At some point in the panel discussion and the resulting blog discussions afterward, we set off an argument about whether or not the Composite Application Block (CAB) framework was necessary or even valuable for building larger, maintainable WinForms applications. My point of view then, and now, is that understanding the core concepts and patterns is the first step. I don’t think you can use the CAB effectively unless you understand the underlying patterns. On the other hand, if you understand the underlying patterns and design forces, you can quite efficiently and effectively do your own thing. Either way, you want a strong core of design principles.
User interface technologies change. I’m sure it’ll take a while, but WPF screens will eventually eclipse WinForms and who knows what’s over the horizon. The specific knowledge that you carry around about WinForms widgets will be obsolete soon. I think the Microsoft community stresses API knowledge too much and core design principles too little. When I switched jobs last fall I knew I was going to need a better foundation on WinForms, so I bought and read one of the more popular books on WinForms by a Microsoft luminary (and I say this without any irony). I read the book and it presented the technology behind WinForms very effectively. The wizards and designer support was well covered, but the book didn’t address the issues of maintainability and testability in WinForms development. There wasn’t a single mention of any kind of View/Controller separation in the entire book. That’s a huge gap in knowledge, and one I hoped to help fill by writing this series.
The way it’s unfolded, this series isn’t really about replacing the CAB with your own Model View Presenter pattern. It’s just the things you can do to make a maintainable WinForms architecture — with or without the CAB. A large chunk of maintainability for me is creating code that’s easy to test with granular unit tests, and you’ll see this desire for testability permeating the entire series. Despite rumors to the contrary, it is possible to Test Drive WinForms development, but you testability has to be a first class design goal.
One of the other points I wanted to get across in my DevTeach talk was that oftentimes a code-centric approach can accomplish user interface tasks faster and with less duplication than using the designer. I’m just now starting to explore some of these code-centric solutions through the later posts involving Fluent Interfaces. The designer is great for laying out the look and feel, but we can’t allow the designer to be our architect. I don’t think WinForms is a bad technology at all, but it’s deeply flawed in the sense that it does not lead you into a maintainable structure. The designer makes it relatively easy to crank out user interface code, and that’s what it’s doing behind the scenes, but some judicious code-centric design can eliminate duplication and reduce the overall effort. Maintainability is your job, not the designer’s.
So hotshot, you think you can build a better CAB?
No, I don’t, that’s not my point. My point was just that if you were well armed with an understanding of the underlying design patterns and motivation that you could happily evolve your own application specific framework that would better fit your application. Part of that belief is that building a framework is much simpler in this day and age of Dependency Injection tools. Today, you can pull one of a couple different Inversion of Control / Dependency Injection tools off of the shelf that are all more powerful than the limited ObjectBuilder inside the CAB. Then again, I’m the progenitor of one of those tools, so my opinion might be somewhat suspect.
And yes, to be honest, like many developers there’s an inner demon inside of me that arrogantly believes he can build a better [insert name of framework] and is just dying to prove it. Begone inner demon, begone!