Controls, Interfaces, and Helper Methods

I just ran into an interesting design decision today (at least it was interesting to me. You may end up going into a coma due to boredom after reading this post - you have been warned ;) ). Here's the situation. I wanted all of the common WinForm controls (like TreeView, Panel, and Form, just to name a few) to have some common "tweaking". Let's say this common behavior can be encapsulated in a property called Tweak [1]. Unfortunately, I can't "share" this code across these control because I have no access to a common ancestor in the inheritance tree, so what I did is create an interface called ITweaked that has the Tweaked property, and I create custom versions of the WinForm controls (like TweakedPanel) that implement ITweaked. It looks like this:

public interface ITweaked
{
    bool Tweaked
    {
        get;
        set;
    }
}

public TweakedPanel : Panel, ITweaked
{
    public bool Tweaked
    {
        // Tweak the panel...
    }
}

Now, what I do in Tweaked is a little different for each custom control, but whether I created a helper class that my custom controls all use or if I use the "copy-and-paste" technique is not important to discuss right now. What is important is that, when the tweaking is done (which happens in the setter), I have to tweak the control's child controls, and this code can be generalized into a helper method.

This is where it gets interesting, because how the child control is tweaked is dependent on the fact if the child control implement ITweaked or not. Furthermore, I only want objects passed into this method that are of type Control and implement ITweaked. Here's what I did - I'll explain why I did it this way in a moment:

private static void UpdateTweaking(ITweakedControl tweakedControl)
{
    Control control = tweakedControl as Control;

    if(control != null)
    {
        foreach(Control childControl in tweakedControl.Controls)
        {
            ITweakedControl childTweakedControl = childControl as ITweakedControl;

            if(childTweakedControl != null)
            {
                childTweakedControl.Tweaked = tweakedControl.Tweaked;
            }
            else
            {
                if(tweakedControl.Tweaked == true)
                {
                    //  Handle the true case...
                }
                else
                {
                    //  Handle the false case...
                }
            }
        }
    }
}

What I really want is a way to constrain the tweakedControl paramater such that I know it's of type Control and that it implements ITweakedControl. There isn't a way to do that in one shot, so I have to make a choice: is it more important that the incoming type is a Control or implements ITweakedControl? As you can see, I think it's the latter. The reason is that a developer will have to make a conscious choice to use this interface, so it's more likely that UpdateTweaking will be called by a developer who has created a tweaked control. However, I still need to make sure I have an object of type Control, which is what the first line of code does.

Again, this isn't an earth-shattering problem, but I found it interesting!

[1] As you can guess, code naming has been changed to protect the innocent.

* Posted at 12.07.2004 02:58:44 PM CST | Link *

Blog History