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. :)