On the discussion board of the Player Framework there were a number of discussions about specific UI customizations. To have some more samples how to create a sample UI on top of the player framework I actually created one. If you look at it, it looks a bit like a well-known player. Of course there are lots of other UI elements in this sample one to enable all the Player Framework goodness. This is what the player looks like (it’s an image, not the actual player):
This sample UI was completely done in XAML without any code-behind. I want to highlight a few things I found while creating this.
Use of colors-definition or a resource
In the session I gave on customizing the UI of the Player Framework I already mentioned the use of color-definitions and resources is not consistent in the default template of the SMFPlayer control. I found a small clue why that happened in specific cases. When you use a resource brush for a color, it’s impossible to change that to another color in a storyboard or a visual state. This can only be done with color-definitions. A way to work around this is to use various objects (for instance rectangles for the background of a button), use various brushes as resource per background and make only that background visible in a specific state that you need. This is done partially in the default template, that’s why some elements have objects like background, background_hover and background_press. In my sample UI I used the same construct everywhere to define objects with resources. That way you just need to change a resource to make all the color changes.
A good tip when you start drawing the visuals for a player, is to also draw the various states of objects and group them in layers. This will translate in a Canvas or a Grid that contains the various objects. That way you just have to decide how to show and hide them in states. You can use the Visibility property if you just want to change immediately, or use Opacity if you want to animate the transition (e.g. cross-fade).
The time track
A challenge was the time track (the red transparent bar with the circle shape thumb, called the TimelineElement in the template). I wanted to place this outside of the ControllerContainer and I wanted to change it’s shape when we kind of ‘hide’ the controllers when the mouse hasn’t been moved for 5 seconds. As the look and feel of the time track, which is actually just a slider control, is defined in a template, how would you change it with an animation? The answer had to be: Visual States.
First I moved the TimelineElement on the same level as the ControllerContainer under PlayerRoot. MediaPresenterElement is in Row 0 and has a RowSpan of 3. ControllerContainer is positioned in Row 3 and TimelineElement in Row 2. That way the TimelineElement hides the bottom part of the video, which isn’t a problem if it’s transparent.
After that I changed the template of the TimelineElement. I shaped the HorizontalTrackLargeChangeIncreaseRepeatButton, the HorizontalAvailableBar and the HorizontalTrackLargeChangeDecreaseRepeatButton to define the tracks. I made sure that these tracks take the Height defined by their container (HorizontalTemplate). I also shaped the HorizontalThumb to be a circle.
In the shapes for the TimelineElement I added a new group called ActiveStates and I created the Active and Inactive state within that group. The Active state defines what the TimelineElement looks like when everything is visible, the Inactive when … well, it’s not active (sounds logical, doesn’t it). In the Inactive state I changed the Height of the HorizontalTemplate to 6 pixels and I set the Visibility of the HorizontalThumb to Collapsed. I used 0,1 second to do the transition.
The next thing was to trigger the animations. I have also defined a white transparent rectangle inside the ControllerContainer to cover the controls when the mouse wasn’t active for 5 seconds. To make that happen I created a storyboard called AutoDimControllers. At 0,1s the Visibility of the rectangle is set to Collapsed, at 5s the Visibility is set to Visible and the Opacity to 0% and at 5,2s the Opacity is set to 40%. To trigger this I added two ControlStoryboardAction behaviors to the PlayerRoot, both playing the AutoDimControllers storyboard, one on the Loaded event the other on the MouseMove event. The last one is needed to start the animation over and over again when the mouse is moved, so it always stays up for 5 seconds.
But a visual state cannot be set with an animation. So I added 3 GoToStateAction behaviors to the PlayerRoot. The first GoToStateAction fires on the Loaded event of PlayerRoot and sets the visual state “Active” of the TimelineElement, to set the initial state on load. The seconds GoToStateAction fires on the MouseMove event of PlayerRoot, and sets the visual state “Active” of the TimelineElement, to make it visible again when the mouse is moved. The third one uses a Trigger of type StoryboardCompletedTrigger on AutoDimControllers, and it sets the visual state “Inactive” of the TimelineElement.
One strange this is that Blend starts complaining about these states that they cannot be resolved. Don’t get frightened by this, it just works.
Now everything is hooked up to get the controls out of the way (or less intrusive) when the user is watching the video.
The volume control
The volume control expands horizontally. This was all handled by defining the visuals and changing the states within the volume control.
The complete styling is in a separate resource file. If you want to reuse it in another project, just add the resource XAML file as an existing item (a link to this resource is automatically added in App.xaml) and a a reference to Microsoft.Expression.Interactions.dll. If you don’t know how to locate the dll, just add a behavior to the LayoutRoot of your main page and remove that again. The reference is then set automatically.
Let me know what you think or miss.