On Labels and Triggers

Designing form for web pages is not an easy task. No one likes to fill out a form, and most people are uncomfortable when there are more than just a few of controls. Layout of elements that focuses the user’s view on the most important elements, and leads her through the process until completion, makes a huge difference, but there’s more. A web form is not only perceived by the eye, but also by the hand – through the medium of a keyboard or a mouse.

For some people, the tab order in forms in paramount. This is a known and accepted fact in the realm of business applications, and rightfully so. On many forms on the Internet however, the sentence “tab order is important” seems to raise the same reaction as “the moon is made of green cheese”. The big benefit of using the tab key is that you never have to leave the keyboard. A test script for any form, anywhere, should include a test like that:

  • When I open the form, the first control is focused: I can start typing without first manually selecting a control.
  • When I hit “tab”, the focus moves to the next control in a logical order that is intuitive from the layout of the form.
  • When I’m done with filling in data, the focus is on the button or link that executes an action to commit the entered data somehow so that I just have to hit “Enter” to activate it.

Try that on your favorite web site/application.

Another behavior that users expect is that clicking the label next to a control sets the focus. With smaller controls, this behavior increases the area that can be used so that less precision on the user’s side is required. And then there’s people like me who literally can’t read a web site without the finger on the trackpad. I tend to support the point I’m looking at with the cursor, and highlight text that I find interesting (sitting next to me and trying to read the same page isn’t such a good idea). So when I want to select a control on a form, the intuitive thing to do is to click the label – the cursor is there already anyway.

Works on WinForms. Works in HTML (when the tag has been used). In Silverlight? Nope. There is a control called “Label” in System.Windows.Controls.Data.Input.dll that is specifically built for the purpose of acting as a label for controls, but does it implement the selection behavior? Guess.

Luckily, it is extremely easy to build the necessary behavior into a label control of your own.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace GreenIcicle.Controls
{
  public class ControlLabel : ContentControl
  {
    protected override void OnMouseLeftButtonUp( MouseButtonEventArgs args )
    {
      base.OnMouseLeftButtonUp( args );
      if( Target != null )
      {
        Target.Focus();
      }

      // Toggle checkboxes: If a target derives from ToggleButton,
      // toggle its value when then label is clicked AND the control
      // already has the focus. So for unfocused controls, the
      //  first click focuses the  control and the second toggles it.
      ToggleButton toggleableTarget = Target as ToggleButton;
      if( toggleableTarget != null && toggleableTarget.IsFocused )
      {
        // If the toggleable control is indetermined, 
        // treat it as unchecked
        bool isChecked = toggleableTarget.IsChecked ?? false;
        // Toggle the IsChecked state
        toggleableTarget.IsChecked = !isChecked;
      }
    }

    public virtual Control Target
    {
      get { return (Control)GetValue( TargetProperty ); }
      set { SetValue( TargetProperty, value ); }
    }

    public static DependencyProperty TargetProperty = DependencyProperty.Register(
        "Target",
        typeof( Control ),
        typeof( ControlLabel ),
        null );
  }
}

Usage:

<gi:ControlLabel 
  Target="{Binding ElementName=Text1}"
  Content="Some text please">
<TextBox 
  x:Name="Text1"/>

A solution I would have liked a lot more than creating a special control would have been the usage of a trigger. A trigger is defined within the XAML markup in order to add behavior to this element; and because the Silverlight styles are such a strong concept, adding behavior to _exisiting_ controls via triggers looks like a great idea. Except that it does not work in a very interesting way. So this was what I tried: Imported System.Windows.Controls.Data.Input as “dctrl”, and

<dctrl:Label 
  Content="Some text please" >
  <dctrl:Label.Triggers>
	<EventTrigger RoutedEvent="UIElement.MouseLeftButtonUp" >
	  <BeginStoryboard>
		<Storyboard>
		  <ObjectAnimationUsingKeyFrames 
			Storyboard.TargetProperty="IsFocused" 
			Storyboard.TargetName="Text1" >
			<DiscreteObjectKeyFrame 
			  KeyTime="0" 
			  Value="True"/>
		  </ObjectAnimationUsingKeyFrames>
		</Storyboard>
	  </BeginStoryboard>
	</EventTrigger>
  </dctrl:Label.Triggers>
</dctrl:Label>

<TextBox x:Name="Text1"/>

So what I wanted to do is to let the trigger fire on the MouseLeftButtonUp event (you need to declare routed events on the class that declare them; that’s why it’s UIElement.MouseLeftButtonUp), and when that happens, run a storyboard that sets the IsFocused property on the text box to True.

What I got is a runtime XamlParseException that says “Attribute UIElement.MouseLeftButtonUp value is out of range”.

The reason is that EventTriggers in Silverlight only support the Loaded event (as stated here):

In Silverlight, the only event that you can use for an EventTrigger is the Loaded event. For other events, you should declare a storyboard in the Resources property, provide a Name value for the storyboard, and write an event handler that calls the Begin method on the named storyboard.

Now if I need to write an event hander, I can as well resort to the control as shown above. What I wanted to get from the trigger is declarative addition of behavior to an existing control. It’s probably my fault that I haven’t really looked into Triggers (and Behaviors are still to be looked at more closely as well…), but AFAICS, there’s not much value to them right now.

Advertisements
About

Christian is a software architect/developer. He lives in Germany, reads a lot, and likes cycling.

Tagged with: , ,
Posted in Coding

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: