Take It for a Spin
Create a Custom Spinner Control
October 30, 2009
CoverStory
LANGUAGES:C# | XAML
TECHNOLOGIES:WPF
Take It for a Spin
Create a Custom Spinner Control
By Matthew Hess
In Dial It Up a Notch, we took a look at using WPF ControlTemplatesto turn an existing control into a seemingly brand new control. This techniqueworks nicely when you can find an existing control that encapsulates thebehavior you want. In that case, we took the humble ListBox and morphed it intoa three-way switch.
But what are you supposed to do when you can t find anexisting control that has exactly the behavior you want? Go back to WindowsForms? Not so fast! WPF provides the ability to craft controls that have custominteraction logic, as well as custom presentation. We re going to build onesuch control in this article: a numeric Spinner. In the process, we ll take alook at some important and powerful WPF technologies, such as WPF data binding.
So what is a numeric Spinner control, anyway? It s basicallya text edit with two buttons that lets you set a numeric value either by typingin the text box or clicking the buttons to increment or decrement the value. InWindows Forms and the ASP.NET AJAX toolkit, this is called a numeric UpDown.Those of you who worked in Delphi will remember this asthe TSpinEdit. For nostalgia s sake, I ve chosen to name my custom control inthe Delphi tradition, hence, Spinner.
Considering how popular this kind of control is, one mightreasonably ask why WPF doesn t come with its own Spinner control? This is agood question and I would not be surprised to see this control show up in afuture version of WPF. But for now, the framework doesn t have it, and we needit so let s make it. When you see how easy this is, you won t worry aboutwaiting for that next WPF update.
Step 1: Inheriting from RangeBase
Because WPF ships with an abstract class named RangeBase,which defines much of the logic we need, we don t have to start completely fromscratch to make our custom Spinner. WPF has several controls that inherit fromRangeBase, including Slider, ProgressBar, and, interestingly, ScrollBar. What thesecontrols have in common is that they display a numeric value within a range.Their key properties are Value, Maximum, and Minimum. To make our customnumeric UpDown, we ll descend from RangeBase, give it a ControlTemplate toprovide the visuals, then handle some events to nail down the interactionlogic.
To start with, you need to begin a .NET 3.0 project. I mgoing to use a Windows Application with C# as the procedural language, thoughyou could just as easily compile this into an XML Browser Application (XBAP)and use VB.NET. Once you have your project started, you ll need to add a pairof .XAML and .cs files for the custom control. You can make these files byhand, but starting out as if you were going to make a new UserControl isconvenient: click Add New Item | User Control and name the new item Spinner.This should generate the linked Spinner.XAML and Spinner.XAML.cs files for youto use. Then, working in the XAML, change the UserControl tags to RangeBasetags and remove the default Grid tags, which we won t be using, and, in fact,cannot use (as you ll soon see). Spinner.XAML should now look something likethis:
xmlns="http://schemas.microsoft.com/winfx/2006/ xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Next, go into the Spinner.XAML.cs code-behind file and dotwo things. First, add a line for the namespace that includes RangeBase:
using System.Windows.Controls.Primitives;
and change the type of your new class to RangeBase:
public partial class Spinner : RangeBase
{
// etc...
}
Now your class should compile. We re ready to startdesigning the control.
Step 2: Designing the Visuals
At this point, you might be tempted to start adding somevisual content directly to the RangeBase. But you can t. Try it. Add a Grid ora StackPanel and try to compile your code. It fails because RangeBase is not aContentControl. It has no place for you to add visual content directly.However, RangeBase does have a Template property. So we re going tospecify the Template for our control as a resource in the XAML, then wire it upin the code-behind. This is a key conceptual point of this article. Controlslike RangeBase and its new descendant Spinner have no inherent presentation.They get their visuals from their Template. And in this case, since we remaking a new control, we need to supply the default Template.
A numeric UpDown is basically a text box with two buttonsnext to it for incrementing and decrementing the value. For the Template of ourcontrol, then, we re going to use a grid as the main layout device.Specifically, we ll use a two-by-two grid where the left two cells hold thetext box and the right two cells hold two buttons. For the buttons we ll useRepeatButtons so the user can hold them down and have their value keepchanging. Figure 1 shows the start of our Template.