So you were a very happy software developer when you rapidly created the two-tier rich client application for your customer. You dragged and dropped away the forms and user controls, hooked up their events and voila!, you released it to your customer.
Trouble starts a few months later when your customer asks for a few 'simple' modifications that touch the User Interface (UI). For example, he wants a drop down control to be populated based on the invoice type or a data grid to have certain cells editable depending on the account type. At first glance, these requested features seem simple but not after you look into your code and think about how to 'squeeze' or 'hack' in these new features.
If you have not implemented the UI with extensibility in mind, you're about to start diminishing the return of investment (ROI) of your app. Your code will start smelling by the process of patching in new code. This will gradually bring your app development-abiliy to a halt where you won't be able to modify it anymore. A few modifications later, it will crumble under its own weight. You wouldn't be able to respond to your new customer's features and your code becomes what's called a BBOM (Big Ball Of Mud) or start to resemble a famous Italian dish.
However, if you did implement your UI with an extensibility design in mind, you wouldn't be 'hacking' in those new features. They would fit right in like a glove.
By now, you're thinking Model-View-Controller (MVC), but if there's a pattern so widely mis-applied MVC would be the one. This pattern is rarely clarified or implemented properly for rich clients. Usually, I've seen developers implement one side of it but not the other and therefore not reaping the full benefit of using it in the first place.
Since the program is started by a Main Form, it is very convenient (and error prone) for the developer to start the control from the Main Form (UI) and consequently have all forms do a lot of business logic work while creating a 'cosmetic' controller to handle data manupulation.
An extensible MVC implementation is to have the controller implement an interface (IController), initialize and create the view (forms). Then, injecting itself in that view. After that, the have the controller subscribe to the view's events to handle communication. Now the view is totally decoupled from the UI controller.
This design will allow for swapping the controller with another one to implement different variations. So when your customer asks for a variation on UI behavior you can easily subclass the controller or compose it with a different UI strategy.
To go one step further, you can make the view (forms) reusable by having them implement an IView interface of their own. That way you can swap a different view anytime with minimal coding and least errors.
To keep the view interactive have it subscribe directly to the model data events or the more groomed controller data events. That way, the view is decoupled from the model and controller.
One issue remains is the data transfer objects (DTOs) or entities to carry data back and forth from the view. Should they be leaked all the way to the view or make the view totally data generic and use dictionaries and name/value pairs to display data? In other words, should the data in the view be strongly typed or generic?
Although from a purely design standpoint it would be better to have a totally data-generic view for the purpose of reusability of the view, my experience on this with complex/large data entities has been towards using strongly typed entity data in the view whenever you can. In reality, forms are coupled to their projects and are not widely shared, so it is not worth the pain of mapping back and forth from generic data containers to the domain/model data entities.
This is unless of course it is a general utility form. In that case, it makes sense to keep its data generic like dictionary of strings from drop down controls and name/value pairs.
It is simpler and easier to use the same model data entities that are used in the domain and it doesn't hurt extensibility. This is because by definition, these model data entities or DTO's are standard, versioned and also change with the domain data entities anyways. If you change your domain/model data entities, chances are you will need to make those changes to the view data entities, so mind as well reuse the same data entities.
This does mean that as a good practice you should have your domain/model data entities seperately designed and maintained for reuse in all your application layers.
So in a nutshell, to have an easilty extensible UI, do the following:
1. Implement the full MVC pattern by having the controller implement an IController interface, instantiate and initialize the views (forms and user controls).
2. Use events (Observer pattern) and abstraction (interfaces) for both the views and the controller to interact with each other.
3. Use independent DTO's or domain/model entities to communicate all the way to the view in most cases.
I hope now that you have less pain and less bugs adding more UI features to your app. Long live well designed apps!