Many of you may have noticed that Microsoft did not include a DropDownList in the Beta 2 of Silverlight. So I thought I would take a stab at it.

1. The Parts Contract

The first thing that you need to think about when creating a custom control is what are the visual elements that you need to expose to people that are going to reskin your control. This is called the “Parts Contract”. The parts contract consists of specific names and types that must be defined with in the templates of your control in order for it to continue to work properly.

In this case, the DropDownList, I identified two main logical components:

  1. The Selection Indicator
  2. The Drop Down List

The visual tree for each of these parts could be very diverse but as long as these parts are exposed we know that our control will operate as expected.

In my parts contract I wanted to give clients of my custom control access to not only the logical elements but also the containing elements. This will further refine the isolation of behavior and visual between different Parts Elements.

Therefore, I will define the following parts elements:

  1. RootElement
  2. SelectionContentContainer
  3. SelectionContent
  4. ItemsContainer
  5. ItemsListBox

Within the control logic of my custom control each of these parts will play a specific role in the control’s appearance & behavior.

2. Inherit from Control

In this case I am inheriting from ListBox, but that is because I want to leverage some of the existing properties, events, and methods within this class. Many custom controls will inherit from Control, ItemsControl, or the like. As long as you do not inherit from UserControl your control will be able to have new templates applied to it.

public class DropDownList : ListBox

{

...

}

3. Define Default Visuals

Now that I have an idea of how clients will interact with my control I need to define the default visuals that my control will use out of the box. This will give developers a starting place and a reference for templating my control.

In Silverlight, all default visuals are defined in a resource file called “generic.xaml”. This file needs to have a Build Action of “Resource”.

<ResourceDictionary

xmlns="http://schemas.microsoft.com/client/2007"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="clr-namespace:Avanade.Silverlight.Controls;assembly=Avanade.Silverlight.Controls"

>
<Style TargetType="local:DropDownList">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="local:DropDownList">

It is important to note that when defining the local namespace we need to include the assembly information. WPF developers will notice that the {x:Type …} markup expression is not supported in Silverlight.

4. Define Control Logic

This is where all the magic happens. A Control goes through several stages before it is finally rendered to the screen. The stage that we are particularly interested in is when a new template is applied. During this stage we are able to access all of the Parts that we are expecting from our template and wire them up with the logic that will make everything work.

By overriding OnApplyTemplate we can wire up the control logic that will make our DropDownList act as expected. The GetTemplateChild method will return the DependencyObject with the given name or null if it can’t be found.

protected override void OnApplyTemplate() {

base.OnApplyTemplate();

layoutRootElement = this.GetTemplateChild("RootElement") as FrameworkElement;

selectionContainerElement = this.GetTemplateChild("SelectionContentContainer") as FrameworkElement;

selectionContent = this.GetTemplateChild("SelectionContent") as FrameworkElement;

itemsContainer = this.GetTemplateChild("ItemsContainer") as FrameworkElement;

itemsListBox = this.GetTemplateChild("ItemsListBox") as ListBox;

itemsListBox.SelectionChanged += new SelectionChangedEventHandler(itemsListBox_SelectionChanged);

IsItemsContainerVisible = false;

// Update the margin based on the height of the control

double offset = itemsContainer.Height;

itemsContainer.Margin = new Thickness(0, 0, 0, -offset);

itemsListBox.ItemsSource = ItemsSource;

selectionContainerElement.MouseLeftButtonDown += new MouseButtonEventHandler(selectionContainerElement_MouseLeftButtonDown);

}

5. Done!

That’s it! Not so bad, eh? No the fun is just begining. If you did it right, you should be able to have all the flexability that we enjoy with the wonderful control library that Microsoft has provided us in Beta 1 with our very own concoctions.

Who knows, maybe you will see my DropDownList control in Beta 2 :-)

Update: I designed a new skin for my control. See below…