I’ve just finished updating my modification of Joel’s original concept. Joel had a really great base for me to build off of and used styling and templates to create simple controls for a standard Silverlight media player that could be embedded. I took his sample and made some minor adjustments to accommodate automatic sizing as well as make it a bit more parameter-driven. The end result was (what I think at least) a fairly nice player that could be flexible if needed:
Joel made great use of styling primitive controls to be able to leverage core functionality rather than building it all himself. One of those controls used was the ToggleButton (the play/pause feature) which makes sense for the implementation. In the beta 1 version of styling we were able to use different styles for MouseOver states for checked/unchecked features. A snippet of the styling XAML looked like this:
<Storyboard x:Key="MouseOver Unchecked State">
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetName="playSymbol"
Storyboard.TargetProperty="Opacity" To="1" />
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetName="pauseSymbol"
Storyboard.TargetProperty="Opacity" To="0" />
This worked fine in beta 1. Beta 2 introduces the VisualStateManager model which is an exciting new feature for developers and designers. Opening the video player project in the latest tools had some things blow up for us…no problem, let’s make use of VSM to change the styling implementation.
One problem…the ToggleButton no longer supports MouseOver states specifically for Checked/Unchecked as we’ve implemented in the video player. My first thought (and a few emails to the team) was to inherit from the ToggleButton and do my own implementation, adding those states into the control. I was able to do this by creating a new class file in my Silverlight project, then inheriting from ToggleButton and a few overrides. I first wanted to support VSM so I added the appropriate TemplateParts to my class:
[TemplateVisualState(Name = "CheckedMouseOver", GroupName = "CheckStates"),
TemplateVisualState(Name = "CheckedMouseOut", GroupName = "CheckStates"),
TemplateVisualState(Name = "UncheckedMouseOver", GroupName = "CheckStates"),
TemplateVisualState(Name = "UncheckedMouseOut", GroupName = "CheckStates")]
public class ToggleButtonEnhanced : ToggleButton
This worked fine and they showed up in Blend designers as well for me to implement. I then chose to override the MouseEnter/Leave events and did something quick like this:
protected override void OnMouseEnter(MouseEventArgs e)
VisualStateManager.GoToState(this, "CheckedMouseOver", true);
else if ((bool)!base.IsChecked)
VisualStateManager.GoToState(this, "UncheckedMouseOver", true);
Fair enough right? Well it appeared to be working. The problem was that ToggleButton maintained a Normal state that was conflicting with certain scenarios I had in my implementation. You see the base.OnMouseEnter() was essentially the rathole here. Some spelunking showed that when a state changed it actualy went from the desired state, then back to ‘Normal’ – for example the Pressed action wend: MouseOver,Pressed,Normal. The final transition was causing my problem.
Now I was looking at overriding the OnApplyTemplate method and thus virtually having to re-implement all the other state transitions as well. Now I’m no longer really getting a huge benefit out of my inheritance. So I went back to some experts for some advice. Dave gave me some pointers and we chatted about the implementation and desired outcomes. Dave’s much smarter than me on VSM, well, because he had to implement it :-). For my particular scenario he pointed out that I really had only one property that was changing in the MouseEnter/Leave events: Opacity. So why not just change Opacity for the Grid container rather than worry about the elements. Duh.
So now I no longer needed my custom ToggleButton, but could use the primitive ToggleButton to do my dirty work. I implemented the MouseOver, Checked, and Unchecked states is all I really needed to manage my transitions. A few learnings about some things I was doing wrong and boom, the new player works as expected.
I learned a few things in this quick journey through VSMville, and one was that it was pretty easy to implement a custom control to support the VisualStateManager model as well. I think I’ll be digging into this one deeper soon.
Hope this helps someone! Here’s the code for the updated Video Player. While the ToggleButtonEnhanced is not used in the final implementation, I kept it in the code file so you could see what path I started along.