Okay, I’m going to air some dirty laundry here – a few weeks ago we had an internal discussion about the pros and cons of using sub-classing of Windows Forms controls as standard practice to support cross cutting concerns like security, validation and even things like stylings. It all started out innocently enough with an e-mail containing the following string of text:

“Any non-trivial application should only use UI Libraries via inheritance.”

Those of you who know me probably know that I am a bit of a fan of extender providers, so I felt compelled to argue the case for extender providers instead of sub-classing. After twenty-six e-mails had been exchanged I hadn’t managed to swing the other thread participants around to my point of view, I was squarely in the minority. Doh!

After writing another extender provider component today some of the discussion points started rattling around in my head and I felt compelled to put some of my thoughts down in this post, and maybe, just maybe convince my co-workers that extender providers are more fun than a pin in a balloon shop.

Before I can effectively argue the case for extender providers we really need to take a closer look at all that the sub-classing approach entails from an implementation point of view. Essentially the strategy involves sub-classing every single Control before using it in your project. Diagramatically you would end up with the following class hierarchy if all your application used was buttons and text boxes.

InheritenceClassHierarchy

Yes – I know that there are a few more levels of inheritence in there, but thats a minor detail as far as we are concerned. The idea of the above approach is that within each sub-classed control we could insert code that addresses application-wide or even enterprise-wide concerns. The diagram below shows how you might nest security and validation logic into the controls.

LogicInControls

The obvious objection that can be raised here is that there is a certain amount of duplication that occurs in each sub-classed controls implementation to support these cross cutting concerns. The counter argument is that you might write that code anyway since every controls behaviour under certain security conditions (as an example) is likely to be different. My experience however tells me that when it comes to UI security you are going to do one of three things with a control – make it invisible, make it read-only or make it disabled.

Building an extender provider to check list of roles (extended property) against an underlying credential store and use reflection to find the Visible, Enabled and ReadOnly properties is probably a reasonable solution to this problem – but I’m sure you can find a reason to use the sub-classing approach.

If you persist with your sub-classing ways you will end up with a very broad class hierarchy with a whole heap of sub-classes that are doing very similar things (security, validation etc).

ExpandedHierarchy

To help reduce the maintainence headaches associated with the above situation most reasonable developers will abstract away the common code from each of the sub-classed controls and leave a pretty thin call site which just delegates as much as possible to the API.

LogicViaCallSiteToAPI

This approach works really well until you start dealing with the more complex “multi-component controls” like the TabControl, the built-in DataGrid or even components like Infragistics. Lets take the TabControl as an example that should be familiar to everyone.

HumbleTabControl

If you look at the above screenshot you can see a form with a tab control on it – two components right? Wrong. This form actually has four components, the form itself, the tab control, and one for each tab page.

If we look at the security scenario again, its actually the tab pages that we want to grant or deny access to, not the tab control itself so we would need to sub-class TabPage and create OurTabPage. Thats fairly trivial – if you’ve been using the sub-class approach up until now you are probably pretty adept at adapting the call site logic to new controls.

Unfortunately now you’ve got a problem. Developers are used to clicking on “Add Tab” in the designer, but when they do that it throws in the stock standard TabPage, not OurTabPage, the only workaround you have is dropping into code and manually adding tabs – lots of fun, but hardly a productive experience.

If you are like me, you are probably thinking – I wonder if I can change the behaviour of “Add Tab” through the use of some designer magic. Most if not all of the controls that ship out of the box with the .NET Framework have an associated designer, and for more complex third-party controls its almost a given.

What we would need to do is sub-class the TabControl designer and  override the Verbs get accessor which is where the “Add Tab” context menus and hyperlinks (in the property grid) come from. The class you want to derive from is TabControlDesigner which is in the System.Windows.Forms.Design namespace in the System.Design assembly (confusing eh? get used to it – designer technology is seldom straight-forward).

Of course, I’m just teasing because TabControlDesigner is actually marked internal so you can’t sub-class it which means you can’t intercept those calls to “Add Tab”. About the only thing you could do is create a whole new designer for the tab page yourself and encapsulating the existing designer (only interfacing to it via Refelection). If you think thats a good idea – then this may be your language of choice.

Actually, it is possible to do it without implementing a designer or using reflection, but its a bit of a timing based hack as you can see below.

VerbInjectionCode

I include this code not as a form of encouragement, but more as proof that I have really considered the sub-classing approach to addressing cross cutting concerns. The code above obviously has issues and is not ready for production use.

The point I am trying to make is that sub-classing pretty quickly becomes non-trivial and you end up having to know more about designer plumbing that you would have had to if you just went with the extender provider approach. Can you imagine trying to use the above tricks with the Infragistics grid?

Time for a wierd analogy. When I think about interacting with components on a design surface I assume that I am dealing with an iceberg. On the surface they all look pretty simple, but underneath there can be something big and scarey waiting to sink your ship (actually – I just wanted to include this cool graphic of an iceberg).

iceberg.jpg

Extender Providers essentially provide you with the visibility you need at the sub-component level and allow you to add properties at design time and handle events at runtime to apply new state and behaviour to any component on the design surface.