A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.
This is a little long but hopefully provokes some interesting conversation...
Say you have a set of classes representing UI controls (I'll base my examples loosely on the .NET Windows Forms controls -- don't get too picky if the controls already support some of these things -- it's just an example). You have a base Control class from which the other controls derive (not necessarily directly) -- Label, TextBox, Button, etc.. You'd like to enhance some of these with some custom behaviors. For example, you'd like to have the ability to set some controls "transparent" to mouse clicks so that the clicks transfer back to the parent. You'd also like some of the controls to have a consistent custom background. Some of the controls will have both of these behaviors, some will have one or the other, some will have neither.
The typical way of adding new behaviors would be to derive new classes -- Control -> Label -> CustomBackgroundLabel. This has a few obvious problems. For one thing, I'd have to derive new controls for every control that I wanted to have this behavior, using essentially identical new code for each. Another problem is that there are multiple new behaviors. I'd need CustomBackgroundLabel and TransparentClickLabel and CustomBackgroundTransparentClickLabel. Add another new behavior and you double the number of new classes needed. Or you could go with the kitchen sink approach and toss all your new custom behaviors into one derived class MyLabel but that gets ugly fast and can inhibit easy reuse of the class in other projects. Another annoying thing about the base class approach is that you have to derive new classes for every new control introduced that you want to add the behaviors to.
In an ideal world, I'd be able to flexibly insert a class in between the Control and its child classes, for example, Control -> CustomBackgroundControl -> Label. I've seen this kind of idea implemented using templates in C++ but I don't think a similar facility exists in C# or most other languages.
They do provide an interesting system in Windows Forms for adding new behaviors -- IExtenderProvider -- which allows new properties and new behaviors to be easily associated with existing control classes. The problem with this is that it doesn't give you complete control -- for example, you can hook into a control's events but have no (simple) way of overriding virtual methods -- such as WndProc.
So all of this has me thinking of the best ways to handle this. How could the controls in Windows Forms have been better designed to facilitate extending the controls with new behaviors? What are some good ways of extending the behavior of existing controls in a library you don't have direct control over?
In the case of the Windows Forms example, I'm leaning towards the idea of deriving new controls from the existing controls to support more flexible extending. These controls would mostly just add events for some of the common overrides that don't have associated Control events -- such as OnPaintBackground and WndProc. It's the kitchen sink approach but in this case the idea is to add a bunch of things to support new behaviors but not the new behaviors themselves. New behaviors would then be added using the existing IExtenderProvider mechanism or with something similar.
I've seen one implementation of the layers you suggest: the framework provided a base layer and the layer that you actually used. Between the two sat a layer of your own code. Similar to your suggestion of "Control -> CustomBackgroundControl -> Label". It was in PowerBuilder. I vaguely recall it being more trouble than it was worth, and I've certainly never missed it when working in other tools.
This thread on extending existing classes is interesting - http://weblogs.asp.net/ericgu/archive/2004/07/01/171149.aspx - although there's nothing implemented or announced yet.
Tuesday, November 30, 2004
Your problem indeed is a task for templates. Did you think about creating a dll containing your widgets using C++.NET? I'm not sure, however, if you can use both templates and .NET features in C++ at the same time, but I see no reason why it shouldn't work: Having the best of any languange is a key feature of .NET, isn't it?
If you are not familiar with C++, you can try to encapsulated the desired behaviour into a C# component, reducing the C++ code to a bunch of one-liners that are just overloading and delegating.
If C++ is no alternative, I would tend to do it like you mentioned. Create an Interface (IExtendedWidget), derive from this interface and each widget (Label, Button), keep an internal collection of desired behaviour classes (TransparentCilck, CustomBackground), and delegate to them when required. For the code is always the same, execpt only the base class changes, this would be a task for a code generator.
Tuesday, November 30, 2004
The Decorator design pattern is what you are looking for. The idea is to use composition instead of inheritance to add or enhance behaviour. Seems to be fit the bill ..
Tuesday, November 30, 2004
I learned early on to create a shim derived class from every single UI base class in whatever the class library was and whatever language and to only create custom classes or use that shim layer and never the base class.
Wednesday, December 01, 2004
What you need is the Controller part of the Model-View-Controller architecture. Unfortunately, WinForms and (most other toolkits) collapse the View and Controller into a single object.
Controllers ideally are composable, so you could write your "TransparentController" generically, and have it delegate stuff it doesn't understand to the lower level LabelController or ButtonController or what have you.
You CAN still do this sort of stuff in winforms by hooking a control's WndProc. Then process the messages as they go by. Unfortunately, it does require some low-level hackery and you're now down in the bowels of Win32 and lose those nice high-level abstractions.
This topic is archived. No further replies will be accepted.Other recent topics
Powered by FogBugz