This article is compatible with the latest version of Silverlight.
Introduction
This articles focuses on the process of creating a Custom Control in Silverlight 2. It describes the basics of what you need to build a styleable control with custom logic that can be used in Silverlight applications. The article shows a sample(part) of implementing a LinkLabel control that recognizes URIs in a given text and displays them as links instead of as plain text.
Overview
The control model in Silverlight 2 offers creating UserControls and Custom Controls. UserControls enable encapsulation of a specific logic. They are used in scenarios where you want to reuse XAML and/or logic in multiple places or build a sophisticated page with smaller chunks(the UserControls). However, in other scenarios you may need a custom logic to be built in the control and this control to allow designers customize easily its look. You need a way to modify the control visuals without having to modify the control logic. This is where the Custom Controls comes in handy. In Silverlight they derive from the Control class and specify a default style for the way they will look like.
To get started create a Silverlight Class Library project in Visual Studio. This will generate just a single .cs file. Lets take a sample implementation of a LinkLabel control.
public class LinkLabel : Control
{
...
}
Ok, the first step is to define a default look for this control. A file called generic.xaml contains the UI of your custom controls. It should be placed in a "themes" directory inside your project. Add a new item of type XML File in your project in the "themes" directory and name it generic.xaml. By default when you add a new file the Build Action is set to Silverlight page. But the platform look for the generic.xaml as a Resource file. Therefore you need to change the default behavior:
- Open the Properties window of the file.
- Change the Build Action to Resource.
- Clear the Custom Tool value.
In the generic.xaml add a ResourceDictionary element where you will put all the resources.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
...
</ResourceDictionary>
The next step is to add the default style for the control. Start by including the XML namespace of the assembly.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CompletIT.Windows.Controls;assembly=LinkLabel">
<!-- Default style for CompletIT.Windows.Controls.LinkLabel -->
<Style TargetType="local:LinkLabel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LinkLabel">
<StackPanel x:Name="LayoutRoot">
<ContentPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In this code snippet you see a Style element just like the one you might have already used in your Silverlight applications. You may also notice that there is no x:Key attribute - you don't need it because when the platform looks for the default style of the control it just examines the TargetType. To specify the control's look set the Template property and add a ControlTemplate. This is where you can define the full appearance of the control.
With the release of the VisualStateManager in Beta 2 it is important to point that this style is also the place where you can define the state parts of your control. This is not the main focus of this article so I won't get deep in it. For more information on this topic you can read Parts & States Model with VisualStateManager and watch Creating Rich, Dynamic User Interfaces with Silverlight 2 by Karen Corby.
Ok, enough of XAML. Lets see what's needed in the code.
Start with creating a constructor and set the FrameworkElement.DefaultStyleKey - type that should be used to look up the built-in style - to the Type of the control.
public LinkLabel()
: base()
{
DefaultStyleKey = typeof( LinkLabel );
}
Then, define all the custom properties of the control. Most of the properties in the framework are of DependencyProperty type. If you want a particular property of your control to be template-bindable, data-bindable or styleable, you need to define it as a DependencyProperty. For the LinkLabel control lets just add a Text property.
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof( string ),
typeof( LinkLabel ),
new PropertyMetadata( new PropertyChangedCallback( OnTextChanged ) ) );
public string Text
{
get
{
return ( string )this.GetValue( TextProperty );
}
set
{
base.SetValue( TextProperty, value );
}
}
As you can see on line 5 a new PropertyChangedCallback is added that will be called when the text is changed. This is not needed for all controls. We will walk through the implementation of the OnTextChanged method in a second. Do the same for all properties your control needs.
One last step before proceeding to the control logic code. The StackPanel we use for a "built-in" style in the generic.xaml is still not accessible from the code file. To assign it to a member variable create a private field of type StackPanel and override the OnApplyTemplate method like this:
public override void OnApplyTemplate()
{
this.layoutRoot = this.GetTemplateChild( "LayoutRoot" ) as StackPanel;
Debug.Assert( this.layoutRoot != null, "LayoutRoot is null" );
base.OnApplyTemplate();
}
As you see in the above code we call this.GetTemplateChild(childName) to retrieve the element from the visual tree.
That's it. From there on you should write the custom logic needed by your control to act as you want. For our LinkLabel sample we need to process the entered text by replacing all URIs with links. To achieve this we need to define a new method (ProcessText()) that should be called (1) from the OnApplyTemplate event and (2) from the OnTextChanged event. I'll skip the implementation of the ProcessText method here as I'll blog about the full implementation of the LinkLabel control later this week and will provide the source code and a sample usage.
private static void OnTextChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
LinkLabel linkLabel = d as LinkLabel;
Debug.Assert( linkLabel != null, "LaytouRoot is null" );
linkLabel.ProcessText();
}
Conclusion
Building a Custom Control in Silverlight can be tricky sometimes, but that really depends on the complexity the control requires. Silverlight really gives the freedom to create whatever your imagination can born and push it to the highest level. You are able to build beautiful controls that can be easily customized by designers on their own. You are sure that the designer won't brake your logic while making the fascinate look of the control. With the new VisualStateManager and the "Parts and States Model" you can easily define complex UI while keeping the designer's ability to style it. This is another quite a big and interesting topic that I'll try to cover in future posts.
Stay tuned to see the LinkLabel control. I'll provide a link in this article too.
Update
Check out the LinkLabel control here.
References
Styling and Templating Overview on MSDN
Creating a Templatable Control on MSDN
Creating Rich, Dynamic User Interfaces with Silverlight 2 by Karen Corby
Parts & States Model with VisualStateManager by Karen Corby