Updated Silverlight Control Tutorial for Silverlight 2 Beta 2

I've finally gotten the Silverlight Control tutorial sample updated for Silverlight 2 Beta 2.

There have been some non-trivial changes to how controls are written between Beta 1 and Beta 2.  My goals were to do more than a straight port.  I wanted to take advantage of some of these changes as an example of how to use the new functionality, and I wanted to address some feedback I've gotten from the original post.  Some of the ways I chose to do things in the original post were overly complicated, and I took this opportunity to streamline things a bit.  The result is the same functionality with a lot less code.

Default template

The major change centers around the Visual State Manager.  When my team and I first started working with Silverlight, we found the existing templating model to be difficult and confusing.  The model of defining all of your animations as key-framed timelines was work intensive and difficult to get right.  In addition, the model of having parts and states defined in the same way led to what we called "state explosion".  This occurred with controls like the CheckBox when you get into permutations of states - "Checked Focused MouseOver", "Checked Unfocused MouseOver"..."Unchecked Unfocused MouseOver"..."Checked Disabled"....you get the idea.  Each time you added another axis, you added another state to define and it quickly got out of control.  I think CheckBox had 18 states at one point.  Not good.

We brought this up with the Blend team and they came up with a great solution to the problem: Visual State Manager.  Not only did this simplify things by allowing the system to compute may of the transitions, but it also allowed Blend to implement a MUCH nicer tooling experience (Tim has a great overview here).   Doing this conversion was the first time I'd used this in action and the difference is dramatic.  I'm so glad we were able to do this.   We got together with the Blend and Silverlight teams and workout out a way to get it into the platform.  It's a huge win.

The other changes are simplifications and updates due to changes in the Silverlight platform (there weren't many of these).

Basically, in my original sample, I wasn't taking advantage of binding properly.  So I had some properties and parts I didn't need.  Moreover, I committed some "no-nos" like including code in my CLR property wrapper for accessing a Dependency Property.  Fixing these issues up really smoothed things along.

And, finally, in the app.xaml, I included to very basic templates to show what a simple template would look like.  One collapses horizontally, the other vertically:

Horizontal Collapse vertical collapse

To give you an idea how much things are simplified using VSM. here are some before and after code samples:

Beta1:

        protected override void OnApplyTemplate()

        {

            base.OnApplyTemplate();

            // Fish out all of the template children and wire them up.            //

       _rootElement = GetTemplateChild(ExpandoHeaderControl.RootElement) as FrameworkElement;

            if (_rootElement != null)

            {

                bool opened = IsOpened;

                _buttonElement = GetTemplateChild(ExpandoHeaderControl.ButtonElement) as ToggleButton;

                if (_buttonElement != null)                {

                    // setup the buttons initial state.

                    //

                    _buttonElement.IsChecked = opened;

                    _buttonElement.Click += new RoutedEventHandler(ToggleButtonElement_Click);

                }

                _openAnimation = _rootElement.Resources[ExpandoHeaderControl.OpenAnimation] as Storyboard;

                _closeAnimation = _rootElement.Resources[ExpandoHeaderControl.CloseAnimation] as Storyboard;

            }

            ChangeVisualState();

            _templateLoaded = true;

        }

        private void ChangeVisualState()        {

  // manage the animations base on the current state.

            //

            Storyboard toState = IsOpened ? _openAnimation : _closeAnimation;

            Storyboard fromState = !IsOpened ? _openAnimation : _closeAnimation;

            if (toState != null)            {

                if (!_templateLoaded)                {

                    toState.SkipToFill();

                }

                else                {

                    toState.Begin();

                }

            }

            if (fromState != null)            {

                fromState.Stop();

            }

        }

Beta 2:

        public override void OnApplyTemplate()        {          

            base.OnApplyTemplate();

            // fetch the button and hook up to it's click event.

            //

            _buttonElement = GetTemplateChild(ExpandoHeaderControl.ButtonElement) as ToggleButton;

            if (_buttonElement != null)            {

                _buttonElement.Click += new RoutedEventHandler(ToggleButtonElement_Click);

            }           

            // set up our initial state

            //

            ChangeVisualState(false);

        }

        private void ChangeVisualState(bool useTransitions)        {

            // Tell VSM to manage the animations based on the current state.

            //

            VisualStateManager.GoToState(this, IsOpened ? NormalState : ClosedState, useTransitions);

   }

Much simpler!  Note the call to VSM in the ChangeVisualState method.

The other changes to call out.  To get generic.xaml to work, you need to specify a DefaultStyleKey in your constructor:

        public ExpandoHeaderControl()        {       

            DefaultStyleKey = typeof(ExpandoHeaderControl);

        }

Also note OnApplyTemplate is now public.

Other than that the process is basically the same as documented in the prior tutorial.  Inside of the generic.xaml file, you'll see how I templated the ToggleButton.  The animation on ToggleButton took me a long time on Beta 1, and I never got it quite right.  With the Blend June Preview that supports VSM, this was a snap.

HeaderB2.zip