Page view counter

September 2008 - Posts

Dependency Property System – Deeper Dive Part 1

On the 17th I began a discussion of Dependency Properties, but as I said then, there is more to say.  This central concept to Silverlight Programming has traditionally  been taught as an advanced concept.(Can we have  traditions in a product that hasn't really been released yet?) And to some degree, with good reason: you can pretty much ignore the Dependency Property System until/unless you're writing custom controls. Here are three reasons not to:

1. They're fascinating
2. They explain a lot about how things work
3. They are critical to creating your own custom controls

Okay, the real reason is #1; they're just interesting.

There are a couple ways to approach Dependency Properties. Most of us approach them practically, to accomplish a specific goal at first ("How do I wire up a DP in this custom control?") and then later, we return to understand the entire system into which they fit. It is the purpose of today's column to begin that second stage: examining the system as a whole.

An Adventure in Spelunking the Dependency Properties System

We need to start by acknowledging a few somewhat surprising facts:

The folks who created WPF and Silverlight made some rather radical (and I would argue technically courageous) decisions:

  • they said "the current properties are inadequate for our needs, and we're going to add a new layer on top that will not break the underlying layer but that will give us tremendous additional options." 
  • Thus, the Dependency Properties system is really a radical extension to the CLR and to that which is available in C++, Java, or even pre-WPF C#/VB.NET.  
  • Then the folks at Silverlight chose which parts of the system they would use, which they'd adapt and which they'd leave behind based on optimizing for the needs of Silverlight rather than just for pure isomorphic consistency.

Controversial but highly defensible decisions, and ones that will give us early headaches trying to understand all of it,  but long term benefits.

The biggest problem, of course, is that figuring out what is really going on can be a bit problematic, especially right now. This particular moment in history is a documentation purgatory.

  • The WPF books are excellent but not everything they say applies to Silverlight
  • The Beta2 chm file and help files are very good but now partially obsolete
  • The change document is helpful but not guaranteed to be complete
  • Full documentation of Dependency Properties was never a priority as it was considered somewhat advanced/obscure

So we're in for some fun.

To make this work, I'm going to need to (a) break this up into a few related posts and (b) try to impose some discipline on myself to do the set of DP posts within a relatively short period so I don't forget what I've covered already!   I'll tag them all with "Dependency Properties" so you can find them or skip them as you choose.

Quick Review of Why Dependency Properties were Added

As noted in my posting on the 17th the WPF designers quickly found that standard CLR properties were not responsive enough nor extensible enough to support declarative, animated and databound client-side applications. What was needed was a system that could establish the value of a property, at run time, based on input from a number of sources (e.g., the current value of other properties, rapidly changing animation values, etc.).

A key value of the Dependency Properties system was the ability to build properties that automatically notify any registered interested party each time the value of the property changes.  This free, painless and automatic implementation of the observer pattern is tremendously powerful and greatly reduces the burden on the client programmer (in fact, the data-binding system depends on it!).

[We cover the observer patten in detail in Programming .NET 3.5 – for an unauthorized excerpt, click here ]

That alone is worth the price of admission. It means that if you bind numerous controls to dependency properties, you are guaranteed to be notified any time the value of the property changes, without your writing a line of code. Whooah!

Cost - Benefit

 

I believe (and obviously the designers believed) there is enormous benefit to the Dependency Properties system, but let's not ignore the fact that you do pay a price, if only in learning this system.  In this posting I'll cover that price, in the next I'll start to explore some of the benefits.

The price, such as it is….

You need to learn, and grok, a new element in your class – Dependency Properties that interact with an underlying system that didn't usta'  exist. More, some of your properties are really just wrappers to these new DP's. What that means is that the backing value for some of your properties is not a member variable or a value in a database or a computation, but a Dependency Property.  That takes a little mind-share.

Here's what that looks like in code:

// clr wrapper
public bool Valuable
{
   get { return (bool) GetValue( ValuableProperty ); }
   set { SetValue( ValuableProperty, value ); }
}

A pretty standard get and set, except that you access your backing variable using GetValue and SetValue to get and set the value of a Dependency property named ValuableProperty (and that is the idiom, the CLR property name + the word Property = the name of the DP, thus Valuable + Property = ValuableProperty.

The declaration of the DP itself is much weirder,

public static readonly DependencyProperty ValuableProperty =
  DependencyProperty.Register(
  "Valuable",
  typeof( bool ),
  typeof( MyCustomControl ),
  new PropertyMetadata( new PropertyChangedCallback(   
      MyCustomControl.OnValuablePropertyChanged ) ) );

Let's  break this down. The first line declares my object (which is really a reference to a DependencyProperty) as public; it must be static and readonly, and its type is DependencyProperty and its name (identifier) is ValuableProperty.

We set that reference to what we'll get back by calling the static Register method on the DependencyProperty class. Register takes four arguments:

  1. The name of the dependency property wrapper
  2. The type of the DP being registered
  3. The type of the object registering it
  4. The Callback

The Callback is of type PropertyMetaData. You can imagine a world in which there are various pieces of MetaData for the DependencyProperty. At the moment, however, in Silverlight, there is only one: the callback.

The constructor for the PropertyMetaData takes an object of type PropertyChangedCallback which will be called any time the effective property value of the DP property changes.  We pass it a reference to the method to call (which equates to a callback).

The net of all of this is that we present to the world a CLR property (Valuable) which is in fact backed by a DependencyProperty which will call back to the method OnValuablePropertyChanged any time the effective value of the property changes.

The callback method will take two arguments:

  • A DependencyObject (the control)
  • An object of type DependencyPropertyChangedEventArgs

Typically you'll cast the first argument to be the type of the control that contains the property, and you'll cast the NewValue property of the DependencyPropertyChangedEventArgs object to the DependencyProperty that changed. You can then take whatever action you need to based on the change in the DP's value

[Listing updated 10/1 – 10:30 am]

   1: public class MyCustomControl : Control
   2: {
   3:  
   4:    public static  readonly DependencyProperty 
   5:       ValuableProperty = DependencyProperty.Register(
   6:        "Valuable",
   7:        typeof( bool ),
   8:        typeof( MyCustomControl ),
   9:        new PropertyMetadata( new PropertyChangedCallback( MyCustomControl.OnValuablePropertyChanged ) ) );
  10:  
  11:    public bool Valuable
  12:    { 
  13:       get { return (bool) GetValue( ValuableProperty );}
  14:       set { setValue( ValuableProperty, value );}
  15:    }
  16:  
  17:    private static void OnValuablePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
  18:    {
  19:       MyCustomControl control = d as MyCustomControl;
  20:       bool b = (bool) e.NewValue;
  21:    }
  22: }
  23:  

 

 

That's pretty much it, except getting your head around why you did all this; and for that we have to begin to explore, in more detail, the benefits of the system. That is, once you have this, what have you gained?

Stay tuned, rather than gloss over it, I'd like to take the time to explore it in depth.

-jesse

Isolated Storage – Might Be Easier Than You Think

 

Tim Heuer did an excellent video on IsolatedStoarge, but I'd like to quickly review the the fundamentals as they are quite a bit simpler than many fear.  The idea of IsolatedStorage is to provide your Silverlight Application with access to the user's disk, so that you may store… well, whatever you like when your application is not running. Whether or not you have access, and how much space is allocated is entirely under the control of the user.

In many ways, IsolatedStorage is the next generation of cookies: you can store more, more robustly, with greater precision and control. A full blown usage of IsolatedStorage might involve the creation of files within a directory structure (all of which is obfuscated on the user's hard drive), quota allocation and the storage of binary data. You might use this to store far more than window positions or the user's current "state" within your application; you could conceivably store parts of your application or other binary data to speed up or otherwise enhance the user's experience.

At its simplest though, using IsolatedStorage can be as simple as writing to one of two pre-defined dictionaries: IsolatedStorageSettings.ApplicationSettings or IsolatedStorageSettings.SiteSettings. 

In both cases, the key is defined to be a string, the value to be an object. You grab a reference to the dictionary and the IsolatedStorage system ensures that your application's dictionary is isolated from the dictionaries of other applications. SiteSettings are, as you might expect, shared among all applications on a site.

To be more exact, Applications settings are per-application, per-computer and per-user while SiteSettings are  per-domain, per-computer and per-user.

If I have IE open to two sites, http://microsoft.com/redmond/example1.xap and http://microsoft.com/redmond/example2.xap, these applications will share site settings since they share the same domain, but different application settings.

This can get tricky, though. If my browser is open to http://microsoft.com/redmond/example1.xap and http://microsoft.com/boston/example1.xap it turns out they share the same site (microsoft.com) but not the same application as these are independent .xap files as determined by the full path.

But now we're deep in the mud, and at a high level, getting started with IsolatedStorage is easy, which is the point of this posting.

The application I built (total development time 10 minutes) looks like this,

IsoAdd

Clicking the Add button adds three key/value pairs to IsolatedStorageSettings.ApplicationSettings  and 9 key/value pair to IsolatedStorageSettings.SiteSettings.

   1: void Add_Click( object sender, RoutedEventArgs e )
   2: {
   3:    try
   4:    {
   5:       appSettings.Add( "Ulysses", "Stephen Dedalus" );
   6:       appSettings.Add( "The Catcher in the Rye", "Holden Caulfield" );
   7:       appSettings.Add( "Tom Sawyer", "Huckleberry Finn" );
   8:       siteSettings.Add( "Connection", "Fios" );
   9:       siteSettings.Add( "SpeedUp", "20" );
  10:       siteSettings.Add( "SpeedDown", "5" );
  11:       siteSettings.Add( "Browser1", "IE7" );
  12:       siteSettings.Add( "Browser2", "FireFox" );
  13:       siteSettings.Add( "Browser3", "IE8" );
  14:       siteSettings.Add( "Browser4", "Safari" );
  15:       siteSettings.Add( "OS1", "Vista" );
  16:       siteSettings.Add( "OS2", "Leopard" );
  17:       Display( "Added 3 to app settings, 9 to app settings." );
  18:    }
  19:    catch ( ArgumentException ex )
  20:    {
  21:       Display( ex.Message );
  22:    }
  23: }

Retrieve is hardwired to return the value where the key is Ulysses,

   1: void Retrieve_Click( object sender, RoutedEventArgs e )
   2: {
   3:  
   4:    try
   5:    {
   6:       Display("If the user asked for the main character  in Ulysses, we'd return " + 
   7:          appSettings["Ulysses"].ToString());
   8:    }
   9:    catch ( System.Collections.Generic.KeyNotFoundException ex )
  10:    {
  11:       Display( ex.Message );
  12:    }
  13: }

 

 

 

 

 

IsoRetrieve

Finally, to demonstrate that these are normal collections, with the normal keys and values collection, I provide buttons that iterate through the collections.

IsoValues

Once again, the code is just C# iterating through a dictionary's value collection…

   1: void Values_Click( object sender, RoutedEventArgs e )
   2: {
   3:    string header = "Values: ";
   4:    string msg = header;
   5:    foreach ( object o in appSettings.Values )
   6:    {
   7:        if ( msg.Length > header.Length )
   8:             msg += ", ";
   9:          msg += o.ToString();
  10:       
  11:    }
  12:  
  13:    foreach ( object o in siteSettings.Values )
  14:    {
  15:        if ( msg.Length > header.Length )
  16:             msg += ", ";
  17:          msg += o.ToString();
  18:    }
  19:    Display( msg );
  20: }

 

 

 

 

Not Just Strings

Of course, you don't have to add just strings. The value is an object and can be anything. Let's make an incredibly simple GeekObject and substitute that for the applicationSettings.  We'll start by adding a GeekClass,

   1: namespace SimpleIsolatedStorage
   2: {
   3:    public class Geek
   4:    {
   5:       public string Name { get; set; }
   6:       public int GeekScore { get; set; }
   7:       public string FavoriteGeekBook { get; set; }
   8:       public bool PassTheBestSceneInApollo13Test { get; set; }
   9:  
  10:       public Geek( string name, int score, string book, bool passed )
  11:       {
  12:          Name = name;
  13:          GeekScore = score;
  14:          FavoriteGeekBook = book;
  15:          PassTheBestSceneInApollo13Test = passed;
  16:       }
  17:    }
  18: }

 

Notes: The GeekScore is your results on the Computer Geek test. The PassTheBestSceneInApollo13Test is very simple: what's your favorite scene in the movie Apollo 13?   The value true indicates you answered with some variation of the following:[Several technicians dump boxes containing the same equipment and tools that the astronauts have with them onto a table] Technician: We've got to find a way to make this
[square CSM LiOH canister]  fit into the hole for this [round LEM canister]  ... using nothing but that.

The changes to the code are in Add, Retrieve and Values as follows:

   1: void Add_Click( object sender, RoutedEventArgs e )
   2: {
   3:    try
   4:    {
   5:       appSettings.Add( "Jesse", new Geek("Jesse Liberty", 62, "Dragon Book", true));
   6:       appSettings.Add( "Chris", new Geek("Chris ???",30.96647,"Programming WPF",true));
   7:       appSettings.Add( "Dan", new Geek("Dan Hurwitz", 0, "Programming ASP.NET", true));

Adding the appSettings, we make sure we add Geek objects.

Retrieving, we retrieve a hardwired value again, but this time we have to parse out the Geek values,

void Retrieve_Click( object sender, RoutedEventArgs e )
{

   try
   {
      object o = appSettings["Jesse"];  // hard wired retrieve
      Geek g = o as Geek;
      string notString = g.PassTheBestSceneInApollo13Test ? String.Empty : "not ";
      Display("Retrieving Geekiness for Jesse. Score:  " + g.GeekScore.ToString() +
         " and " + g.Name + " did " + notString + "pass the Apollo 13 movie test. ");
   }
   catch ( System.Collections.Generic.KeyNotFoundException ex )
   {
      Display( ex.Message );
   }
}

IsoGeekRetrieve

 

 

 

Finally, when we tick through the values collection, we want again to make sure we're dealing with the Geek object and not just the object per se,

   1: void Values_Click( object sender, RoutedEventArgs e )
   2: {
   3:    string header = "Values: ";
   4:    string msg = header;
   5:    foreach ( object o in appSettings.Values )
   6:    {
   7:        if ( msg.Length > header.Length )
   8:             msg += ", ";
   9:  
  10:        Geek g = o as Geek;
  11:        msg += g.Name + "(" + g.FavoriteGeekBook + ")";
  12:       
  13:    }

IsoGeekValues

Full Source Code SimpleIsolatedStorage.zip 

 

Thanks.

RC0 & ContentPresenter

 

The breaking changes document points out that ContentPresenter now derives from FrameworkElement and thus loses 18 public properties  as well as TextAlignment, TextDecorations and TextWrapping. All of this calls for a bit of rewriting if you've used this powerful and useful control, and there was a request in one of the internal discussion lists that this be called out to developers; hence this blog entry.

VideoStartSerendipitously, I have a video that uses the ContentPresenter control.

The ContentPresenter control is the enabling control behind entering "Content" rather than text in the standard Button, CheckBox and so forth.  Until RC0, you could have set the fontsize, FontWeight, etc. in the ContentPresenter itself, though that never would have been good programming practice, as it always made more sense to leave that for the client (the programmer using your control.

Thus, in my template, I set few properties on the ContentPresenter itself, and when I use the control that the ContentPresenter is part of (the Button) I can set the characteristics of the content, which are then passed to the ContentPresenter. This will continue to work in RC0

Let me be explicit, if you used ContentPresenter as intended (note the Beta documentation which states "Typically, you use the ContentPresenter directly within the ControlTemplate of a ContentControl to mark where the content is to be added.") then you would probably not have used any of the properties that are no longer available to you, as you would have wanted, as I did, to leave that flexibility to the consumer of your control.

On the other hand, if you did use those properties, the fix is fairly simple, you just remove the properties from the content control, and if you need the property set, you set it when you call the control

An example will make this explicit.  Assume you define your button template in App.xaml as follows (the following listing is abridged):

   1: <ControlTemplate x:Key="RoundButton" TargetType="Button">
   2:     <Grid>
   3:         <vsm:VisualStateManager.VisualStateGroups>
   4:         </vsm:VisualStateManager.VisualStateGroups>
   5:         <Ellipse >
   6:         </Ellipse>
   7:         <ContentPresenter Margin="0,10,0,0" x:Name="RoundButtonContent" 
   8:                           RenderTransformOrigin="0.5,0.5" 
   9:                           HorizontalAlignment="Center" 
  10:                           VerticalAlignment="Center"
  11:                           FontFamily="Comic Sans MS"                           
  12:                           FontSize="24"
  13:                           FontWeight="Bold"
  14:                           Foreground="#FFFF0000">
  15:             <ContentPresenter.RenderTransform>
  16:                 <TransformGroup>
  17:                 </TransformGroup>
  18:             </ContentPresenter.RenderTransform>
  19:         </ContentPresenter>
  20:     </Grid>
  21: </ControlTemplate>

Focusing on lines 11-14, you have to ask why you would hard code into the ContentPresenter portion of your button the font characteristics and the color. This would mean that every Button the user wants to create must have a 24 point Comic Sans MS Bold red font, which seems a little restrictive.

In any case, starting in RC0 and consistent with WPF, this is no longer legal Silverlight code, and you'll now need to change your ContentPresenter definition to remove these lines. But that is a good thing, they belong instead in the xaml file that uses your button. Thus, the definition of the Button becomes:

   1: <ControlTemplate x:Key="RoundButton" TargetType="Button">
   2:     <Grid>
   3:         <vsm:VisualStateManager.VisualStateGroups>
   4:         </vsm:VisualStateManager.VisualStateGroups>
   5:         <Ellipse >
   6:         </Ellipse>
   7:         <ContentPresenter Margin="0,10,0,0" x:Name="RoundButtonContent" 
   8:                           RenderTransformOrigin="0.5,0.5" 
   9:                           HorizontalAlignment="Center" 
  10:                           VerticalAlignment="Center">
  11:             <ContentPresenter.RenderTransform>
  12:                 <TransformGroup>
  13:                 </TransformGroup>
  14:             </ContentPresenter.RenderTransform>
  15:         </ContentPresenter>
  16:     </Grid>
  17: </ControlTemplate>

and the instantiation of two RoundButtons might look like this (in, e.g., Page.xaml):

   1: <Button x:Name="Go" 
   2:         HorizontalAlignment="Right" 
   3:         VerticalAlignment="Stretch" 
   4:         Width="109" Grid.Row="5" Content="Go!" 
   5:         Template="{StaticResource RoundButton}" 
   6:         Foreground="#FF00FF00" 
   7:         FontFamily="Georgia" 
   8:         FontSize="36" 
   9:         FontWeight="Bold"/>
  10: <Button x:Name="Stop"
  11:         HorizontalAlignment="Stretch" 
  12:         VerticalAlignment="Stretch" 
  13:         Grid.Column="1" Grid.Row="5" 
  14:         Content="Stop!" 
  15:         Template="{StaticResource RoundButton}" 
  16:         Foreground="#FFFF0000" 
  17:         FontFamily="Comic Sans MS" 
  18:         FontSize="18" 
  19:         FontWeight="Semi-Bold"/>

This allows for the creation of two RoundButtons, but each with content that has a different font family, size, weight and color:

 ToRoundButtons

Generated Duration

Note that another small breaking change in RC0 is that within the definition of VisualStateGroups, the Duration property of Transitions has been renamed to GeneratedDuration, thus, the example code must change so that the VisualTransition Durations are now marked as Generated Transitions

Beta Code

   1: <vsm:VisualStateManager.VisualStateGroups>
   2:     <vsm:VisualStateGroup x:Name="FocusStates">
   3:         <vsm:VisualState x:Name="Unfocused"/>
   4:         <vsm:VisualState x:Name="Focused"/>
   5:     </vsm:VisualStateGroup>
   6:     <vsm:VisualStateGroup x:Name="CommonStates">
   7:         <vsm:VisualStateGroup.Transitions>
   8:             <vsm:VisualTransition Duration="00:00:00.2000000"/>
   9:             <vsm:VisualTransition Duration="00:00:00.1000000" To="Disabled"/>
  10:             <vsm:VisualTransition Duration="00:00:00.1000000" From="Disabled"/>
  11:         </vsm:VisualStateGroup.Transitions>

RC0 Code

   1: <vsm:VisualStateManager.VisualStateGroups>
   2:     <vsm:VisualStateGroup x:Name="FocusStates">
   3:         <vsm:VisualState x:Name="Unfocused"/>
   4:         <vsm:VisualState x:Name="Focused"/>
   5:     </vsm:VisualStateGroup>
   6:     <vsm:VisualStateGroup x:Name="CommonStates">
   7:         <vsm:VisualStateGroup.Transitions>
   8:             <vsm:VisualTransition GeneratedDuration="00:00:00.2000000"/>
   9:             <vsm:VisualTransition GeneratedDuration="00:00:00.1000000" To="Disabled"/>
  10:             <vsm:VisualTransition GeneratedDuration="00:00:00.1000000" From="Disabled"/>
  11:         </vsm:VisualStateGroup.Transitions>

 

The changes are on lines 8, 9 and 10 in the snippet above. 
Posted by jesseliberty | 13 comment(s)
Filed under:

Skinnable Custom Controls – Change to generic.xaml

This is the first in a series of explorations of breaking changes in the Release Candidate for Silverlight 2.  Please be sure to read Tim's blog entry about Silverlight RC0 for context and directions.

Summary

There are 2 significant breaking changes in how you handle the file generic.xaml when creating a Skinnable custom control in Silverlight 2.  This is a change from my blog post on 21st of September and the 12th of September and will be reflected in the forthcoming videos and tutorial.  The first change is that the file itself has a new location inside the Themes folder. The second is that the syntax has changed from declaring storyboards as resources and the referring to them in the declaration of Visual States, to combining the declaration of visual state with defining the associated storyboards.

The first change makes Silverlight more WPF-like.

The second change may or may not have been 100% intentional but will be in the release and I personally find it totally inoffensive.

Location, Location, Location

The file generic.xaml  has moved in RC0 from the root of the class library to a folder named Themes:

ThemesFolder

(Right click on Class Library and choose New Folder – name it Themes. Right click on the folder to choose New Item and create the user control or if you've already created it, drag it into the folder).

Syntax

The syntactic change is to remove the <Grid.Resources> section and to combine the definition of the Storyboards with the definition of the States. Here is the old syntax:

   1: <ResourceDictionary
   2:  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     &lt;Style TargetType="controls:StatusControl">
   4:         <Setter Property="Template">
   5:             <Setter.Value>
   6:                 <ControlTemplate TargetType="controls:StatusControl">
   7:                     <Grid x:Name="LayoutRoot">
   8:                          <Grid.Resources>
   9:                             <Storyboard x:Key="MouseOverState">
  10:                             </Storyboard>
  11:                             <Storyboard x:Key="OnState">
  12:                             </Storyboard>
  13:                         </Grid.Resources>
  14:                         <vsm:VisualStateManager.VisualStateGroups>
  15:                             <vsm:VisualStateGroup x:Name="CommonStates">
  16:                                 <vsm:VisualState x:Name="Normal" />
  17:                                 <vsm:VisualState x:Name="MouseOver" 
  18:                                       Storyboard="{StaticResource MouseOverState }" />
  19:                             </vsm:VisualStateGroup>
  20:                             <vsm:VisualStateGroup x:Name="StatusStates" >
  21:                                       Storyboard="{StaticResource OnState }" />
  22:                             </vsm:VisualStateGroup>
  23:                         </vsm:VisualStateManager.VisualStateGroups>
  24:                         <Ellipse x:Name="Core" >
  25:                             <Ellipse.RenderTransform>
  26:                             </Ellipse.RenderTransform>
  27:                             <Ellipse.Fill>
  28:                             </Ellipse.Fill>
  29:                         </Ellipse>
  30:                     </Grid>
  31:                 </ControlTemplate>
  32:             </Setter.Value>
  33:         </Setter>
  34:     </Style>
  35: </ResourceDictionary>   

 

 

 

 

I've left out a lot of detail, but what is essential here is that we were declaring the storyboards as resources, and then referring to them in the VisualStateManager.VisualStateGroups.  The new syntax is to combine the Visual State declarations with their associated storyboards, as we do when creating templates:

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     &lt;Style TargetType="controls:StatusControl">
   4:         <Setter Property="Template">
   5:             <Setter.Value>
   6:                 <ControlTemplate TargetType="controls:StatusControl">
   7:                     <Grid x:Name="LayoutRoot">
   8:                       <vsm:VisualStateManager.VisualStateGroups>
   9:                             <vsm:VisualStateGroup x:Name="CommonStates">
  10:                                 <vsm:VisualState x:Name="Normal" />
  11:                                 <vsm:VisualState x:Name="MouseOver" >
  12:                                     <Storyboard >
  13:                                     </Storyboard>
  14:                                 </vsm:VisualState>
  15:                             </vsm:VisualStateGroup>
  16:                             <vsm:VisualStateGroup x:Name="StatusStates" >
  17:                                 <vsm:VisualState x:Name="OnState" >
  18:                                     <Storyboard >
  19:                                     </Storyboard>
  20:                                 </vsm:VisualState>
  21:                             </vsm:VisualStateGroup>
  22:                         </vsm:VisualStateManager.VisualStateGroups>
  23:                         <Ellipse x:Name="Core" >
  24:                     </Grid>
  25:                 </ControlTemplate>
  26:             </Setter.Value>
  27:         </Setter>
  28:     </Style>
  29: </ResourceDictionary>   

 

Notice there is no <Grid.Resources> area, as each <vsm:VisualState> is declared, if there is an associated storyboard it is placed within the VisualState definition.

Thanks.

-jesse

Posted by jesseliberty | 26 comment(s)
Filed under:

Silverlight 2 – The release candidate and next steps

On Thursday night we published the first public release candidate for Silverlight 2, described in some detail by Scott Guthrie.  This is a developer-only release and you can learn all about what it is and how to get it in a terrific blog post by Tim.

Starting immediately,  I'll be re-focusing this blog on clarifying what has changed, how it affects your programming and what you need to know to get ready for release.

As of today, I am making a commitment to you to have a meaningful blog entry published every day by 1pm Eastern (GMT –5).  By every day I mean… every day.

It is my goal for this blog to become the go-to resource on a day to day basis and for there to be a substantive Silverlight article here every day. 1pm.  Look for less clutter, more signal, less noise.

To kick things off, today I'll be blogging on a change in how you create the default appearance of custom controls (in generic.xaml) in the release candidate.  Look for it by 1pm.

Podcast on Templating

 

 

I'm pleased to announce that the first of my monthly Sparkling Client podcasts has been posted. This month, we take a Tour of Silverlight Templates.

SaprklingClient Subscribe

Resources

Additional resources to supplement this topic include

Creating Custom Controls – A Common Starter Application

Follow up posted at bottom of article

It will be helpful as we explore custom controls to have a common starting project. You may  remember from my posting on the 12th that I laid out a structure with three projects in one solution, with 5 important files.

  1. In the ClassLibrary, customcontrol.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. 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.

I've created a starter application with corresponding files and placed it here  The starter application just lays out the structure of the program and will be used for three inter-related purposes

1. An intensive tutorial (expected publication date approximately 10/13
2. A set of video I'll be developing in the next week or two
3. Blog entries exploring what I hope will be interesting topics in depth

Following the suggestions received here, the tutorial should be pretty intensive, while the blog and videos will assume a more intermediate level of knowledge.  If this topic (templatable custom controls) is of interest, you'll want to start by reviewing some or all of these:

Blog entries

Tutorial

Videos

Thanks.

 

Follow Link for Related Article


More Skinnable Custom Controls – Change to generic.xaml

What topics are important to you? Survey Results

The present letter is a very long one, simply because I had no leisure to make it shorter. - Blaise Pascal

Again, thank you for taking the time to respond to the survey, it was extremely helpful to me in planning the videos, tutorials and blog posts I'll be creating in coming months.

The survey is still open, but this constitutes my report. Unless there are any surprises, I'll read what comes in and incorporate it into my plans.

Communication

iStock_ScreamingAtLapTopXSmall The most significant aspect of the survey is that you took the time to let me know what you need, and I'm happy to say that what you need fits in well with what we're geared up to provide.

Overall, most of you are quite happy with the material here and find this a useful resource, but I'm convinced we can and must do a lot better.

What won't help is to keep saying that, so with this final report on the survey , it is my intention to stop blogging about  plans and start churning out new resources.

 

A Rare Medium Well Done

I asked about your preference for tutorials, walk through, video, etc.  You were of a single mind. Examining your first and second choices, we see that:

  • 85% value in-depth tutorials
  • 67% value videos
  • 44% value short walk-throughs
  • 10% value web casts.

As a result, expect to see more tutorials and videos, but also expect to see an attempt at making webcasts a more useful medium. They clearly are not successful as live versions of "how do I videos," and frankly I don't find them a good substitute for live conference presentations, but I've not given up. Live is live, and I'm convinced I can find a way, despite whatever barriers there are, to leverage that immediacy.

Please Sir, I Want Some More…

As for the level (novice to advanced), there was a very strong consensus (over 80%) that all the material should be at the intermediate to advanced level – but of course the folks who were most likely to respond were the most involved and most experienced users of the site. 

What comes through equally clearly, however is that you see the tutorials as the place for the most advanced material. 

Real Code for Real Programmers

iStock_ShockedManAtPCXSmallWhile there was no single topic that stood out as the most desirable, there was an overwhelming consensus that it was time to move beyond the introductory material and into the more advanced real-world, business oriented application-building areas.

Making sense of this kind of survey is 10% statistical analysis, 90% how you cluster the information.   Because this can't by its nature be a scientifically valid survey, I've aggregated where I can, looking for trends more than pin-point scaling. This led me to three groups:

  • Most Important indicates that 2/3 of the participants marked these as most important or very important.
  • Very Important   At least 1/2 marked as Very important or Important.  These I ordered based on the percentage who ranked a topic as very important or most important, but where the numbers were close I put similar topics together.
  • Unimportant These are the two topics that 1/3 of the participants marked as unimportant.

Here are the groups, brief analysis of some of the topics follows.

Most Important

  • Walk through of moderate size application
  • Custom Controls
  • Multiple page applications
  • Dynamic Loading
  • Web Services

Very Important

  • DataGrid
  • From SQL to Data Controls
  • Data Services (Astoria) 
  • XML into Data Controls  
  • Deployment
  • Blend
  • Mouse
  • Drag and Drop
  • Drawing, Brushes, Transforms
  • Animation
  • ASP.NET app services
  • Isolated Storage
  • Streaming
  • Asp.NET
  • Media

Consensus Unimportant

  • Markers in Video
  • Hypervideo

Additional Topics requested in comments

  • More about new controls
  • Authentication
  • Validation
  • A Learning Path

 

Brief Analysis

Without belaboring the point, this is almost entirely good news, because all of these are topics for which  we can readily provide extensive resources in a relatively short amount of time, and we can do so at the more advanced level you've requested. 

Let me focus briefly on the five members of the first group – Most Important:

App Walk Through This is very high priority for us. Most likely the first example of this will be related to Tim Heuer's new undertaking which I believe he has started to talk about and plan but has not rolled out yet.

As an aside, I also have another approach cooking in which I will be building some stand-alone modules that I hope to plug together into a larger application over time.  There is yet a third, unrelated project being developed in another group, that starts with design and walks all the way through delivery that I'm looking into and will report back on.
Custom Control Expect a 400-level tutorial & a series of videos within the next few weeks
Multi-page Apps Expect a brief 400 level tutorial or a solid walk through.  We have two videos. Part 1  and Part 2
Dynamic Loading Expect a brief 400-level tutorial and at least 1 video before the end of the year
Web Services Expect a brief 400-level tutorial and a series of videos  before the end of the year

A brief note on Web Services: I want to talk with some folks in the WCF team about this. While I'm more than happy to create a WCF server and then show how to interact with it so that you can see both ends of the cycle, Silverlight is more of a consumer; the WCF part is really outside of Silverlight's area of concern. That said, many of you have asked for this, so it may be faster to just do it.

Very Brief Note about Video Markers – This was the only topic that almost everyone marked as unimportant. I think this speaks to the poor job I've done conveying what these are for. Some time early next year I'll put together a project that shows what can be done with these that I think will transform the way we deal with video in the way that hypertext transformed the way we deal with text and images.

Additional Topics

Of course, in addition to the top 5, I'l be creating videos (and eventually tutorials) on each of the subjects in the second group, though not necessarily in strict order. Videos and shorter blog-based walk through's may be available before full-blown tutorials, though one way to get more information out there is to make sure both the videos and the tutorials are shorter and have a higher signal:noise ratio.

Note, a learning path was requested;  you may want to start here.

Other innovations are in the works, but there is enough substance in what is laid out above to serve as the central focus as we move rapidly towards release.

Again, thanks for taking the time to fill out the surveys and as always please feel free to post your concerns on any of the feedback forms or to send me email.

-jesse

Notes
A Rare Medium Well Done was the VideoTex slogan at one time. How I wish I made it up. 
Please Sir, I want some more
… is a quote from Oliver Twist.


(30)

Survey – Results compiled tonight

iStock_HourGlassXSmallThank you for an unprecedented response to this week's survey on what topics are most important to you. I will keep the survey open, but I will attempt to compile and report on the results by tomorrow morning. Your feedback is incredibly valuable.

Statistics as of this morning include over 250 responses.

Full report in the morning, but here are some very preliminary impressions:

The results have held steady from the very beginning – Webcasts have overwhelmingly been the "last choice" for over 2/3 of users and in-depth tutorials have been the first choice; with short videos and short tutorials splitting the middle position.  In any case, there is almost total unanimity that all presentations should be at the Intermediate (300) level except for tutorials,  which nearly 2/3 of you think should be more advanced.

 

I'll break the above out in more detail in the morning, and I'll certainly give more detail on the topics, but the top level there is that there are only a few topics that nearly everyone thinks is unimportant!

Preliminary Indicators

In any case, there is  tremendous agreement about the important topics. Big winners include custom controls, anything touching on data, advanced features, web services, deployment, dynamic loading… well, like I said, I'll have a full report tomorrow.

Perhaps the most surprising finding to me (so far) is that fully 1/3 of the respondents find interacting with ASP.NET unimportant. I'll be probing that more.

The Free Form Comments

The true treasure is in the free-form comments that fell under "what didn't we ask and what would you have told us" – Here I have 91 thoughtful views to read through and summarize. Terrific stuff.

First Cut-off: 17:00 (GMT –5) Sept 18

If you can get your response in by tonight, that will certainly have the greatest impact, but as I say, I will keep the survey open and I'll consult the results over coming weeks.

Quick Responses To A Couple Comments

VB? VB!

One comment was "VB -- VB -- VB -- VB -- VB -- VB -- VB -- VB -- VB Do examples in Visual Basic!!!! VB -- VB -- VB -- VB -- VB -- VB -- VB -- VB – VB"  -- You'll be happy to know that (virtually) all the videos will be done in VB with code available in both VB and C# and that the tutorials are (slowly) being redone so that we can offer both a C# and a VB version of each.

"You should be telling us not us telling you"

Very quick note to the person who advised that I not ask for guidance from the community – that I "am meant to be a visionary, not a follower"  I have to say that I shy away from visionaries, they are almost always wrong and Kool-Aid gives me a stomach ache. 

While I certainly see that my job is to convey information and best practices, I also believe that it is imperative that I be responsive to the needs of those who are investing their time here. And I find that one of the  best way to find out what people want is to ask.

I do hope you'll contact me personally about your other frustrations with the site and with the quality of the material here; I'm sure we can find what you need.

Personality Vs Information

Finally, this seems like a good place to say (and then be quiet) that I do see this as a blog rather than as a mini-newsletter. To me, that means that this is where I get to interact, to create a presence and to inject some personality into my interactions. A geeky personality to be sure, but a human one as well, complete with family, pets, and the occasional opinion.

My own guidelines are to balance like this: roughly 85% focused on hard-core  Silverlight technical information, 10% news, information, plans, and 5% random thoughts about technology and tangential topics.  This summer it has been out of balance, but I'm actively re-balancing.

NB: I've moved all political, personal, and publishing information to my personal web-site/portal.  It's true that the dividing line between the two sites is  semi-permeable but  I like a little ambiguity in my life. 

Stay tuned, full analysis tomorrow.

Thanks again.

-jesse

Dependency Properties – Background for Custom Controls

In  a previous post I began talking about Custom Controls, and I will continue that discussion over the next few days and weeks even as I start producing videos on the topic.

It turns out that the more you look at Custom Controls, the deeper you get into the Silverlight UI architecture, and there is much to see.

As an example of this phenomenon, in writing about Custom Controls, you quickly realize that sooner or later you must describe and explain the Dependency Properties System in Silverlight. This brings great joy to my heart, because it is one of the more interesting and one of the less discussed aspects of Silverlight programming

So this evening I'm going to leap ahead (a bit out of order) and introduce this system, though by no means cover it in depth in one blog post. 

(Written late Tuesday, posted early Wednesday)

Understanding Our Roots

Traditional C# classes consist of

  • Methods
  • Events
  • Member Variables
  • Properties

You can easily view these in the Object Browser in Visual Studio (all but the member variables, which are almost always marked private and not shown).

ObjectBrowserUIButtonNoCircles

[Note, image cut down to save space]

C++  did not have properties (nor running water, nor electricity). Back in those dark days, if we wanted to provide access to the value of a member variable, we created methods to do so.

Thus, you ended up with funny looking code like this:

overallApproval = jesse.GetApprovalAverage() + tim.GetApprovalAverage();

Rather than the more natural,

overallApproval = jesse.Approval + tim.Approval;

Properties were brilliant. They looked to the class author just like methods, but they looked to the class consumer just like variables.

This was crucial. The class author was free to continue to obtain the actual value from backing variables or anywhere else:

   1: private double approval = .4;
   2: public double Approval
   3: {
   4:    get
   5:    {
   6:       if ( currentViewer.ID == "ScottGu" )
   7:          return .9;
   8:       else
   9:          return approval;
  10:    }
  11: }

In practical experience, most properties are backed by member variables, though that is by no means universal – some are backed by values in persistent storage, and some are computed dynamically. But some very large percentage are backed by member variables, which is why Auto-implemented properties were invented (and they are also brilliant). Prior to C# 3.0 my code was peppered with annoying properties that looked like this:

   1: private int myMember;
   2: public int MyProperty
   3: {
   4:    get { return myMember; }
   5:    set { myMember = value; }
   6: }
   7:  

Auto-implemented properties let me write

public int MyProperty { get; set; }

which C# 3.0 interpreted as an exact equivalent – the IL code produced is identical. 

Yes, there are a few restrictions, the only significant one is that I can't do anything in the getter or setter except get and set the value. If I need to do anything else, it's back to the full getter and setter – leaving me no worse off than before. 

So What's Wrong With Properties?

As terrific as properties are, they just don't cut it in a declarative, animated, data-bound world. Think about the property that describes the position of an object that is being animated; that value is changing very rapidly, in real time, possibly in response to conditions that cannot be anticipated at compile time. Properties just were not invented for that.

A Hidden World Underneath Your Feet

It turns out that WPF and Silverlight have extended properties in a way that you might never have suspected, and really never need be aware of until and unless you decide to create your own controls.

In fact, it is entirely possible to write completely robust Silverlight applications for quite a long while without knowing that underneath the surface lies an entirely new and very powerful system, humming gently and doing its work.

When you do learn of it, however, the revelation is not unlike the moment when you learned that beneath the streets of your city was a gargantuan system of pipes and wires that made up the city’s power and water grid. A bit unsettling at first, and then totally fascinating (at least it was to me, but I'm a nerd). 

In brief:  the Dependency Properties system allows Silverlight to determine the value of a property not from a value at compile time, but rather dynamically from a number of different inputs: specifically from

  • Resources

allowing you to write code that looks like this

   1: <UserControl.Resource>
   2:   <SoldColorBrush x:Key="ABrushFromAResource" Color=Red" />
   3: </UserControl.Resource>

and then later in your Xaml file use that code like this

   1: <Button "{StaticResource ABrushFromAResource theBrush}" width="100"
   2: height="100" />
  • DataBinding

By using Dependency Properties the value of the property can be set based on late binding to a data source.

  • Animation

Animation makes the highest demands on a property; requiring that its value change quickly over short periods of time. It is animation that makes crystal clear that the Dependency property system had  to be build, that the extant Reflection system could never be sufficiently performant

Dependency Property Precedence

One issue that quickly arises is this: if a property might be affected by multiple sources (the template says I should be red, but the data binding says I should be blue, but the animation just turned me to violet" – who wins?

To solve this problem, a non-negotiable (and quite logical) order of precedence was established. Like all such things, there is some complexity to handle border cases, but the essence of it is straight forward, and just what most of us would do given enough time (say 5 years) to think it through:

  • Animation always wins
  • IN the absence of animation, local values (values on the object itself) take precedence
  • If neither of the above apply, use the templated value
  • If none of the above apply use the style
  • Failing all of that, use the default value

But Wait, There's More…

There is always more to say, but this general overview of the Dependency Property system will serve as a solid foundation for our next exploration of custom controls. We'll return to the Dependency Properties system in more depth as we go.

 

Thanks!

 

-jesse

Ok, so tell me….

 

 

iStock_SurveyXSmall

At the suggestion of a reader, I've created a survey to find out what topics are most important to you.

It won't be 100% scientific, and of course I can't guarantee instant responsiveness, but I really am interested in what you'd like to see more of, learn about, hear about and have us focus on. So if you have a moment, please do take the (very short) survey.

Thanks!

-jesse

On Meeting the Needs of Every User

I received a public flame recently that I've become complacent in this job and am not doing the hard work of meeting the needs of those who want to go beyond the basics.  While it is tempting to talk about 16 hour days, missed weekends with my family, 50 videos, 300 blog posts and yadda yadda,  no one rightly cares about all that. What is cared about is "How much is Microsoft committed to helping me meeting my needs to get my  work done?"

Fair enough.  You don't take this job without understanding that there will come a time in the life-cycle when you're going to have to simultaneously meet the needs of people who are just getting started, folks who are intermediate in their understanding and those who need advanced material.

My personal assessment is that we're getting better at meeting the needs of the first two groups, but we don't yet have a lot of material for the last group, though as we've said, we're working on that.  The fact that we don't isn't really all that surprising – the product is relatively new, so are we, and everything is a moving target. But so what? If it were easy no one would need us.

the dancerThus, I don't have much to offer you yet, except an open acknowledgement that you've been heard, that what you say has merit, and that we're working on it.

There is an old story: a man meets a woman in a bar. They start to dance. He says "I'm only here for the weekend." She says "I'm dancing as fast as I can."

Stay tuned.

 

-jesse

Throwing Together An Application

Creating a Quick and Dirty App in Silverlight

 There comes a point, usually within 3-6 months after I commit to a new technology, when it becomes my platform of choice for throwing together quick applications… when I just find it easier to think in terms of the new technology than one of the old.

I also find that it usually comes in two stages: first, it is easier to use the new technology for small illustrative programs, then it is easier for creating quick and dirty utilities, apps, whatever it is I need to get done.

Today I had my first such opportunity when my daughter needed a stop watch for her Mac. Rather than looking for one (I'm sure there are hundreds) it just seemed easier to throw it together using Silverlight. 

Total development time, including thinking, napkin design, layout in Blend, coding, debugging, and testing on Windows and Mac… 20 minutes. Not too bad.

Rachel timer on mac

One of the things I enjoyed about this project was that it reminded me how very cross-platform Silverlight really is. This was, of course, written and tested on Vista, and yet, when I ran it on the Mac, it looked just like a Mac program should look. I had to go back and run it on Vista to remind myself that it started out life looking like a Vista program!

Rachel timer on vista

That was kind of neat.

Start In Expression

Creating the program itself was painfully simple. I started out in Blend, and created a grid, then filled in the heading with a Textblock, added a row with three buttons, then four more text blocks. 

TimerProgramInBlend

 

Once you get over the idea that Blend "really isn't for our kind" – it is a gas. I make a point of using Comic Sans MS as I'm told it is a clear signal to any designer that I don't have a clue what I'm doing design-wise; it's the design equivalent of wearing a button that says "what is an object, anyway?"

…Proceed To Visual Studio

With the UI in place, and all the little UI objects named, I flipped over to Visual Studio to write the code to support the timer.

I won't walk you through every detail (unless you need the sleep) but briefly… I gave each button its own event handler (I'd probably consolidate this to a single handler if the program were any larger).  Taking them easy to harder…

The Pause button calls Pause( true) which disables the Pause button (so you can't press it twice), and sets a paused flag.

The Stop button disables both the pause and the stop button and sets the started flag to false and turns off the timer (timer is explained in just a moment). The net effect is to stop everything but leave the current elapsed time showing.

The start button has all the fun. It enables the Pause button and sets the startTime member variable to now (we keep track of two times, the time since the start button was first pressed, and the time since it was most recently pressed). It then calls a helper method named Start.

The helper method checks to see if this is the first time the start button has been pressed. If so it records the time (we use this for the total elapsed time) and enables the stop button and we reset the elapsed time and start up the timer.

The timer uses a System.Windows.ThreadingDispatcherTimer as explained here.

The start code (combined and cut down) looks like this:

   1: void startButton_Click( object sender, RoutedEventArgs e )
   2: {
   3:    // un-pause
   4:    Pause( false );  
   5:    // start a new elapsed time
   6:    startTime = DateTime.Now;  
   7:    Start();
   8: }
   9:  
  10: private void Start()
  11: {
  12:    // if first time started
  13:    if ( started == false )
  14:    {
  15:       started = true;
  16:       stopButton.IsEnabled = true;
  17:       firstStartTime = startTime;
  18:       ResetElapsed();
  19:       StartTimer();
  20:    }
  21: }
  22:  
  23: public void StartTimer()
  24: {
  25:     // set up timer with event handler
  26:     timer = new System.Windows.Threading.DispatcherTimer();
  27:     timer.Interval = new TimeSpan(0, 0, 0, 0, 100); 
  28:     timer.Tick += new EventHandler(TickTock);
  29:     timer.Start();
  30: }
  31:  
  32:  
  33: // event handler for timer (10 times/second)
  34: public void TickTock( object o, EventArgs sender )
  35: {
  36:    if ( !started )
  37:       return;
  38:  
  39:    totalElapsed = DateTime.Now - firstStartTime;
  40:    TimeSpan elapsed = notPausedElapsed;
  41:  
  42:    if ( !paused )
  43:    {
  44:       TimeSpan temp = DateTime.Now - startTime;
  45:       elapsed += temp;
  46:    }
  47:  
  48:    // display paused and total time
  49:    time.Text = elapsed.Hours.ToString() + ":" + elapsed.Minutes.ToString() + ":" + elapsed.Seconds.ToString();
  50:    totalTime.Text = totalElapsed.Hours.ToString() + ":" + totalElapsed.Minutes.ToString() + ":" + totalElapsed.Seconds.ToString();
  51: }

As you can see, no rocket-science; and very familiar; we've all done it a zillion times. But it works, and it works on the client, cross-browser and cross-platform. You can stream it or you can just drop the .xap and html file right on the machine so you don't have to be on line to use it. Sweet.

Here's the source code for this post: RachelTimer1.zip

 

Posted by jesseliberty | 6 comment(s)
Filed under: ,

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

Most Frequently Asked Question: When Will Silverlight Ship?

Dock at Camden ME

My way of acknowledging that you need to know, and we want to tell you as soon as we can

As an aside, about four months ago I started to buy photos to make my presentations “pop” (see Presentation Zen). 

As a side benefit, I’ve rediscovered just a hint of my old interest in photography – wow have things changed. In any case, unlike all the others I’ve been using; this one is mine. :-)

Next