Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Building Media Applications for the Universal Windows Platform (UWP) using the MediaPlayerElement

Over the past couple of years at Built to Roam we’ve built a number of media applications. Back when UWP was first launched, all media was played using the MediaElement, which was fairly familiar to XAML developers as it’s been the core element for any video or audio playback on Windows and Windows Phone for quite some time. Unfortunately the built in controls on the MediaElement were not easily stylable, resulting in most developers switching to using the Player Framework, which provided a host of capabilities out of the box such as closed captions, stylable regions and controls, and nice viewmodel architecture that made data binding easy.

Fast forward a year and a bit, and there’s a new player in town, literally. The MediaPlayerElement is a new control that shipped with the Anniversary update (v10.0.14393.0) and is definitely the control that you want to use if you want to do fancy things, such as a persistent video overlay where playback continues as the user continues to navigates throughout the application – this can be achieved by detaching the internal MediaPlayer from one MediaPlayerElement and attach it to another. It’s important to note that whilst the MediaPlayerElement has been around since the Anniversary update, features have been added in both the Creators update and more recently the Fall Creators update, so make sure you take a look at the documentation to make sure you are using features that align with the minimum and target versions you have set for your application.

In this post we’re going to look at the basics of using the MediaPlayerElement and how you can alter its styling. I’m going to start with a new UWP project, selecting the Creators update as the minimum version and the Fall Creators update as the target version. The MediaPlayerElement can be super simple to use – for example the following code is the simplest way to add video to a page that will automatically start playing.

<MediaPlayerElement Source="http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4"  AutoPlay="True"/>

By default the MediaPlayerElement doesn’t show any of the typical playback controls you’d expect, instead it’s just a raw video canvas that can be used to render video anywher on a page or control. In order to enable the default controls, you simply need to add the AreTransportControlsEnabled attribute set to true (I’ve removed the AutoPlay attribute as there are now going to be playback controls that can be used to start/stop video etc)

<MediaPlayerElement Source="http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4"  AreTransportControlsEnabled="True"/>

The following image shows the default controls with Progress Bar, Play/Pause, Volume, Aspect Ratio, Cast and Full screen buttons and as you’d imagine, they’re all connected to the appropriate action.

image

But what about other controls like fast forward/rewind, skip forward/backward etc. To customise what controls appear you need to override the TransportControls attribute on the MediaPlayerElement:

<MediaPlayerElement Source="http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4"
                     AreTransportControlsEnabled="True">
    <MediaPlayerElement.TransportControls>
         <MediaTransportControls IsSkipForwardButtonVisible="True"
                                 IsSkipBackwardEnabled="True"
                                 IsSkipBackwardButtonVisible="True"
                                 IsSkipForwardEnabled="True"
                                 IsFastForwardButtonVisible="True"
                                 IsFastForwardEnabled="True"
                                 IsFastRewindButtonVisible="True"
                                 IsFastRewindEnabled="True" />
     </MediaPlayerElement.TransportControls>

</MediaPlayerElement>

Here I’ve set the TransportControls attribute to be a new instance of the MediaTransportControls class – this is actually what the default behaviour does; the difference is that I now have access to an instance that I can set attributes on. Here I’m enabling, and showing both the buttons to skip forward/backward and the buttons for fast forward/rewind. The updated layout looks like the following:

image

As you can see from the image the skip forward and backward buttons default to 30 and 10 seconds respectively. In order to adjust these you’ll need to override the behaviour that is associated with the skip forward/backward buttons, which can be done by attaching an event handler to the PositionReceived event on the MediaPlaybackCommandManager:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
     base.OnNavigatedTo(e);

    Player.MediaPlayer.CommandManager.PositionReceived += CommandManager_PositionReceived;
}

private void CommandManager_PositionReceived(MediaPlaybackCommandManager sender, MediaPlaybackCommandManagerPositionReceivedEventArgs args)
{
     sender.MediaPlayer.PlaybackSession.Position = TimeSpan.FromMinutes(2);
     args.Handled = true;
}

In this case, I’ve named the MediaPlayerElement in the XAML (ie by adding x:Name=”Player” to the MediaPlayerElement), and then when the page has been navigated to I’m attaching the appropriate event handler. When either skip forward or backward is pressed, the position in the playback is set to the 2 minute mark and the args.Handled property is set to true, effectivey overriding the default skip behaviour. Big reminder: if you are attaching event handlers in code, make sure you detach them too (ie PositionReceived –= CommandManager_PositionReceived in the OnNavigatedFrom method).

The fast forward and fast rewind buttons will increment the playback speed up and down by doubling the rate over the range of rates supported by the content (eg –8, –4, –2, –1, 1, 2, 4, 8). The MediaTransportControls class is clever enough to detect the supported range of rates and automatically disable the fast foward/rewind buttons when the limit on the range has been reached.

Note: In working with the MediaPlayerElement, and even in preparing the sample code for this blog post, I was not able to get the fast rewind functionality to work. Fast foward appears to work but it causes the control bar to hide and show in a weird oscillating fashion. My recommendation would be to give these buttons a wide birth as this feature is severely under baked – if you want to implement something similar, perhaps with thumbnails, you should consider implementing it yourself!!! (and yes, we’ve had to do this on a project)

In this post, you’ve seen the basics of adding a MediaPlayerElment and playing some content using the built in media controls. I’ll go into more detail soon on really customising the style of your player.

Update: It seems that by setting the minimum version of the UWP application to the Fall Creators update, it appears that fast forward/rewind seems to work. I’m guessing it may have been fixed!

Comments are closed
Cross Platform Visual States

Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Cross Platform Visual States

Last year I posted on “Taking Visual States Cross Platform to iOS and Android with Xamarin” and is a topic that I often come back when discussing the design and development of mobile applications. Let’s start by discussing what visual states are and why they’re important when building applications (and this really applies to any application, not just mobile applications). During the design of an application it’s common to prepare wireframes and visual designs that document each page, the key elements on each page and any associated behaviour. This typically includes documenting when elements are hidden or shown, often in response to either data changes or user interactions. The following screenshots show a recent application we worked on for Hungry Jack’s for Windows 10. This is the same page of the same application, running on the same device, just with a different window size. As you can see the difference between the first two images is quite significant as the navigation bar switches from being at the bottom (similar to what you’d expect for mobile) to on the left side. The third image simply augments the position of elements further to make better use of the available screen size.

image image image

Thorough analysis during the design phase will reveal all possible layout combinations for a page; these combinations are what we refer to (at least in the Windows/XAML world) as visual states. In the case of the screenshots from the Hungry Jack’s application, each of these layouts represents a different visual state for this page. If you don’t take the time to determine what visual states exist on each page and what triggers a transition between visual states, during development you’ll find yourself toggling attributes on element on the page in an attempt to recreate each required combination. This lack of a structured approach makes it not only hard to layout each page, it also makes it hard to test as there is no definitive list of layouts that need to be verified.

On the Windows platform, Visual States are something we take for granted; they can be declared in XAML and Blend has support for designing each visual state. However, other platforms are not so blessed and have to resort to changing attributes manually in code. Some cross platform technologies make use of data binding to allow visual elements to be dynamically updated based on changes in the corresponding data (ie the view model). These include MvvmCross and Xamarin.Forms. However, data binding should be reserved for updating data values on a view, not controlling the visual states on a page.

Learning to develop for the Windows platforms, developers go through a series of learning steps.

  • Coding Changes: Most developers come from building applications or web sites where they’re used to having to set data values in code.
  • Data Binding: The first step along the progression is learning how to use data binding to update content on the page (eg Text on a Textblock)
  • MVVM: After seeing the benefit of data binding, the next step is to appreciate the separation of concerns that MVVM offers. At this point developers often look at what MVVM libraries there are out there and usually settle on something like MvvmCross, MvvmLight, Caliburn.Micro, Prism etc
  • Converters: Equipped with the new found power of data binding, developers often go nuts and start data binding everything, including using properties on the view model to control when items should be visible. This is where they look to use converters to adapt properties on the view model (eg XYZIsVisible which would be a bool) to attributes on visual elements (eg XYZ.Visibility which is a Visibility). The issue with this is that at design time, in a tool like Blend, it’s very difficult to see what the layout looks like. You can’t simply change the Visibility property on elements, since they’re now data bind. You can temporarily remove the data binding, but then of course you forget to put it back and then spend hours trying to work out why the application is broken.
  • Visual States: Enter Visual States…. instead of data binding attributes that control the layout of a page, it’s better to use visual states to define what elements are visible and any layout changes required for a particular layout. Blend supports design time editing of visual states and the ability to visualize any combination of visual states from different state groups
  • View Model States: Eventually developers realise that not only should they use visual states, they should track and control them from their view model, thus making yet another aspect of their application testable. I’ve talked about this a couple of times (http://nicksnettravels.builttoroam.com/post/2015/08/10/application-development-using-states-and-transitions.aspx, http://nicksnettravels.builttoroam.com/post/2014/01/11/Visual-States-in-Windows-Phone-and-Windows-Applications-using-MvvmCross.aspx, http://nicksnettravels.builttoroam.com/post/2014/05/19/Taking-Visual-States-Cross-Platform-to-iOS-and-Android-with-Xamarin.aspx)

Ok, so now that you have the basics on what a visual state is, and some background on why I believe visual states are so important, let’s discuss the elephant in the room….. Visual States only exist in XAML on the Windows platform…. making it very difficult to use visual states when building cross platform applications. So, what can we use when building cross platform? Well let’s go through the progression that developers go through. As you’d expect, all platforms support developers being able to adjust values via code. Unfortunately, this is where most developer technologies end, for example neither iOS (Objective-C, Swift) or Android (Java) support data binding out of the box. There are some third party solutions that attempt to bridge this gap, such as the data binding support in MvvmCross and Xamarin.Forms. In fact both these options provide not only the ability to data bind, but also enable MVVM and support using converters as part of data binding.

In actual fact there’s no requirement to have data binding (and subsequently MVVM and the use of converters) in order to start using visual states to control layout. However, again there’s almost no platform, or even third party, support for defining visual states.Over the weekend, I was experimenting with Xamarin.Forms and was thinking about how to define and transition between visual states. Whilst it would be nice to do it declaratively in XAML, I thought I’d better walk before I run, so I figured I’d work out a way to define visual states in code. Before getting started I thought though the basic mechanics of how visual states should work:

- Visual States should be declared in groups, and each group can only have one active visual state at any given time

- A visual state should define any number of value actions

- A “value action” defines setting a property on an element to a specific value

- The visual state manager should be able to change to a specific visual state

- Changing to a specific visual state, should only adjust the current state of the group that the visual state belongs

I’ve always felt that one of the weaknesses of Visual states on the XAML platform is that they’re named using a string, and the only way to reference them when changing state, is using a string literal. So, for my attempt at a visual state manager I’m going to have my visual states defined as an enumeration. In fact, each group of states will use a different enumeration type – thus each visual state corresponds to a unique enumeration value. The end game is to be able to declare visual states in a relatively fluid manner, as shown in the following example which defines two groups based on the enumerations SecondStates and SecondStates2.

VisualStateManager
    .Group<SecondStates>()
        .DefineState(SecondStates.State1)
        .DefineState(SecondStates.State2)
            .Target(textBlock)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFF008B"))
            .Target(textBlock)
                .Change(x => x.FontSize, (x, c) => x.FontSize= c)
                .ToValue(40)
        .DefineState(SecondStates.State3)
            .Target(textBlock)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFFC500"))
            .Target(textBlock)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(10)
        .DefineState(SecondStates.State4)
    .Group(SecondStates2.Base)
        .DefineState(SecondStates2.StateX)
        .DefineState(SecondStates2.StateY)
            .Target(textBlock2)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFF008B"))
            .Target(textBlock2)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(40)
        .DefineState(SecondStates2.StateZ)
            .Target(textBlock2)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFFC500"))
            .Target(textBlock2)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(10);

In my next post we’ll look at the different classes that make up the visual state manager and the extension methods that allow for the fluid declaration seen in this example.

Pingbacks and trackbacks (2)+

Comments are closed