Hard-won wisdom that will improve your Silverlight coding
In the first part of this article series discussing real-world techniques that can be used in Silverlight applications, I discussed how the MVVM pattern can lead to more flexible and maintainable applications, as well as how nested controls can be data bound using a DataContextProxy class. In this second article, I'll walk through additional techniques that I've found useful in different Silverlight application development scenarios. The topics covered include notifying users of successes or failures and extending existing controls to maximize productivity in custom scenarios. Let's start by taking a look at simple animation techniques that can be used.
Notifying Users of Successes or Failures
Traditional applications often notify users of successes or failures by updating labels or displaying dialog boxes. While these techniques are certainly valid in different scenarios, Silverlight provides support for animations and media playback that can be leveraged to provide a more unique type of message to users. By taking advantage of animations, you can make it clear to users when an operation succeeds; when something fails, you can provide them with additional details.
In one particular client application we were working on, users could update a timesheet with information about what type of work they performed that week. If the operation succeeded, we would animate a DataGrid down to a height of 0 to visually show them that everything worked. Although this technique was well received, some users were confused as to whether the operation worked or not and thought that because the DataGrid disappeared something went wrong. Updating a TextBlock control with a status about the operation would work but we wanted to try something a little different. A simple MessageBox or ChildWindow would work as well, but then the user would have to click to close the box, slowing down the overall application workflow.
We instead decided to animate a visual element that would make it obvious when an operation succeeded or failed but would not get in the way at all. To do this, a Border control is placed above the application and moved out of sight using a TranslateTransform. When an operation is completed, the Border is assigned a green or red gradient and animated down into view along with a status message for about two seconds. After two seconds, the Border is animated back out of sight. Figure 1 shows what the status message looks like in the sample application (which is available with this article's downloadable code). Adding this simple animation into a Silverlight application is extremely easy to do even if you're new to Silverlight development. Figure 2 shows an example of a Border control that is added into the MainPage.xaml code. Notice that the control's CompositeTransform TranslateY value is set to -42 to move it off the screen.
To animate the Border control, you can use Expression Blend to create a storyboard that moves the Border down for a few seconds and then slides it back up. (The storyboard is quite simple in this case and can be manually added into the XAML as well.) The storyboard used in the sample application is shown in Figure 3. It targets the Border control, adjusts the TranslateY property over a span of a few seconds, and then auto reverses to move the control back out of sight.
As an operation is triggered by an end user the storyboard can be started and the Border gradient and status message can be displayed. The code to handle this is shown in Figure 4.
Looking through the code in Figure 4, you may notice that in addition to starting the storyboard, the code also handles playing a MediaElement object. Although not all end users will have speakers available, providing a subtle sound to signify successes or failures can be an effective way to communicate with users that lets them know what happened even if they're not looking at the screen. An example of defining success and failure MediaElement objects using XAML is shown next:
<MediaElement x:Name="SuccessMediaElement" AutoPlay="False"
<MediaElement x:Name="FailureMediaElement" AutoPlay="False"
The wma files referenced above are quite small and compiled into the Silverlight application as resources. That's why you see the /SilverlightDemos;component/\\[wma file name\\] syntax being used in the Source property.
Although the concept of animating a status message and integrating media into a Silverlight application is quite simple, a lot of developers I work with are still inclined to think of interacting with end users in a more traditional way (status labels, message boxes, etc.). Silverlight gives you the power to think outside the box for some parts of an application, which can help engage users more when done right.
Extend Existing Controls
Silverlight 4 comes with a lot of built-in controls that provide excellent out-of-the-box functionality. When combined with the controls available in the Silverlight Toolkit, you have a tremendous number of controls at your disposal. Although many applications can be created using the built-in controls, there will invariably be times when you need custom functionality. In this section I'll discuss how you can take advantage of the flexibility afforded by Silverlight to customize existing controls.
The client for an application my company is currently building in Silverlight 4 ended up needing many ComboBox controls to be nested inside of DataGrid, ListBox, and other controls. Nesting controls and even binding data to nested controls can be handled fairly easily (see my previous article, Lessons Learned Building Real-World Silverlight Applications: Part 1, for details on binding to controls in more complex nesting scenarios). Adding the ability to filter a ComboBox is a bit more challenging, since that type of functionality isn't built into the control. We therefore decided to build an AutoCompleteComboBox control that was based upon the AutoCompleteBox control available in the Silverlight Toolkit.
If you haven't used it before, Silverlight’s AutoCompleteBox control provides filtering capabilities that let a user type text directly into a TextBox. As they type, the text entered is used to select matching items, providing a quick and easy way to filter large sets of data. Figure 5 shows an example of the AutoCompleteBox in action.
Our customers wanted the capability to filter data, but also to be able to click on a down arrow available in the standard ComboBox control. Fortunately, deriving from AutoCompleteBox to create a custom AutoCompleteComboBox control is fairly straightforward. To create this type of control you'll first need to create a new Silverlight class library and add a class named AutoCompleteComboBox into it. Derive the class from AutoCompleteBox as shown below:
public partial class AutoCompleteComboBox : AutoCompleteBox
Once the class is created, add a folder into the project named Themes that contains a file named Generic.xaml. This file is used to define the XAML that the custom control will use to render its interface. Right-click on Generic.xaml, select Properties, and ensure that the Build Action is set to Resource.
Within Generic.xaml you'll need to add XAML that creates the triangle available in the ComboBox control so users can click on it to see all of the available data, rather than just filtered data. The complete XAML that should be added into Generic.xaml is too big to show here (it is available in the article's downloadable source code), but the portion of the code that creates the triangle shape is shown in Figure 6. I took the path from the Generic.xaml file that the Silverlight ComboBox control uses.
Once the Generic.xaml file is completed, a TemplatePart needs to be added into the control's class. We can add a TemplatePart attribute above the class that references the ToggleButton control shown in Figure 6. Template parts are used to reference the different controls defined in the Generic.xaml template. Although TemplatePart’s Name property can be given a direct string value, I followed the convention seen in Silverlight controls and used a constant to define the name value as shown below:
\\[TemplatePart(Name = AutoCompleteComboBox.ElementToggleButton,
Type = typeof(ToggleButton))\\]
public partial class AutoCompleteComboBox : AutoCompleteBox
private const string ElementToggleButton = "ToggleButton";
Next you'll need to add a ToggleButton property into the AutoCompleteComboBox class, as Figure 7 shows. Looking through the code, you'll see that the set block handles attaching or detaching events that determine if the user is moving the mouse over the triangle shape defined in Figure 6, moving the mouse out, or clicking the shape.
Now that the ToggleButton property is defined and ready to handle events, you need to tell the AutoCompleteComboBox control to use the custom Generic.xaml file mentioned earlier. This is done by setting the control's DefaultStyleKey property in the constructor as shown below:
this.DefaultStyleKey = typeof(AutoCompleteComboBox);
Loaded += (sender, e) => ApplyTemplate();
Next, override the control’s OnApplyTemplate method. You need to do this so that the ToggleButton shown in Figure 6 can be injected into the process, letting us handle the user clicking on it to show all of the items like a normal ComboBox would. Figure 8 shows the code that handles this process.
This code accesses the ToggleButton control defined in the Generic.xaml template using the base class’s GetTemplateChild method. Once the ToggleButton control is found, it’s assigned to the ToggleButton property of our control which handles hooking up the Click, MouseEnter, and MouseLeave events. The ToggleButton_Click event handler does the magic of making the AutoCompleteComboBox control look and feel like a standard ComboBox. As the Mouse event handlers are called the code uses the VisualStateManager to scale the ToggleButton and corresponding triangle path to give a nice visual effect. Figure 9 shows what the AutoCompleteComboBox control looks like in action.
Improve End-User Interactions
In this article you've seen how a simple animation can be added into a Silverlight application to enhance end-user interaction, and how time can be saved by deriving from existing Silverlight controls rather than writing one from scratch. There are a lot of additional "real-life" tips and tricks that could be covered, but I hope that the articles in this 2-part series give you some different ideas for your applications.