Page view counter

Digging Into Custom Controls

Follow up posted at bottom of article Last night’s post was something of a preface, but let’s get started.

[ For those of you who crave the details, the code, the feel of bits between your fingers, watch for a series of videos on this subject to be released in the next couple weeks with source in VB and C# ]

As I started to say last night, the key distinction in writing custom controls in Silverlight as opposed to other GUI environments is the strict division between logic and visuals embodied in the Parts and States Model.

As an aside, this is where we always point out that there is nothing in Silverlight that requires or enforces that you implement your custom control using the Parts and States model, but it is the model recommended by Microsoft, and it is the model understood and supported by Expression Blend. The fact is, I can’t imagine creating a custom control that does not conform to the P&S model except to show that it can be done.

A Brief Introduction to the Parts and States Model

The key concept behind the P&S model is that your control will have a strict separation of logic from visuals, and the visuals will be managed by the Visual State Manager which will need to know (a) what States might the control be in (states are defined in just a moment) and (b) what parts of the control might be under VSM control.

States are familiar to those who’ve worked with Templates, and in truth, if you haven’t you want to stop right here and go do that.  I posted three videos on styles and templates that will get you started as well as a few useful blog entries 

From a P&S model perspective, a control is either in a state or transitioning from one state to another.  The Visual State Manager is responsible for running the storyboard associated with your control being in a given state (such as MouseOver).

If you are templating an existing control, the states have been enumerated already, you can’t add new states unless you create a custom control. More on that in a moment

Parts

Controls are of course made up of many parts (little p) but from a P&S perspective they aren’t considered Parts unless they will be called by methods of the control itself.

For example, the ScrollBar is a control available in the Silverlight toolbox. From the P&S view point it can be decomposed into four Parts.

  1. · Down Repeat Button
  2. · Up Repeat Button
  3. · Scroll Bar
  4. · Thumb

ScrollBar

While there may be other elements in a Scrollbar, these are the Parts, because these elements are the only elements that other elements of the Scrollbar must address directly.

Many controls, for that support the P&S model, such as Button, have no parts at all (!)

Creating the Contract

When you create a custom control in Silverlight you create a “contract” stating “this part is under the domain of the VSM” and the rest is considered logic that is on the “other side of the wall.”

Attributes are a mechanism to store metadata within a .NET program.  You can see an example in this excerpt from a Ratings control, which can be “lit” or not depending on the user’s action.  (For more on attributes see any good book on C# or VB )

   1: [TemplatePart( Name = "Core", Type = typeof( FrameworkElement ) )]
   2:  
   3: [TemplateVisualState( Name = "Normal", GroupName = "CommonStates" )]
   4: [TemplateVisualState( Name = "MouseOver", GroupName = "CommonStates" )]
   5: [TemplateVisualState( Name = "Pressed", GroupName = "CommonStates" )]
   6:  
   7: [TemplateVisualState( Name = "Lit", GroupName = "RatingStates" )]
   8: [TemplateVisualState( Name = "Norm", GroupName = "RatingStates" )]
   9:  
  10: public class RatingControl : Control

 

This snippet shows six attributes being added to a new Custom Control. The first is the only “Part” named “Core” (stolen directly from Karen Corby).  The next three are the three “common states” this new control will support. Notice that they share the GroupName of “CommonStates”.  Finally, on lines 7 and 8 are the two RatingStates of Lit and Norm. 

The Contract Divides Logic from Visuals

These few lines draw a powerful contract that the developer and designers can rely on, as can Expression Blend. They state clearly that the “Core” object (to be created in Xaml) will be under the management of internal methods as a Part,  that the new control will have two state groups, and it enumerates the states within each group.

Further, the class definition shows that our new control derives from the base class Control.

Implementing the Contract

It is up to me now to implement the contract. The steps to getting here were:

  1. Create a Silverlight Application and choose Project Type Web Site
  2. Right click on the solution and Add New Project of type Silverlight Class Library
  3. Add a new class to the Class Library (I named it Rating) which generates Rating.cs
  4. Throw away Class1.cs which was created for you
  5. Right click on the Class Library Project and choose Add->New Item. Pick the Silverlight User Control template and name it generic.xaml. It must have that name (!)
  6. Throw away generic.xaml.cs

Here’s where we are

  • Rating.cs will contain the code for your custom control, along with the meta-data to create the contract for the Parts and States model
  • generic.xaml will contain the default appearance of your control (in Xaml)
  • Once your control is created you will make an instance of it in the Page.xaml of the project you created back in step 1 above.
   1: <UserControl x:Class="BookRater1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:Controls="clr-namespace:ClassLibrary;assembly=ClassLibrary"
   5:     xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
   6:     Width="600" Height="400">
   7:  
   8:     <Grid x:Name="LayoutRoot" Background="White">
   9:         <Grid.RowDefinitions>
  10:             <RowDefinition Height=".5*" />
  11:             <RowDefinition Height=".5*" />
  12:         </Grid.RowDefinitions>
  13:         <Grid.ColumnDefinitions>
  14:             <ColumnDefinition Width=".5*" />
  15:             <ColumnDefinition Width=".5*" />
  16:         </Grid.ColumnDefinitions>
  17:         
  18:         <Controls:RatingControl x:Name="Rating1"  Grid.Row="0" Grid.Column="0" />
  19:         <Controls:RatingControl x:Name="Rating2"  Grid.Row="1" Grid.Column="0" Template="{StaticResource RatingControlControlTemplate1}"   />
  20:     </Grid>
  21: </UserControl>

A Few Things To Notice

Remember that this is a view of Page.xaml – the page that is using the custom control.

  • On line 4 you set up the namespace for the class library.
  • On lines 7 and 8 you create two instances of the custom control, the second of which overrides the default appearance by using a template, just as you might do with any other control (we’ve not seen the creation of that template yet)

What is in Rating.cs and generic.xaml?

generic.xaml

   1: <ResourceDictionary
   2:    xmlns=  -- Many of these -->
   3:   <Style TargetType="controls:RatingControl">
   4:     <Setter Property="Template">
   5:       <Setter.Value>
   6:         <ControlTemplate TargetType="controls:RatingControl">
   7:           <Grid x:Name="LayoutRoot">
   8:             <Grid.Resources>
   9:               <Storyboard x:Key="UnLight" >
  10:                 <DoubleAnimation 
  11:                    Storyboard.TargetName="Core" 
  12:                    Storyboard.TargetProperty="(UIElement.Opacity)" 
  13:                    Duration="0:0:0.01" From="1" To=".5"/>
  14:               </Storyboard>
  15:               <Storyboard x:Key="Light" >
  16:                 <!-- -->
  17:               </Storyboard>
  18:               <Storyboard x:Key="Bounce" RepeatBehavior="forever" >
  19:                 <DoubleAnimationUsingKeyFrames 
  20:                     BeginTime="00:00:00" 
  21:                     Duration="00:00:01" 
  22:                     Storyboard.TargetName="Core" 
  23:                     Storyboard.TargetProperty="(UIElement.RenderTransform).
  24:                     (TransformGroup.Children)[3].(TranslateTransform.Y)">
  25:                         <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
  26:                         <SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="25"/>
  27:                         <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
  28:                         <SplineDoubleKeyFrame KeyTime="00:00:00.75" Value="50"/>
  29:                         <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
  30:                 </DoubleAnimationUsingKeyFrames>
  31:               </Storyboard>
  32:               <Storyboard x:Key="Dip" >
  33:                 <!-- -->
  34:               </Storyboard>
  35:             </Grid.Resources>
  36:             <vsm:VisualStateManager.VisualStateGroups>
  37:               <vsm:VisualStateGroup x:Name="CommonStates">
  38:                 <vsm:VisualState x:Name="Normal" />
  39:                 <vsm:VisualState x:Name="MouseOver" 
  40:                     Storyboard="{StaticResource Bounce}"/>
  41:                 <vsm:VisualState x:Name="Pressed" 
  42:                     Storyboard="{StaticResource Dip}"/>
  43:               </vsm:VisualStateGroup>
  44:               <vsm:VisualStateGroup x:Name="RatingStates">
  45:                 <vsm:VisualState  x:Name="Norm" 
  46:                     Storyboard="{StaticResource UnLight}" />
  47:                 <vsm:VisualState x:Name="Lit" 
  48:                     Storyboard="{StaticResource Light}" />
  49:               </vsm:VisualStateGroup>
  50:             </vsm:VisualStateManager.VisualStateGroups>
  51:             <Ellipse 
  52:                 x:Name="Core" 
  53:                 Width="200" 
  54:                 Height="200" RenderTransformOrigin="0.5,0.5" >
  55:               <Ellipse.RenderTransform>
  56:                 <TransformGroup>
  57:                   <ScaleTransform/>
  58:                   <SkewTransform/>
  59:                   <RotateTransform/>
  60:                   <TranslateTransform/>
  61:                 </TransformGroup>
  62:               </Ellipse.RenderTransform>
  63:               <Ellipse.Fill>
  64:                 <RadialGradientBrush>
  65:                   <GradientStop Color="#FFFFD954" Offset="0.004"/>
  66:                   <GradientStop Color="#FFE9F515" Offset="1"/>
  67:                   <GradientStop Color="#FFF1F712" Offset="0.911"/>
  68:                 </RadialGradientBrush>
  69:               </Ellipse.Fill>
  70:             </Ellipse>
  71:           </Grid>
  72:         </ControlTemplate>
  73:       </Setter.Value>
  74:     </Setter>
  75:   </Style>
  76: </ResourceDictionary>

This file has been cut down, but you can see that it looks very much like a standard template file. We begin the substantive work on line 8 creating a Resources sections. In here we create a Storyboard for each of the behaviors we might want in a given state. That is, if we have decided that the behavior when we hover over the custom control will be for it to bounce up and down hyperactively  we would create the storyboard for that here in the resources area (as we do on lines 18-31).

After the Resources (line 35) we define the Visual State Groups (lines 36-50) and within each of the groups, the visual states.  The job here is to assign the appropriate story board to each of the states.

Finally, on line 51 we create our custom control’s default appearance, including the named Part, “Core” which is the ellipse defined on lines 51 to  70.  In this simplified example that happens to be the only object in the control, but more complex controls may have many unnamed elements as well.

Rating.cs

The code file for our class defines both the logic and the enabling (private) code for the translation of CLR events to states that the VSM will recognize.  It is also here that we apply either the default look (generic.xaml) or the templated look that was requested when the control was instantiated in page.xaml. This is done, essentially by calling firing the base class’s OnApplyTemplate event.

We extract the named part from the Xaml and hold onto it in a member variable, as we’ll use it quite a bit and then we tell the control to GoToState, a private helper method that checks other member variables and determines how to call the Visual State Manager’s static GoToState method,

   1: public class RatingControl : Control
   2: {
   3:    private FrameworkElement corePart;
   4:    private bool isMouseOver;
   5:    private bool isPressed;
   6:    public event RoutedEventHandler Click;
   7:      public RatingControl()
   8:      {  DefaultStyleKey = typeof(RatingControl);  }
   9:  
  10:      public override void OnApplyTemplate()
  11:      {
  12:          base.OnApplyTemplate();
  13:          CorePart = (FrameworkElement)GetTemplateChild("Core");
  14:          GoToState(false);
  15:      }
  16:  
  17:      private void GoToState(bool useTransitions)
  18:      {
  19:          if (isPressed)
  20:          { VisualStateManager.GoToState(this, "Pressed", useTransitions); }
  21:          else if (isMouseOver)
  22:          { VisualStateManager.GoToState(this, "MouseOver", useTransitions); }
  23:        //...
  24:      }
  25:      //...
  26:    }

The two major missing pieces are converting the CLR events to the VSM events and the sneaky fact that the setter for the private member CorePart doesn’t just set the CorePart but it also unregisters its old event handlers and registers its new event handlers for MouseEnter, MouseLeave, MouseLeftButtonDown and MouseButtonUp.  This latter step lets us accomplish the former step with event handlers like this

   1: void corePart_MouseEnter(object sender, MouseEventArgs e)
   2: {
   3:     isMouseOver = true;
   4:     GoToState(true);
   5: }
   6:  
   7: void corePart_MouseLeave(object sender, MouseEventArgs e)
   8: {
   9:     isMouseOver = false;
  10:     GoToState(true);
  11: }

Even walking through it fairly carefully it can get very confusing; there are some pretty complex attachments going on. Thus, rather than add insult to injury I’ll stop here and recap and then wait until the first video where you can see the pieces working together before going any further.

Putting It Together

In a nutshell, you have 5 files working together when all is done.

  1. In the ClassLibrary, Rating.cs  which defines the logic and methods as well as the attributes of the custom control. The attributes define the contract, and are what make the control skinnable with the assistance of tools like Blend
  2. Also in the ClassLibrary is a file that must be named generic.xaml that defines the default look for your custom control (in xaml).
  3. In your application there are three files (as usual) that do the heavy lifting: Page.xaml, Page.xaml.cs and App.xaml.  They do their normal jobs here. That is: Page.xaml makes an instance of the control and may or may not include a Template statement asking for a Template in Page.xaml or (more likely) in App.xaml to override the default look and feel
  4. Page.xaml.cs contains the logic of the application (not of the control) just as it always does
  5. App.xaml may have a template for your new Custom control just as it may have a template for button or checkbox.

Follow link for related article" style


More here:
Creating Custom Controls – A Common Starter Application

Published 12 September 2008 09:17 PM by jesseliberty

Comments

# 2008 September 13 - Links for today « My (almost) Daily Links said on 13 September, 2008 03:43 AM

Pingback from  2008 September 13 - Links for today &laquo; My (almost) Daily Links

# Maciek said on 13 September, 2008 09:53 AM

This is exactly what I've been waiting for Jesse, thank you a LOT. I want MOOOOOOOOOOOOOORRRRRRRREEEEEEEEEEEEE :))))))))))))))))))

I have a small side request/question :

I've been having some fun with states and I've noticed that it is still possible to record storyboards (Blend 2.5 Beta)as we used to in SL Beta 1 .

Let's assume that I've created two states :

State A

State B

Let's assume that I've recorded 2 storyboards, 1 for each state :

StoryBoard A = 2 frames(0s, 2s)

StoryBoard B = 2 frames(0s, 3s)

Let's assume that I've added transition lenghts of 1s each, to each state.

How does one affect the other? I don't seem to understand it ?

# Dew Drop - September 13, 2008 | Alvin Ashcraft's Morning Dew said on 13 September, 2008 09:53 AM

Pingback from  Dew Drop - September 13, 2008 | Alvin Ashcraft's Morning Dew

# jesseliberty said on 13 September, 2008 06:21 PM

Maciek,

First, thank you for the kind words. As for your question, let me put something together that will illustrate the question you are asking as it is an interesting one.

Thanks.

# Maciek said on 14 September, 2008 09:58 AM

Awesome, I can't wait.

# mikeb123 said on 14 September, 2008 03:06 PM

Great article Jesse.

If the xaml for the custom control needs to be in generic.xaml, how does one define multiple custom controls in a single class library?

Thanks

# Maciek said on 14 September, 2008 06:04 PM

That's how :

<ResourceDictionary

  xmlns=  -- Many of these>

<Style TargetType="controls:Control1">

</Style>

<Style TargetType="controls:Control2">

</Style>

<Style TargetType="controls:Control3">

</Style>

</ResourceDisctionary>

# Santiago Palladino » Controls Contract said on 15 September, 2008 09:29 AM

Pingback from  Santiago Palladino &raquo; Controls Contract

# jesseliberty said on 15 September, 2008 04:37 PM

One error that I thought I fixed, but didn't. I wrote at the top of the article that the Parts are those elements under the control of the VSM. <wrong, but thanks for playing!>.  What I should have said is that Parts are those elements that are called by methods of the control itself -- the cannonical example is when you click on the repeat button of a scroll bar, the thumb has to move; the scroll bar is responsible for all of this, and so both the repeat button and the thumb are Parts.

Karen Corby was kind enough to point out (at my request)  that I fouled the wording about OnApplyTemplate implying that the control calls this, but of course the platform does.

I do owe a demo program on how transition timing works. That may take a week or two as things pile up here, but I'll see what I can do asap.

# borith said on 15 September, 2008 10:26 PM

could i have the source? i can't follow your instructions. thanks

# Mirrored Blogs said on 17 September, 2008 09:23 AM

In&#160; a previous post I began talking about Custom Controls, and I will continue that discussion over

# Microsoft Weblogs said on 17 September, 2008 09:57 AM

In&#160; a previous post I began talking about Custom Controls, and I will continue that discussion over

# Odegaard said on 17 September, 2008 01:29 PM

Where does the RatingStates and CommonStates groups come into the picture? I don't see you mentioning them anywhere besides declaring them.

# jesseliberty said on 17 September, 2008 02:27 PM

>> Where does the RatingStates and CommonStates groups come into the picture? I don't see you mentioning them anywhere besides declaring them. <<

Ahh, excellent... yes, that is coming, but we have so much to cover first :-)

# Community Blogs said on 17 September, 2008 03:17 PM

Chris Cavenagh has his YouCube interactive, Jesse Liberty on Custom Controls, Pete Brown with SL TechFest

# Frank La Vigne said on 17 September, 2008 05:01 PM
# Jesse Liberty - Silverlight Geek said on 17 September, 2008 09:13 PM

In&#160; a previous post I began talking about Custom Controls, and I will continue that discussion over

# Jesse Liberty - Silverlight Geek said on 21 September, 2008 08:54 AM

It will be helpful as we explore custom controls to have a common starting project. You may&#160; remember

# Mirrored Blogs said on 21 September, 2008 09:11 AM

It will be helpful as we explore custom controls to have a common starting project. You may&#160; remember

# Microsoft Weblogs said on 21 September, 2008 09:17 AM

It will be helpful as we explore custom controls to have a common starting project. You may&#160; remember

# Amin Mahpour » Blog Archive » Silverlight post collections said on 22 September, 2008 05:54 AM

Pingback from  Amin Mahpour  &raquo; Blog Archive   &raquo; Silverlight post collections

# Mirrored Blogs said on 27 September, 2008 01:26 PM

This is the first in a series of explorations of breaking changes in the Release Candidate for Silverlight

# Microsoft Weblogs said on 27 September, 2008 01:58 PM

This is the first in a series of explorations of breaking changes in the Release Candidate for Silverlight

# More Signal said on 30 September, 2008 02:47 PM

This is the first in a series of explorations of breaking changes in the Release Candidate for Silverlight

# Jesse Liberty - Silverlight Geek said on 09 October, 2008 09:47 AM

Over the past month I've posted half a dozen min-articles about creating custom controls that can interact

# Microsoft Weblogs said on 09 October, 2008 10:10 AM

Over the past month I've posted half a dozen min-articles about creating custom controls that can interact

# Mirrored Blogs said on 09 October, 2008 10:37 AM

Over the past month I&#39;ve posted half a dozen min-articles about creating custom controls that can

# Custom Controls - the handling at Blog von J??rgen Ebner said on 10 October, 2008 06:15 AM

Pingback from  Custom Controls - the handling at Blog von J??rgen Ebner

# Jesse Liberty - Silverlight Geek said on 09 November, 2008 12:29 PM

For the past six months or so I've been experimenting with and then &quot;preaching&quot; the idea of

# Microsoft Weblogs said on 09 November, 2008 12:47 PM

For the past six months or so I've been experimenting with and then &quot;preaching&quot; the idea of

# Jesse Liberty - Silverlight Geek said on 05 December, 2008 03:01 PM

&#160; &#160; I have completed the four part video series Creating Skinnable Customer Controls and I

# Microsoft Weblogs said on 05 December, 2008 03:52 PM

&#160; &#160; I have completed the four part video series Creating Skinnable Customer Controls and I

# Erstellen von anpassbaren Custom Control at Programming with Silverlight, WPF & .NET said on 08 December, 2008 04:07 PM

Pingback from  Erstellen von anpassbaren Custom Control at Programming with Silverlight, WPF &amp; .NET

# Jesse Liberty - Silverlight Geek said on 22 December, 2008 05:34 PM

When we started creating How Do I videos, the idea was to have stand alone videos that do not depend

# Microsoft Weblogs said on 22 December, 2008 06:17 PM

When we started creating How Do I videos, the idea was to have stand alone videos that do not depend

# Templates & Custum Controls at Programming with Silverlight, WPF & .NET said on 23 December, 2008 08:29 AM

Pingback from  Templates &#038; Custum Controls at Programming with Silverlight, WPF &amp; .NET

# Templates & Custum Controls at Programming with Silverlight, WPF & .NET said on 23 December, 2008 08:32 AM

Pingback from  Templates &#038; Custum Controls at Programming with Silverlight, WPF &amp; .NET

# The difference between understanding a concept??? - Jesse Liberty - Silverlight Geek said on 31 December, 2008 04:12 PM

Pingback from  The difference between understanding a concept??? - Jesse Liberty - Silverlight Geek

# Jesse Liberty - Silverlight Geek said on 01 January, 2009 06:34 PM

&#160; Note&#160; Starting now (Jan 1 2009) when I add an article that updates an older blog post, I

# themar said on 11 January, 2009 01:32 PM

Hi Jesse,

Great Blog and your videos rock!!!

Have the same question that Mike asked

"How does one define multiple custom controls in a single class library?"

Is it possible?

All my searched couldn't find me any answers

Can you point to any blog or video that explains the above.

Thanks a lot.

# stevensrf said on 15 February, 2009 11:03 AM

where does the vsm come from.

No reference to any vsm object mention above.

# stevensrf said on 15 February, 2009 11:05 AM

WORTHLESS POST WITH ALOT ERROR

# stevensrf said on 15 February, 2009 11:06 AM

I WASTED TWO GDM HOUR TRYING TO IMPLEMENT THIS WORTHLESS CRAP ARTICLE AND THERE ARE SO MANY ERRORS

WHO EVER POSTED THIS SHTFACE ARTICLE PLEASE GOT TO HELL

# stevensrf said on 15 February, 2009 11:22 AM

hey dipsht face author

vsm is referenced by

xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"

# stevensrf said on 15 February, 2009 11:28 AM

HEY FUCK FACE AUTHOR CorePart not defined in

public override void OnApplyTemplate()  

   {  

       base.OnApplyTemplate();  

       CorePart = (FrameworkElement)GetTemplateChild("Core");  

       GoToState(false);  

   }  

WHY IN SAM HELL DID YOU NAME THE CLASS RatingControl AND NOT Rating.

OH YOUR A FUCK FACE THAT WHY

# stevensrf said on 15 February, 2009 11:34 AM

DMB ASS SOB

# Jesse Liberty - Silverlight Geek said on 29 April, 2009 11:16 PM

The Silverlight Toolkit is innovative in many ways, not least of which is that controls are released

# Microsoft Weblogs said on 30 April, 2009 12:14 AM

The Silverlight Toolkit is innovative in many ways, not least of which is that controls are released

# Jesse Liberty - Silverlight Geek said on 29 May, 2009 04:57 PM

&#160; &#160; I had the pleasure of presenting What’s New In Silverlight 3 both at TechEd this year and

# Microsoft Weblogs said on 29 May, 2009 05:42 PM

I had the pleasure of presenting What’s New In Silverlight 3 both at TechEd this year and then again

# Programming with Silverlight, WPF & .NET » Was ist neu in Silverlight 3 said on 05 June, 2009 06:26 AM

Pingback from  Programming with Silverlight, WPF &amp; .NET &raquo; Was ist neu in Silverlight 3

# Top-silverlight » Blog Archive » What???s New In Silverlight 3 said on 06 June, 2009 07:46 AM

Pingback from  Top-silverlight  &raquo; Blog Archive   &raquo; What???s New In Silverlight 3

Search

Go

This Blog

News

     
     
    What's New In Silverlight 3
    A Frequently Updated WikiDoc .
    Last Update: July 10
     
    AgOpenSource
    From Design to implementation
     
    Better Videos
    Diary of an experiment in extreme post-production
     
    Quick Bits
    Too big for Twitter, Too Small for the blog
     
    New to Silverlight?
    Click here to get started.

Syndication