Feed Icon  

Contact

  • Bryant Likes
  • Send mail to the author(s) E-mail
  • twitter
  • View Bryant Likes's profile on LinkedIn
  • del.icio.us
Get Microsoft Silverlight
by clicking "Install Microsoft Silverlight" you accept the
Silverlight license agreement

Hosting By

Hot Topics

Tags

Open Source Projects

Archives

Ads

Behaviors vs Subclassing in Silverlight

Posted in Silverlight at Wednesday, September 30, 2009 1:42 AM Pacific Daylight Time

As a Silverlight developer, when you want to add functionality to an existing control, you have two main options as I see it (if you want to get reuse from your code). You can either subclass the control or, as of Silverlight 3,  you can write a behavior for it. For example, one of the requests for the current Silverlight application that I’ve been working on was to have the TextBox select all the text when you tabbed to it or clicked in it. We can easily add this functionality using both of the above methods:

Here is how this could be done using subclassing:

public class SelectAllTextBox : TextBox 
{
    public SelectAllTextBox()
    {
        this.GotFocus += new RoutedEventHandler(TextBox_GotFocus);
    }

    private void TextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        this.SelectAll();
    }
}

And here is how you would write this as a behavior:

public class SelectAllBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.GotFocus += new RoutedEventHandler(AssociatedObject_GotFocus);
    }

    void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
    {
        ((TextBox)sender).SelectAll();
    }
}

The behavior has one more line of code and the added requirement of adding a reference to System.Windows.Interactivity.dll from the Blend 3 SDK. The bigger difference is how the code looks in our view when we add the control to it.

The subclassed control looks like (where ctrls is the controls namespace of our subclassed control):

<ctrls:SelectAllTextBox Text="{Binding MyText}" />

And the behavior looks like (where i is the System.Windows.Interactivity namespace and b is our behavior’s namespace):

<TextBox Text="{Binding MyText}">
    <i:Interaction.Behaviors>
        <b:SelectAllBehavior />
    </i:Interaction.Behaviors>
</TextBox>

Obviously the behavior is more verbose in this case than the subclassed approach.

Since both of these approaches work, which is the better approach? I think the subclassing is the easier approach, but I think the behavior would be the recommended approach. The reason is that I can build my SelectAll behavior today and then down the road build a different behavior and then selectively apply them to my TextBoxes as appropriate. However, if use the subclass approach I would automatically get the new behavior on all my controls which might not be what I wanted. It also means that if someone builds a better TextBox that I want to use that I would have to try to subclass that control, but with the behavior I could just apply it to the new control.

Update: A couple of quick updates. First, Alan Le pointed out that it depends on reuse. Obviously if you had to add the behavior to 20 TextBoxes it would take more time to use the behavior. However, Blend makes this a lot easier. Secondly, Brian mentioned in the comments that you could also use an attached property to do this so I thought I would quickly show what that might look like.

The code for the attached property would be:

public static class TextBoxProperties
{
    public static readonly DependencyProperty SelectAllProperty =
        DependencyProperty.RegisterAttached("SelectAll", typeof(bool), 
        typeof(TextBoxProperties), new PropertyMetadata(false, OnSelectAllChanged));

    public static void SetSelectAll(DependencyObject o, bool value)
    {
        o.SetValue(SelectAllProperty, value);
    }

    public static bool GetSelectAll(DependencyObject o)
    {
        return (bool)o.GetValue(SelectAllProperty);
    }

    private static void OnSelectAllChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue == true)
        {
            ((TextBox)d).GotFocus += new RoutedEventHandler(TextBoxProperties_GotFocus);
        }
    }

    private static void TextBoxProperties_GotFocus(object sender, RoutedEventArgs e)
    {
        ((TextBox)sender).SelectAll();
    }
}

And the Xaml would look like:

<TextBox Text="{Binding MyText}" ctrls:TextBoxProperties.SelectAll="true" />

I still think the Behavior is the best method to use since (at least in this case) we are just trying to add a behavior to the TextBox, not add significant functionality. The attached property also doesn’t feel right to me, but it does work just fine. Ultimately it comes down to preference and what method you like to use. :)

Wednesday, September 30, 2009 6:24:12 AM (Pacific Daylight Time, UTC-07:00)
I've found attached properties to also be a nice way to do some of this simeple kind of work.

<ComboBox ... local:Clear.EscapeKey="True" />

Allows us to clear the selection when escape key is hit... (we also did backspace)

-Brian
Brian
Wednesday, September 30, 2009 11:03:13 AM (Pacific Daylight Time, UTC-07:00)
"Brian mentioned in the comments that you could also use an attached property to do this"

Just to nitpick, the behavior you added is also an attached property, so there's not really a difference there.
Thursday, October 01, 2009 3:08:50 AM (Pacific Daylight Time, UTC-07:00)
@Morten While behaviors are implemented as an attached property I don't see them as being the same thing. They have first class support in Blend 3 for designers and are considered a feature of Silverlight 3 (while attached properties have been around since Silverlight 2).
Bryant Likes
Thursday, October 01, 2009 4:42:49 PM (Pacific Daylight Time, UTC-07:00)
Hopefully the third-party component builders are reading this and paying attention. This could stop the huge amounts of properties they throw at developers so that they can customize all these controls. Just provide behaviors and it will do the same, but a lot less messy.
Thursday, October 01, 2009 5:42:31 PM (Pacific Daylight Time, UTC-07:00)
I'd suggest that the important difference isn't about brevity or even reuse: it's about clarity and intent. Interestingly, the reuse question really calls back to Silverlight's difficulty place in between "scripting" and "engineering."

They're both great ways to get work done, but they have totally different implications for maintenance. They have different reuse implications as well, but let's be clear: maintenance is far more important than reuse!

The meaning(1) of subclassing is different from using a Behavior. In the end, the two compile to similar enough code to have very similar _denotation_, but the _connotation_ is wildly different. In the real world, that connotation is the difference between projects that fail after their first demo and those that survive to reach two-point-oh.


A SelectAllTextBox is a different _class_. It isn't intended to be a text box with one additional thing: it is a new thing with its own (lower-case) behaviors. You create a subclass when you want a new entity that can stand in the place of an old one. You are saying, "There is thing that is like a TextBox, but is more if you look closely, and that is the kind of thing I am using here." You are saying, "I have a model of the objects in this system and that model includes this as a discreet item."

You instruct the maintenance engineer to maintain that model: Because a SelectAllTextBox is a new kind of thing, whoever reads the code has to learn *that* it exists and *what* it does, but that person can then use it as a drop-in replacement as needed. Its existence implies that the SelectAllTextBox is either an important item that may be needed repeatedly or that it may be the base for several additional kinds of classes.
- A SelectAllTextBox ISA TextBox and has its on additional attributes. It is part of the toolset now. Different instances of it behave similarly.

On the other hand, a behavior is a decorator to an _object_, not to _class_. You take an existing TextBox and "doctor it up" to do something new. You are saying, "This is a text box, plus I want it to be a little special," much like setting any property would do. The SelectAllBehavior leaves the attention on the control _instance_ and adds a special case: there is more to attend to right now, but nothing in the larger context to clutter the mind of the engineer.
- A TextBox that HASA decoration to select all its contents when it gets the focus is a special case applied to an existing element. You have to examine each TextBox element to see if it has that decoration.

These aren't just different ways of getting the same few lines of MSIL to execute (and, to be fair, they get to those line pretty differently).

Subclassing is the more classic "engineering" type of approach. It suggests that you have modeled the requirements and identified that some text boxes need this special behavior, so you can created a type. As a new engineer on the project (or as the original author six months down the line, when it's all paged out of my head), A) I have to learn what it does, B) I don't have to examine TextBoxes to see whether they have that attribute. C) I can switch an element from one to the other trivially.

Decorating with Behaviors is a more "scripting" type of approach. It focuses the attention on the screen element and not on any fancy (often overblown) model of user requirements or control types. It is the choice for patching in quick fixes because it localizes change to the one place. As a maintainer A) I don't have to learn any new models or classes, but B) I do have to remember special cases for each and every one-off Behavior that appears.


You can't ever recommend one course or or another without criteria, so what criteria would suggest one versus the other? It's easy to suggest rubrics like Number of Times Used, Shorter Code, or a preference for either Larger or Smaller Object Models, but let's draw back and get to the real criteria behind those: Clarity of Intent, Simplest Technology, Minimal Context ("compactness"), and Low Change:Risk Ratio.

All of these feed into the question of reuse, but we often consider reuse as though it were a virtue all by itself. It isn't. We care about reuse because we want to create useful and effective projects that we (and others) can maintain and extend reliably. If we're arguing about what takes more characters to type instead of what comprises an elegant and readable program so programmers can make practicable changes, then we've lost the point(2).


The top recommendation is simple: Just don't ever, ever mix the two. Having a SelectAllTextBox and a TextBox with the Select All Behavior in the same application is evil incarnate. It is, sadly, also a likely scenario, especially since the "scripting" approach doesn't lead as easily to discovery (you can easily wind up with both SelectAllTextBox and TextBoxThatSelectsOnFocus classes, but you are at least as likely to have SelectAllBehavior and SelectOnFocusBehavior floating around).

If I see the behavior approach on a project, I know the author didn't intend heavy maintenance. This is appropriate for many pieces of many apps. It's often easier to just rewrite screens than try to refactor them. If I see the subclass, I step back and ask what the original author thought they knew and make sure I'm following that.


I guess the point is: The call to select the text gets run in both cases. What are you telling the programmer who looks at the code in six months? Is this a one-off attribute? Part of a considered design? A patch? Should it be preserved as-is or should it be rewritten?


And sadly, no matter what, you're usually better off going with what your tools support best, regardless of most other considerations, since that is what later programmers will be comfortable with and what they can most quickly read.

---

1) "Meaning" is the *intent* it conveys and directly affects how one reads, maintains, and reuses the code.

2) Well, unless you're being a "computer scientist" instead of a "programmer." Most computer scientists I've met care a lot more about how their code looks on paper than whether it works or how someone can maintain it.
Thursday, October 08, 2009 8:11:46 PM (Pacific Daylight Time, UTC-07:00)
Your blog cuts off the ends of most of the lines which makes your articles a bit of a hard read.

I'm using IE8.
GF
Friday, October 16, 2009 9:14:47 AM (Pacific Daylight Time, UTC-07:00)
I like the behaviors as well. However with an attached property you can set this in the style and apply it to all 20 textboxes, so this approach also has its advantages (You cannot add to the behaviors collection in a styles without using yet another surrogate attached property that does it for you)
Saturday, November 14, 2009 4:05:59 AM (Pacific Standard Time, UTC-08:00)
@Seth

Seth, from what you wrote it looks like _EVERY_ feature should be implemented with derivation: but what about a red foreground textbox? would you make a RedForegroundTextBox : TextBox class? Yes, it would be really readable, but then, when you need a red foreground textbox with the selectallonfocus behaviour? Rapidly you would go out of classes, to represent all the combinations! :-) But then, I already know it, some engineer would come out with a complex plugin architecture, letting the programmer adding dynamically just the behaviours we get for free (almost) with behaviour support in Silverlight 3!

More practically, I always think that one more technique is better than one less, and that trying to engineering every aspect of programming falls always short, somewhere in the future of the application.

Just my two cents...
Andrea Bioli
Comments are closed.