Click to Rate and Give Feedback
Related Articles

This article reviews the Prism project developed by the Microsoft patterns & practices group and demonstrates how to apply it to composite Web applications using Silverlight.

Shawn Wildermuth

MSDN Magazine July 2009

...

Read more!

This article describes how to use XHTML and ASP.NET MVC to implement REST services.

Aaron Skonnard

MSDN Magazine July 2009

...

Read more!

In this column, the author lays out some guiding principles that you should follow when working with the ASP.NET MVC framework.

Matt Ellis

MSDN Magazine July 2009

...

Read more!

We demonstrate creating a peer-to-peer processing platform where multiple players function together for a common purpose: getting your work done.

Matt Neely

MSDN Magazine June 2009

...

Read more!

In this article, we cover some of the best practices for assembly binding and loading using the CLR.

Aarthi Ramamurthy and Mark Miller

MSDN Magazine May 2009

...

Read more!

Also by this Author

ASP.NET has become a bit of a gold standard for Web programming. The upcoming version, ASP.NET 2.0 will have even more of the kinds of features that have made it the popular framework it is today. This article takes a broad look at those features, including what's new in data source controls, themes and skins, the DataGrid and its new functionality, managing roles, and other administrative tasks.

Jeff Prosise

MSDN Magazine June 2004

...

Read more!

With the help of Silverlight Deep Zoom and a remarkable control named MultiScaleImage, you can create scenes with many levels of zoom. Jeff Prosise illustrates with what else but the Mandlebrot set.

Jeff Prosise

MSDN Magazine July 2009

...

Read more!

: Jeff Prosise presents great tips for Silverlight development, which while it's gaining wide adoption, still needs more documentation and best practices so developers can make the most of the dazzling new features.

Jeff Prosise

MSDN Magazine Launch 2008

...

Read more!

Data-driven site navigation is among the niftiest and most useful features in ASP. NET 2. 0. To get it working, all you do is create an XML site map file (or a SQL site map if you're using the MSDN®Magazine SqlSiteMapProvider), add a SiteMapDataSource, and bind a TreeView or Menu to the SiteMapDataSource.

Jeff Prosise

MSDN Magazine June 2006

...

Read more!

As a Web platform, Silverlight should be fast. Don’t keep your users waiting by not heeding these performance tips.

Jeff Prosise

MSDN Magazine March 2009

...

Read more!

Popular Articles

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

Here we introduce you to some of the concepts behind the new F# language, which combines elements of functional and object-oriented .NET languages. We then help you get started writing some simple programs.

Ted Neward

MSDN Magazine Launch 2008

...

Read more!

See how routed events and routed commands in Windows Presentation Foundation form the basis for communication between the parts of your UI.

Brian Noyes

MSDN Magazine September 2008

...

Read more!

This article introduces 10 development tools that can increase your productivity, give you a better understanding of .NET, and maybe even change the way that you develop applications. The tools covered include NUnit to write unit tests, Reflector to examine assemblies, FxCop to police your code, Regulator to build regular expressions, NDoc to create code documentation and five more.

James Avery

MSDN Magazine July 2004

...

Read more!

WPF is one of the most important new technologies in the .NET Framework 3.0. This month John Papa introduces its data binding capabilities.

John Papa

MSDN Magazine December 2007

...

Read more!

Wicked Code
Craft Custom Controls for Silverlight 2
Jeff Prosise

Code download available at: WickedCode2008_08.exe (585 KB)
Browse the Code Online
This article discusses:
  • The WPF control model
  • Creating control templates
  • Deriving controls
  • Adding events
This article uses the following technologies:
Silverlight 2
This article is based on the Beta 2 version of Silverlight 2. All information herein is subject to change.
One of the many features that distinguishes Silverlight™ 2 from Silverlight 1.0 is support for controls. Silverlight 2 features a rich and robust control model that is the basis for the controls included in the platform and for third-party control packages. You can also use this control model to build controls of your own. But for developers unfamiliar with the Windows® Presentation Foundation (WPF) control model, building that first Silverlight custom control can be a daunting experience. At the time of this writing—just before the release of Silverlight 2 Beta 2—there was precious little documentation available, and a quick Web search turned up few tutorials to show the way. While I'm on that topic, it's a good time to note that although I am using Beta 2, there will likely be further changes before the final release.
When learning to write custom controls for a new platform, I often begin by trying to duplicate some of the built-in controls: buttons, listboxes, and so on. They may seem simple on the outside, but controls such as these invariably expose key features of the control model and test one's knowledge of the same. Plus, you can't build a super-duper-multicolor-multithreaded-all-in-one-do-it-all widget control if you can't first build a simple push button.
The best way to learn about Silverlight 2 custom controls is to build one—step by step, piece by piece. You not only learn about the parts that make up a control, but how the parts fit together. The following tutorial describes how to build a SimpleButton control that duplicates important aspects of the built-in Button control's look and behavior and provides a helpful first look at control development, Silverlight style.

Step 1: Create a New Silverlight Project
The first step in creating a custom control is to fire up Visual Studio® 2008 (make sure you've installed the Silverlight add-in for Visual Studio so you can create Silverlight projects) and create a project. Normally you'd create a Silverlight class library project so the control can be compiled into its own assembly and added as a reference to projects that will use it. I'll take a slightly different path and create a Silverlight application project so the control can be built and used from within the same project. Therefore, let's begin by creating a new Silverlight application project named SimpleButton­Demo, as shown in Figure 1. Answer "yes" when Visual Studio offers to create a Web project to go with the Silverlight project.
Figure 1 Creating the SimpleButtonDemo Project (Click the image for a larger view)

Step 2: Derive from Control (or ContentControl)
The next step is to add a C# class representing the control. At a minimum, the control class should derive from the Silverlight System.Windows.Controls.Control class in order to inherit basic control functionality. However, it can also derive from Control derivatives such as ContentControl and ItemsControl. Many of the built-in controls derive directly or indirectly from ContentControl, which adds a Content property that allows the control's content—for example, the content on the face of a push button—to be customized. The ListBox control, in contrast, derives from ItemsControl, which implements the basic behavior of a control that presents a collection of items to the user. We'll derive from ContentControl since we're implementing a button.
Use the Visual Studio Add New Item command to add a new C# class to the SimpleButtonDemo project. Name the file Simple­Button.cs. Then open SimpleButton.cs and modify the Simple­Button class so that it derives from ContentControl:
namespace SimpleButtonDemo
{
    public class SimpleButton : ContentControl
    {
    }
}
At this point, you've implemented a bare-bones custom control—one that can be instantiated with a declaration in a XAML document. To demonstrate, add the following statement to Page.xaml:
<custom:SimpleButton />
In order for Silverlight to make sense of this declaration, you also need to add the following attribute to Page.xaml's root User­Control element:
xmlns:custom="clr-namespace:SimpleButtonDemo; assembly=SimpleButtonDemo"
As you can see here, clr-namespace identifies the namespace in which the SimpleButton class is defined, and assembly identifies the assembly containing the control. In this example, the control assembly and the application assembly are one and the same. If Simple­Button were implemented in a separate assembly named MyControls.dll, you'd set assembly equal to "MyControls" instead. The code in Figure 2 shows the contents of Page.xaml after these modifications are made. Incidentally, you don't have to use custom as the prefix for custom controls; you could just as easily use foo or your company name instead.
<UserControl x:Class="SimpleButtonDemo.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo"
    Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">
      <custom:SimpleButton />
    </Grid>

</UserControl>
You can see what your efforts have wrought so far by launching either of the test pages (SimpleButtonDemoTestPage.aspx or Simple­ButtonDemoTestPage.html) within the Simple­ButtonDemo_Web project that Visual Studio added to the solution. Figure 3 shows how SimpleButtonDemoTestPage.html looks in the browser. Granted, there's not much to write home about just yet, but that will change in the next step.
Figure 3 Presenting the SimpleButton Control (Click the image for a larger view)

Step 3: Create a Control Template
The reason you found yourself staring at a blank browser window in the previous step is that even though SimpleButton was instantiated, it didn't render a UI. You can remedy that by modifying the SimpleButton declaration in Page.xaml to include a control template. The code in Figure 4 shows the modified control declaration.
<custom:SimpleButton>
  <custom:SimpleButton.Template>
    <ControlTemplate>
      <Grid x:Name="RootElement">
        <Rectangle x:Name="BodyElement" Width="200" Height="100"
          Fill="Lavender" Stroke="Purple" RadiusX="16" RadiusY="16" />
        <TextBlock Text="Click Me" HorizontalAlignment="Center"
          VerticalAlignment="Center" />
      </Grid>
    </ControlTemplate>
  </custom:SimpleButton.Template>
</custom:SimpleButton>
The declaration now initializes the control's Template property, which defines the control's visual tree, to include a Rectangle and a TextBlock positioned inside a 1-row, 1-column Grid. Launch SimpleButtonDemoTestPage.html in your browser again and the output will look very different (see Figure 5). SimpleButton now has a face!
Figure 5 The SimpleButton Control (Click the image for a larger view)

Step 4: Create a Default Control Template
It's unreasonable to require developers using your control to define their own control templates. A custom control should have a default template so even a simple declaration like the one in Figure 2 will display something on the page. Providing a default template doesn't preclude someone from overriding it with a template like the one in Figure 4, but it does make your control a lot friendlier by removing the requirement that a template be provided.
The mechanism used to define a default template for a custom control is borrowed from WPF. You begin by adding a file named Generic.xaml to the control project. (Yes, it has to be named Generic.xaml. Case doesn't matter, but the name itself does.) Then you define a style in Generic.xaml that uses a property setter to assign a value to the control's Template property. The Silverlight runtime automatically looks for Generic.xaml in the control assembly (where it is embedded as a resource) and applies the style to control instances. In addition to defining a default template, the style also can assign default values to other control properties such as Width and Height.
To see for yourself, use the Visual Studio Add New Item command to add a text file named Generic.xaml to the SimpleButtonDemo project. Then replace the contents of Generic.xaml with the code in Figure 6. Generic.xaml now includes an unnamed style that is applied to all instances of SimpleButton (note the TargetType attribute). That style includes a default value for SimpleButton's Template property that is identical to the Template value explicitly assigned to the control in Figure 5.
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement" Width="200" Height="100"
              Fill="Lavender" Stroke="Purple" RadiusX="16" RadiusY="16" />
            <TextBlock Text="Click Me" HorizontalAlignment="Center"
              VerticalAlignment="Center" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
With a default template in place, go back to SimpleButton.cs and add the following statement to the class constructor:
this.DefaultStyleKey = typeof(SimpleButton);
Then open Page.xaml and modify the control declaration so that it looks again like this:
<custom:SimpleButton />
Launch the test page in your browser and the control should look exactly as it did before. But this time, obtaining that look was much simpler.

Step 5: Add Template Bindings
One problem with SimpleButton as it exists right now is that property values assigned to the control aren't honored by the control template. In other words, if the control were declared as follows, it would still have a width of 200 and height of 100 because these values are hardcoded into the control template:
<custom:SimpleButton Width="250" Height="150" />
One of the most important features of Silverlight 2 from a control developer's perspective is template binding. Template bindings allow property values assigned to a control to flow down into the control template and are declared in XAML using the {TemplateBinding} markup extension. Rather than define the Width and Height properties of the Rectangle that forms the body of Simple­Button using hardcoded values like this:
Width="200" Height="100"
You should define them this way instead:
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
Now the width and height assigned to the control will also be the width and height assigned to the Rectangle.
Figure 7 shows a revised version of Generic.xaml that assigns default values to the Width, Height, and Background properties inherited from the base class, and that uses template bindings to reference these property values in the control template.
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <TextBlock Text="Click Me"
              HorizontalAlignment="Center"
              VerticalAlignment="Center" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
Test the modified control template by editing the control declaration in Page.xaml as follows:
<custom:SimpleButton Width="250" Height="150" Background="Yellow" />
The output is illustrated in Figure 8. The TemplateBindings are a huge, important step in the right direction because now you have instances of SimpleButton honoring the property values that you assign to them.
Figure 8 The Improved SimpleButton Control

Step 6: Replace TextBlock with ContentPresenter
The fact that SimpleButton derives from ContentControl means it has a Content property that developers can use to customize the content on the face of the button—at least in theory. The built-in Button control can be customized with the XAML in Figure 9 to produce the look shown in Figure 10.
<Button Width="250" Height="150">
  <Button.Content>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Ellipse Width="75" Height="75" Margin="10">
        <Ellipse.Fill>
          <RadialGradientBrush GradientOrigin="0.25,0.25">
            <GradientStop Offset="0.25" Color="White" />
            <GradientStop Offset="1.0" Color="Red" />
          </RadialGradientBrush>
        </Ellipse.Fill>
      </Ellipse>
      <TextBlock Text="Click Me" VerticalAlignment="Center" />
    </StackPanel>
  </Button.Content>
</Button>
Figure 10 Button with Customized Content
But try the same with SimpleButton and you'll quickly find that the content is still simple text. In fact, you can't even set Content="Test" to change the button text because the control template currently contains a hardcoded TextBlock with hardcoded text.
You can fix this deficiency by replacing the TextBlock in Simple­Button's default control template with a ContentPresenter, as shown in Figure 11. A TextBlock can only render text, but a ContentPresenter can render any XAML assigned to the control's Content property. With these changes in place, the XAML SimpleButton declaration in Figure 12 produces the output in Figure 13. SimpleButton now supports two levels of customization. Its entire visual tree can be redefined with a custom template, or just its content can be redefined using the Content property. Moreover, you can change the button text with a simple Content attribute. SimpleButton is beginning to behave more and more like a real button control.
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="FontSize" Value="11" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid x:Name="RootElement">
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <ContentPresenter Content="{TemplateBinding Content}"
              HorizontalAlignment="Center" VerticalAlignment="Center"
              FontSize="{TemplateBinding FontSize}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
<custom:SimpleButton Width="250" Height="150">
  <custom:SimpleButton.Content>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Ellipse Width="75" Height="75" Margin="10">
        <Ellipse.Fill>
          <RadialGradientBrush GradientOrigin="0.25,0.25">
            <GradientStop Offset="0.25" Color="White" />
            <GradientStop Offset="1.0" Color="Red" />
          </RadialGradientBrush>
        </Ellipse.Fill>
      </Ellipse>
      <TextBlock Text="Click Me" VerticalAlignment="Center" />
    </StackPanel>
  </custom:SimpleButton.Content>
</custom:SimpleButton>
Figure 13 SimpleButton with Customized Content

Step 7: Add a Click Event
The next step in bringing SimpleButton to life is to have it fire Click events. Implementing events in Silverlight controls is generally no different than implementing events in ordinary Microsoft® .NET Framework classes: you simply declare the events in the control class and then write code to fire them.
One aspect of event firing that is slightly different in Silverlight, however, is the fact that Silverlight supports routed events. In WPF, event routing has a richer connotation in that events can travel up and down the visual tree; in Silverlight, events only travel up, an action known as "bubbling." Routed events are defined by the RoutedEventHandler delegate, and routed event handlers receive a RoutedEventArgs object that contains a Source property identifying the object that fired the event (which will be different from the sender parameter passed to the event handler if the event was originally fired by an object deeper in the visual tree). The built-in Button control's Click event is a routed event, so SimpleButton's should be, too.
Figure 14 shows the modifications you must make to the control class in SimpleButton.cs to fire routed Click events. Simple­Button's constructor now registers a handler for MouseLeftButtonUp events. The handler fires a Click event provided there is at least one registered listener. Traditional button controls only fire Click events if button-up events are preceded by button-down events. That logic was omitted from SimpleButton to keep the source code as simple as possible.
public class SimpleButton : ContentControl
{
    public event RoutedEventHandler Click;

    public SimpleButton()
    {
        this.DefaultStyleKey = typeof(SimpleButton);
        this.MouseLeftButtonUp += new MouseButtonEventHandler
            (SimpleButton_MouseLeftButtonUp);
    }

    void SimpleButton_MouseLeftButtonUp(object sender,
        MouseButtonEventArgs e)
    {
        if (Click != null)
            Click(this, new RoutedEventArgs());
    }
}
To test Click events, you can modify the control declaration in Page.xaml as follows:
<custom:SimpleButton Content="Click Me" Click="OnClick" />
Then add the event handler shown next to Page.xaml.cs. Clicking the SimpleButton should now produce an alert box containing the word "Click!"—proof positive that the event was fired and handled correctly:
protected void OnClick(Object sender, RoutedEventArgs e)
{
    System.Windows.Browser.HtmlPage.Window.Alert("Click!");
}

Step 8: Add Visual States
Two key components of Silverlight controls are visual states and visual state transitions. Visual states define a control's appearance in various states: how it looks when it's pressed, when the mouse enters it, when it's disabled, and so on. Visual state transitions define how a control transitions from one visual state to another: for example, from the "normal" state to the "pressed" state.
Silverlight 2 Beta 2 introduced a new component known as the Visual State Manager (VSM) to simplify states and state transitions and to facilitate better tool support. With the VSM, you use VisualState objects encapsulating Storyboards to define states, and Visual­Transition objects to define transitions. Then you use the Visual­StateManager class's static GoToState method to transition the control to the specified state in response to user stimuli.
Figure 15 shows how to modify Generic.xaml to leverage the VSM. SimpleButton defines two states: Normal and MouseOver. These are declared with <vsm:VisualState> elements. The Normal state doesn't require a Storyboard because it represents the control in its default state. The MouseOver state uses a ColorAnimation to change the control's background color—actually, the Fill property of the rectangle representing the body of the control—to pink. The ColorAnimation's Duration property is 0 because the length of the animation is driven by the Duration property of the corresponding VisualTransition. Note the xmlns:vsm attribute added to the <ResourceDictionary> element to enable declarative use of VisualStateManager.
<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo"
  xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
  <Style TargetType="custom:SimpleButton">
    <Setter Property="Width" Value="200" />
    <Setter Property="Height" Value="100" />
    <Setter Property="Background" Value="Lavender" />
    <Setter Property="FontSize" Value="11" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="custom:SimpleButton">
          <Grid>
            <vsm:VisualStateManager.VisualStateGroups>
              <vsm:VisualStateGroup x:Name="CommonStates">
                <vsm:VisualStateGroup.Transitions>
                  <vsm:VisualTransition To="Normal" Duration="0:0:0.2"/>
                  <vsm:VisualTransition To="MouseOver" Duration="0:0:0.2"/>
                </vsm:VisualStateGroup.Transitions>
                <vsm:VisualState x:Name="Normal" />
                <vsm:VisualState x:Name="MouseOver">
                  <Storyboard>
                    <ColorAnimation Storyboard.TargetName="BodyElement"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="Pink" Duration="0" />
                  </Storyboard>
                </vsm:VisualState>
              </vsm:VisualStateGroup>
            </vsm:VisualStateManager.VisualStateGroups>
            <Rectangle x:Name="BodyElement"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}"
              Fill="{TemplateBinding Background}"
              Stroke="Purple" RadiusX="16" RadiusY="16" />
            <ContentPresenter Content="{TemplateBinding Content}"
              HorizontalAlignment="Center" VerticalAlignment="Center"
              FontSize="{TemplateBinding FontSize}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
Figure 16 shows the completed SimpleButton class, now modified to use the states and transitions declared in Generic.xaml. The class itself is also decorated with [TemplateVisualState] attributes indicating which visual states the control supports. (These attributes aren't required, but they do enable tools such as Expression Blend™ to provide a richer design-time experience.) The class constructor wires MouseEnter and MouseLeave events to a pair of handlers, and the handlers transition the control state utilizing VisualStateManager.GoToState. Now the control turns pink when the mouse enters and goes back to its original color when the mouse leaves.
[TemplateVisualState(Name = "Normal", GroupName = "GroupCommon")]
[TemplateVisualState(Name = "StateMouseOver", GroupName = "GroupCommon")]
public class SimpleButton : ContentControl
{
    public event RoutedEventHandler Click;

    public SimpleButton()
    {
        DefaultStyleKey = typeof(SimpleButton);
        this.MouseLeftButtonUp +=
            new MouseButtonEventHandler(SimpleButton_MouseLeftButtonUp);
        this.MouseEnter +=
            new MouseEventHandler(SimpleButton_MouseEnter);
        this.MouseLeave +=
            new MouseEventHandler(SimpleButton_MouseLeave);
    }

    void SimpleButton_MouseLeftButtonUp(object sender,
        MouseButtonEventArgs e)
    {
        if (Click != null)
            Click(this, new RoutedEventArgs());
    }

    void SimpleButton_MouseEnter(object sender, MouseEventArgs e)
    {
        VisualStateManager.GoToState(this, "MouseOver", true);
    }

    void SimpleButton_MouseLeave(object sender, MouseEventArgs e)
    {
        VisualStateManager.GoToState(this, "Normal", true);
    }
}

The Completed Control
The finished source code for SimpleButton is included with the download that accompanies this article. You could refine the control even further by implementing additional properties and state animations and perhaps sprucing up the UI with a glassed appearance. Nonetheless, SimpleButton contains all the key elements of a Silverlight control, and if you can embrace what you've read here, you should have no problem building controls of your own.

Jeff Prosise is a contributing editor to MSDN Magazine and the author of several books, including Programming Microsoft .NET. He's also cofounder of Wintellect (wintellect.com), a software consulting and education firm that specializes in the .NET Framework. Have a comment on this column? Contact Jeff at wicked@microsoft.com.

Page view tracker