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

A “Default Command” for Silverlight

Posted in Avanade | Silverlight at Monday, September 28, 2009 7:23 AM Pacific Daylight Time

The current Silverlight application that I’m building has a Login view. One of the things that bugged me when I started using the application is that you would have to click the Login button after typing your password. I wanted to duplicate the default button behavior of HTML forms where when you hit the enter key it would trigger the default button on the form. I did some googling on the subject and came across this post by Patrick Cauldwell which is one way to solve the problem. However, in my case I had a username Textbox, a password Passwordbox, and a company Combobox and didn’t want to specify the button for each control.

So I create a simple solution of creating a content control that attaches to all the KeyUp events of all the child FrameworkElements in the content. To do this I used the FindChildren<T> extension method from the Avanade Silverlight Accelerator which is a toolkit we use internally at Avanade to speed up Silverlight development. The ContentControl exposes a DefaultCommand property which you then bind to the ICommand property on your ViewModel.

Below is a trimmed down example of the Login view. I’m using a variant of the RelayCommand/DelegateCommand as the LoginCommand here (see Laurent’s post on the RelayCommand for a good overview of Commands in Silverlight).

<ctrls:FormControl DefaultCommand="{Binding LoginCommand}">   
  <TextBox Text="{Binding Username, Mode=TwoWay}" />
  <PasswordBox Password="{Binding Password, Mode=TwoWay}" />
  <Button IsEnabled="{Binding LoginEnabled}" 
          cmds:ButtonClickCommand.Command="{Binding LoginCommand}" 
          Content="Login" />
</ctrls:FormControl>

There are many other things you could add to this but this is all the functionality that I needed and I decided to keep it simple. Download the class file (plus the extension method) below. Let me know if you find it useful!