How to create custom media transport controls (XAML)
[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]
Create custom transport controls for a MediaElement to control and manage audio and video media playback in a Windows Runtime app using C++, C#, or Visual Basic. We'll go over how to how to create basic transport controls, such as handling play, stop and pause, enable full-screen video playback, set up a slider control and connect it to the media position, and enable support for selecting multiple audio tracks.
Windows 8.1 introduces built in transport controls for the MediaElement. They handle play, stop, pause, volume, mute, seeking/progress, and audio selection. To enable theses controls, set AreTransportControlsEnabled to true. To disable them, set AreTransportControlsEnabled to false.
Windows 8.1 introduces a IsFullWindow property to enable and disable full window rendering. This insures that system optimizations are correctly implemented. In Windows 8.1 and later, you should always use the IsFullWindow property to enable and disable full window rendering.
Roadmap: How does this topic relate to others? See:
- Roadmap for Windows Runtime apps using C# or Visual Basic
- Roadmap for Windows Runtime apps using C++
- To see this feature in action as part of a complete media-playback sample, see Media playback, start to finish.
Tip
Much of the code in this topic is based on the XAML media playback sample code.
Important
This topic focuses mainly on how to implement these features and does not address many of the UI design considerations for creating a Windows Runtime app using C++, C#, or Visual Basic. For more info about designing Windows Runtime apps, see Designing UX for apps.
Tip For simplicity, this topic uses a video file that is embedded with the app. To include a media file in an app, add the file to your Microsoft Visual Studio project and set the Build Action field in the file's properties to Content. In a real-world app, you would probably load a file over the network or use a FileOpenPicker object to enable the user to choose a file from the local file system. For info about how to use FileOpenPicker, see Quickstart: Accessing files with file pickers or the XAML media playback sample.
Prerequisites
This topic assumes that you know how to create a basic Windows Runtime app using C++, C#, or Visual Basic. For help in creating your first app, see Create your first Windows Store app using C# or Visual Basic. For a basic introduction to audio and video, see Quickstart: Video and audio.
Basic MediaElement
The MediaElement class plays audio and video. MediaElement exposes a rich set of methods, properties, and events to control media playback. To make this functionality available to the user, you can use the built in transport controls introduced in Windows 8.1 by setting the AreTransportControlsEnabled property to true. If you need a custom UI or need to add support that the built in controls do not enable, you can create the UI and add event handlers to interact with the MediaElement object.
The next section explains how to create a simple set of transport controls, but we'll start by showing how to create a basic MediaElement in Extensible Application Markup Language (XAML) and how to use the AutoPlay feature to automatically start the media playback when the app loads.
The Source property of the MediaElement object points to an audio or video file. The property can be set to the Uniform Resource Identifier (URI) of a file that is included with the app or the URI of a file on the network. You can use the SetSource method to set the source to a file retrieved from the local system by using a FileOpenPicker object.
AutoPlay specifies whether to begin playing the media file as soon as the MediaElement loads. The default value of AutoPlay is true.
Tip By default, MediaElement opts in to the Play To experience. For more info, see the Streaming media to devices using Play To.
This code creates a MediaElement with the AutoPlay property explicitly set to true and the Source set to the path of a video file that is included in the app.
<MediaElement Source="Media/video1.mp4" AutoPlay="True" />
Next, we wrap the MediaElement inside a ContentControl object. We do this to enable the full-screen mode feature, which is covered later in this topic.
Important In Windows 8.1 this is not necessary. Always use IsFullWindow to enable full window rendering.
This code creates the ContentControl and MediaElement. Notice that both controls are given names so we can easily access them in code. Event handlers are set for the MediaOpened, MediaEnded, CurrentStateChanged, and MediaFailed events. It is good practice to always handle the MediaFailed event. Finally, a value for the PosterSource property is set. A PosterSource is an image, such as a screen shot or movie poster, that is displayed in place of the media. It provides your MediaElement with a visual representation before the media is loaded. The PosterSource is displayed in the following situations:
- When a valid source is not set. For example, Source is not set, Source was set to Null, or the source is invalid (as is the case when a MediaFailed event fires).
- While media is loading. For example, a valid source is set, but the MediaOpened event has not fired yet.
- During Play To streaming.
<ContentControl x:Name="videoContainer"
KeyUp="VideoContainer_KeyUp"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="400" Grid.Row="0" >
<MediaElement Name="videoMediaElement"
Source="Media/video1.mp4"
MediaOpened="videoElement_MediaOpened"
MediaEnded="videoMediaElement_MediaEnded"
MediaFailed="videoMediaElement_MediaFailed"
CurrentStateChanged="videoMediaElement_CurrentStateChanged"
PosterSource="Media/Video1_Poster.png"
AutoPlay="False" />
</ContentControl>
Custom transport controls
Transport controls are UI controls that interact with the MediaElement and control media playback. Examples of transport controls are the play, stop, and pause buttons found on many media player apps.
The MediaElement class exposes a rich set of properties and methods, such as Play, Stop, Pause, Position, Volume, IsMuted, IsLooping, PlaybackRate, and Balance. For a complete list, see the MediaElement reference API.
To implement transport controls for this topic, we do the following:
- Create Button objects to interact with the MediaElement. This will be the UI to play, pause, stop, fast forward, rewind, increase the volume, decrease the volume, and mute the media.
- Include UI for full-screen mode and language selection (which is covered later in this topic).
- Use a simple Style element to create a uniform look for our buttons. The Style sets a value for only its Height, Width, and FontSize properties, but it gives you an idea of what you can do with styles. For guidelines for creating a UI, see the Designing UX for apps. And for more info about using styles, see Quickstart: styling controls.
- Create a TextBlock element to display the current volume.
- Data bind the TextBlock to the MediaElement.Volume property to display the current volume.
This code shows the style that is used for the buttons. You can place the Style element in the Resources section of the Page. The full XAML for this example is included for reference at the end of this topic, so feel free to jump ahead if you want to see how some of these pieces go together.
<Page.Resources>
<Style x:Name="transportStyle" TargetType="Button">
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="75" />
<Setter Property="FontSize" Value="11" />
</Style>
</Page.Resources>
The next code example shows the XAML for the Button objects. Each Button sets a Name property, a Style, a handler for the Click event, and a text string for the Content property that is displayed on the Button. (In a real-world app you may want to use images instead of text strings to depict the functionality of the buttons.) The ComboBox control also appears in this code, and is discussed later in this topic; it's used to switch between multiple audio tracks.
<StackPanel Orientation="Horizontal">
<Button Name="btnPlay" Click="btnPlay_Click"
Style="{StaticResource transportStyle}" Content="Play" />
<Button Name="btnPause" Click="btnPause_Click"
Style="{StaticResource transportStyle}" Content="Pause" />
<Button Name="btnStop" Click="btnStop_Click"
Style="{StaticResource transportStyle}" Content="Stop" />
<Button Name="btnReverse" Click="btnReverse_Click"
Style="{StaticResource transportStyle}" Content="Rewind" />
<Button Name="btnForward" Click="btnForward_Click"
Style="{StaticResource transportStyle}" Content="Forward" />
<Button Name="btnMute" Click="btnMute_Click"
Style="{StaticResource transportStyle}" Content="Mute" />
<Button Name="btnFullScreenToggle" Click="btnFullScreenToggle_Click"
Style="{StaticResource transportStyle}" Content="Full" />
<ComboBox Name="cbAudioTracks"
SelectionChanged="cbAudioTracks_SelectionChanged"
Width="75" />
<Button Name="btnVolumeUp" Click="btnVolumeUp_Click"
Style="{StaticResource transportStyle}" Content="-" />
<Button Name="btnVolumeDown" Click="btnVolumeDown_Click"
Style="{StaticResource transportStyle}" Content="+" />
<TextBlock Name="txtVolume" FontSize="14"
Text="{Binding Volume, ElementName=videoMediaElement}"
VerticalAlignment="Center" HorizontalAlignment="Right" />
</StackPanel>
Most of the event handlers are straightforward.
Button | Click handler action |
---|---|
Stop | Calls the Stop method. |
Pause | Calls the Pause method |
Fast forward | Sets the value of the DefaultPlaybackRate property to 2.0. You can adjust this value to increase or decrease the rate of the fast-forward. Then the handler calls the Play method. |
Rewind | Sets the value of the DefaultPlaybackRate property to -2.0. You can adjust this value to increase or decrease the rate of the rewind. The handler then calls the Play method. |
Play | If the DefaultPlaybackRate is not 1.0, sets the DefaultPlaybackRate to 1.0. The handler then calls the Playmethod. |
Mute | Toggles the IsMuted property between true and false. |
Volume up, Volume down | If IsMuted is true, unmutes the audio. Then the handler increments or decrements the Volume property by 0.1. The volume levels range from 0.0 to 1.0. Note that the data binding on the TextBlock control takes care of updating the volume display. |
private void btnPlay_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.DefaultPlaybackRate != 1)
{
videoMediaElement.DefaultPlaybackRate = 1.0;
}
videoMediaElement.Play();
}
private void btnPause_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.Pause();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.Stop();
}
private void btnForward_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.DefaultPlaybackRate = 2.0;
videoMediaElement.Play();
}
private void btnReverse_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.DefaultPlaybackRate = -2;
videoMediaElement.Play();;
}
private void btnVolumeDown_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.IsMuted)
{
videoMediaElement.IsMuted = false;
}
if (videoMediaElement.Volume < 1)
{
videoMediaElement.Volume += .1;
}
}
private void btnMute_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.IsMuted = !videoMediaElement.IsMuted;
}
private void btnVolumeUp_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.IsMuted)
{
videoMediaElement.IsMuted = false;
}
if (videoMediaElement.Volume > 0)
{
videoMediaElement.Volume -= .1;
}
}
Full-screen
Important In Windows 8.1 and later, use the IsFullWindow property to enable and disable full window rendering. This insures that system optimizations are correctly implemented.
Prior to Windows 8.1, to enable full-screen video playback, and take advantage of system optimizations, you must set the Width and Height of the MediaElement to the Window.Bounds of the current window. Specifically, use Window.Current.Bounds.Width and Window.Current.Bounds.Height.
To prevent the display from be deactivating when user action is no longer detected, such as when an app is playing full-screen video, you can call DisplayRequest.RequestActive. To conserve power and battery life, you should call DisplayRequest.RequestRelease to release the display request when it is no longer required. See the Display power state sample for more info.
To enable full-screen video playback, do this:
- Hide all the UI elements in the app. In this example, all of the UI elements are contained within a StackPanel control named TransportControlsPanel. Set the Visibility property of the panel to Visibility.Collapsed.
- Set the Width and Height properties of the ContentControl and the Width and Height of the MediaElement to the maximum bounds of the display. This makes the height and width of the MediaElement the same size as the window. Remember to save the current Width and Height first, though, so you can restore the video to the correct size when the app exits full-screen mode. Then you can set the dimensions of the ContentControl and MediaElement to the Window.Bounds of the current window. Specifically, use Window.Current.Bounds.Width and Window.Current.Bounds.Height.
To exit full-screen mode and restore the app to its normal state, do this:
Listen to the keyboard events to detect when the user wants to exit full-screen mode. The Esc key is typically used to exit full-screen mode. Add an event handler on the ContentControl to listen to the KeyUp event. In the KeyUp event handler, if the app is in full-screen mode and the pressed key is Windows.System.VirtualKey.Escape, exit full-screen mode.
In a real-world app listen also to the Manipulation touch events to handle touch gestures, such as swipe, to exit full screen mode. For more info about handling touch input in XAML, see Quickstart: Touch input.
Restore the visibility of the UI elements by setting the Visibility property of the transport controls to Visibility.Visible.
Restore the Width and Height of the ContentControl and MediaElement to its previous dimensions.
This code shows the implementation for the full-screen mode. A boolean property called IsFullscreen stores the current state. A Size property stores the original dimensions of the ContentControl. A KeyUp event handler enables leaving full-screen mode. Finally, a method called FullscreenToggle
switches between full-screen and normal modes.
// You should use the MediaElement.IsFullWindow property instead
// to enable and disable full window rendering.
private bool _isFullscreenToggle = false;
public bool IsFullscreen
{
get { return _isFullscreenToggle; }
set { _isFullscreenToggle = value; }
}
private Size _previousVideoContainerSize = new Size();
private void FullscreenToggle()
{
this.IsFullscreen = !this.IsFullscreen;
if (this.IsFullscreen)
{
TransportControlsPanel.Visibility = Visibility.Collapsed;
_previousVideoContainerSize.Width = videoContainer.ActualWidth;
_previousVideoContainerSize.Height = videoContainer.ActualHeight;
videoContainer.Width = Window.Current.Bounds.Width;
videoContainer.Height = Window.Current.Bounds.Height;
videoMediaElement.Width = Window.Current.Bounds.Width;
videoMediaElement.Height = Window.Current.Bounds.Height;
}
else
{
TransportControlsPanel.Visibility = Visibility.Visible;
videoContainer.Width = _previousVideoContainerSize.Width;
videoContainer.Height = _previousVideoContainerSize.Height;
videoMediaElement.Width = _previousVideoContainerSize.Width;
videoMediaElement.Height = _previousVideoContainerSize.Height;
}
}
private void btnFullScreenToggle_Click(object sender, RoutedEventArgs e)
{
FullscreenToggle();
}
private void VideoContainer_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (IsFullscreen && e.Key == Windows.System.VirtualKey.Escape)
{
FullscreenToggle();
}
e.Handled = true;
}
Slider progress bar
A Slider is an ideal object to display and change, or scrub, the video position. In this section we set up a Slider and show how to use a DispatcherTimer object to keep the slider in sync with the MediaElement.Position property. Setting up the synchronization to enable this feature is more complex than the other features added in this topic, so it may be helpful to look at the full listing of the code at the end of the topic from time to time. The built in transport controls introduced in Windows 8.1 include a slider to control media position.
For more info about using the Slider control, see How to add a slider.
The basic steps to create the Slider are:
- Create a Slider object in XAML.
- Create a DispatcherTimer object.
- Set the StepFrequency and Maximum properties on the Slider.
- Set up event handlers for changed events on the Slider, MediaElement, and pointer events.
The first step is to create the Slider object in XAML. Specify a Name on the Slider to access it easily in code. The StepFrequency and Maximum properties are set in code later in this section.
This code shows the XAML for the Slider.
<Slider Name="timelineSlider" Margin="10,0" Width="200"/>
The StepFrequency property of the Slider defines the frequency of the steps on the slider's scale. In this example, the StepFrequency is based on the value of the NaturalDuration property of the MediaElement. Note that this is just a simple example to illustrate how to set the StepFrequency. If you need finer fidelity of the slider thumb movement, tweak these numbers with the lowest frequency set to 250 milliseconds.
This code shows the method to set the StepFrequency based on the duration of the media.
private double SliderFrequency(TimeSpan timevalue)
{
double stepfrequency = -1;
double absvalue = (int)Math.Round(
timevalue.TotalSeconds, MidpointRounding.AwayFromZero);
stepfrequency = (int)(Math.Round(absvalue / 100));
if (timevalue.TotalMinutes >= 10 && timevalue.TotalMinutes < 30)
{
stepfrequency = 10;
}
else if (timevalue.TotalMinutes >= 30 && timevalue.TotalMinutes < 60)
{
stepfrequency = 30;
}
else if (timevalue.TotalHours >= 1)
{
stepfrequency = 60;
}
if (stepfrequency == 0) stepfrequency += 1;
if (stepfrequency == 1)
{
stepfrequency = absvalue / 100;
}
return stepfrequency;
}
A DispatcherTimer object keeps the Slider in sync with the media. The Interval is set to the StepFrequency of the Slider. For each timer tick, the Value property of the Slider is set to the MediaElement.Position. Helper methods are created to set up, stop, and start the timer.
You can optimize the performance of your app by shutting the timer off under certain conditions, but remember to restart the timer when the condition changes again. Shut the timer off when:
- The CurrentState has changed to paused or stopped.
- The slider thumb is moved.
- The media has ended.
- The progress bar is not visible, such as in full-screen mode. Again, remember to restart the timer after the progress bar returns to the screen.
This code shows how to create and set up the DispatcherTimer.
private DispatcherTimer _timer;
private void SetupTimer()
{
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(timelineSlider.StepFrequency);
StartTimer();
}
private void _timer_Tick(object sender, object e)
{
if (!_sliderpressed)
{
timelineSlider.Value = videoMediaElement.Position.TotalSeconds;
}
}
private void StartTimer()
{
_timer.Tick += _timer_Tick;
_timer.Start();
}
private void StopTimer()
{
_timer.Stop();
_timer.Tick -= _timer_Tick;
}
To keep the Slider in sync with the MediaElement.Position we also need to handle a number of different events. The Loaded event of the main page and the MediaOpened event trigger handlers to perform basic setup tasks. When the CurrentStateChanged and MediaEnded events fire, handle state changes on the MediaElement. On the ValueChanged event, handle state changes on the Slider. And finally, the PointerPressedEvent and PointerCaptureLostEvent event handlers process user interactions with the Slider.
This code shows the Loaded and MediaOpened event handlers. In the Loaded event handler of the main page, the Slider and pointer event handlers are set up. The MediaOpened event handler sets the Slider object's Maximum and StepFrequency properties, and a helper function is called to set up the timer. Finally, a helper method is called to populate the list of audio languages, which is covered in the next section.
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
timelineSlider.ValueChanged += timelineSlider_ValueChanged;
PointerEventHandler pointerpressedhandler = new PointerEventHandler(slider_PointerEntered);
timelineSlider.AddHandler(Control.PointerPressedEvent, pointerpressedhandler, true);
PointerEventHandler pointerreleasedhandler = new PointerEventHandler(slider_PointerCaptureLost);
timelineSlider.AddHandler(Control.PointerCaptureLostEvent, pointerreleasedhandler, true);
}
void videoElement_MediaOpened(object sender, RoutedEventArgs e)
{
double absvalue = (int)Math.Round(
videoMediaElement.NaturalDuration.TimeSpan.TotalSeconds,
MidpointRounding.AwayFromZero);
timelineSlider.Maximum = absvalue;
timelineSlider.StepFrequency =
SliderFrequency(videoMediaElement.NaturalDuration.TimeSpan);
SetupTimer();
// Helper method to populate the combobox with audio tracks.
PopulateAudioTracks(videoMediaElement, cbAudioTracks);
}
Finally, this code shows the event handlers for pointer position changes and for the ValueChanged, CurrentStateChanged, and MediaEnded events.
private bool _sliderpressed = false;
void slider_PointerEntered(object sender, PointerRoutedEventArgs e)
{
_sliderpressed = true;
}
void slider_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
{
videoMediaElement.Position = TimeSpan.FromSeconds(timelineSlider.Value);
_sliderpressed = false;
}
void timelineSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (!_sliderpressed)
{
videoMediaElement.Position = TimeSpan.FromSeconds(e.NewValue);
}
}
void videoMediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if (videoMediaElement.CurrentState == MediaElementState.Playing)
{
if (_sliderpressed)
{
_timer.Stop();
}
else
{
_timer.Start();
}
}
if (videoMediaElement.CurrentState == MediaElementState.Paused)
{
_timer.Stop();
}
if (videoMediaElement.CurrentState == MediaElementState.Stopped)
{
_timer.Stop();
timelineSlider.Value = 0;
}
}
void videoMediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
StopTimer();
timelineSlider.Value = 0.0;
}
private void videoMediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
// get HRESULT from event args
string hr = GetHresultFromErrorMessage(e);
// Handle media failed event appropriately
}
private string GetHresultFromErrorMessage(ExceptionRoutedEventArgs e)
{
String hr = String.Empty;
String token = "HRESULT - ";
const int hrLength = 10; // eg "0xFFFFFFFF"
int tokenPos = e.ErrorMessage.IndexOf(token, StringComparison.Ordinal);
if (tokenPos != -1)
{
hr = e.ErrorMessage.Substring(tokenPos + token.Length, hrLength);
}
return hr;
}
The preceding code may be a little hard to follow because we've broken it up for the sake of discussion. To see it all together in a full listing, look at the end of this How to topic.
Multiple audio tracks
If a video file contains multiple audio tracks, you can use the following members of MediaElement to switch among the different tracks.
Member | Description |
---|---|
AudioStreamCount | Returns the number of audio tracks. |
AudioStreamIndex | Gets or sets the current audio track. Setting AudioStreamIndex to null selects the default audio track, which is defined by the content. |
GetAudioStreamLanguage | Returns the language of the audio. The language of the track is identified by a language code. |
We present one example of how to change the audio track here. For another example, see How to select audio tracks in different languages.
Here we use the ComboBox that we created earlier to enable switching among different audio tracks. We create a method called PopulateAudioTracks
, which gets the audio tracks from the MediaElement, and then we create a new item in the ComboBox for each track. We also create a SelectionChanged event handler to change the current audio track when the user requests it by interacting with the ComboBox.
This code shows the implementation for changing the audio track.
private void cbAudioTracks_SelectionChanged(
object sender, SelectionChangedEventArgs e)
{
videoMediaElement.AudioStreamIndex = cbAudioTracks.SelectedIndex;
}
private void PopulateAudioTracks(
MediaElement media, ComboBox audioSelection)
{
if (media.AudioStreamCount > 0)
{
for (int index = 0; index < media.AudioStreamCount; index++)
{
ComboBoxItem track = new ComboBoxItem();
track.Content = media.GetAudioStreamLanguage(index);
audioSelection.Items.Add(track);
}
}
}
Performance notes
Audio and video playback can be an expensive operation. Here are a few performance guidelines to keep in mind while using audio and video in your Windows Runtime app using C++, C#, or Visual Basic. For more info about media app performance, see Optimize media resources.
Display full-screen video playback when possible
The XAML framework can optimize rendering video when the video is the only thing being rendered, as is the case with full-screen playback. This approach uses less power and yields higher frame rates than if you display other elements at the same time. To optimize media playback, set the size of a MediaElement object to the width and height of the screen and don’t display other XAML elements. There are legitimate reasons to overlay XAML elements on top of a MediaElement in full-screen mode—for example, closed captions or momentary transport controls—but hide these elements when they are not needed.
Delay setting the MediaElement source
The XAML framework delays loading DLLs and creating large objects for as long as possible. The MediaElement does this work when the source is initially set by the Source property or the SetSource method. Setting the source only after the user is ready to play the media delays the majority of the performance cost associated with the MediaElement.
Match video resolution with device resolution
Decoding video is memory and GPU intensive, so choose a video format close to the resolution at which it will be displayed. For example, decoding a high-definition (1080) video and then scaling it down to a much smaller size for display uses resources unnecessarily. Many apps don’t have the same video encoded at different resolutions, but if it is available, use an encoding that is close to the resolution of the display device.
Don't animate MediaElement objects
Animation and media playback can both be costly in terms of system resources. Animation, used in the right context, can create beautiful, stunning effects. But, for the sake fo performance, avoid animating MediaElement objects in your Windows Runtime app using C++, C#, or Visual Basic.
Full XAML code
Following is the full listing of the XAML code for the example in this How to topic.
<Page
x:Class="MediaPlayerQuickStart.MainPage"
IsTabStop="false"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MediaPlayerQuickStart"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="MainPage_Loaded">
<Page.Resources>
<Style x:Name="transportStyle" TargetType="Button">
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="75" />
<Setter Property="FontSize" Value="11" />
</Style>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<ContentControl x:Name="videoContainer"
KeyUp="VideoContainer_KeyUp"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="400" Grid.Row="0" >
<MediaElement Name="videoMediaElement"
Source="Media/video1.mp4"
MediaOpened="videoElement_MediaOpened"
MediaEnded="videoMediaElement_MediaEnded"
MediaFailed="videoMediaElement_MediaFailed"
CurrentStateChanged="videoMediaElement_CurrentStateChanged"
PosterSource="Media/Video1_Poster.png"
AutoPlay="False" />
</ContentControl>
<!-- Transport Controls -->
<StackPanel Name="TransportControlsPanel"
HorizontalAlignment="Center"
Grid.Row="1" >
<Slider Name="timelineSlider" Margin="10,0" Width="200"/>
<StackPanel Orientation="Horizontal">
<Button Name="btnPlay" Click="btnPlay_Click"
Style="{StaticResource transportStyle}" Content="Play" />
<Button Name="btnPause" Click="btnPause_Click"
Style="{StaticResource transportStyle}" Content="Pause" />
<Button Name="btnStop" Click="btnStop_Click"
Style="{StaticResource transportStyle}" Content="Stop" />
<Button Name="btnReverse" Click="btnReverse_Click"
Style="{StaticResource transportStyle}" Content="Rewind" />
<Button Name="btnForward" Click="btnForward_Click"
Style="{StaticResource transportStyle}" Content="Forward" />
<Button Name="btnMute" Click="btnMute_Click"
Style="{StaticResource transportStyle}" Content="Mute" />
<Button Name="btnFullScreenToggle" Click="btnFullScreenToggle_Click"
Style="{StaticResource transportStyle}" Content="Full" />
<ComboBox Name="cbAudioTracks"
SelectionChanged="cbAudioTracks_SelectionChanged"
Width="75" />
<Button Name="btnVolumeUp" Click="btnVolumeUp_Click"
Style="{StaticResource transportStyle}" Content="-" />
<Button Name="btnVolumeDown" Click="btnVolumeDown_Click"
Style="{StaticResource transportStyle}" Content="+" />
<TextBlock Name="txtVolume" FontSize="14"
Text="{Binding Volume, ElementName=videoMediaElement}"
VerticalAlignment="Center" HorizontalAlignment="Right" />
</StackPanel>
</StackPanel>
</Grid>
</Page>
Full C# Code
Following is the full listing of the C# code. Note that, for educational purposes, some related code is placed together. This may not be the ideal way to structure your code in a real app.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
// You should use the MediaElement.IsFullWindow property instead
// to enable and disable full window rendering.
private bool _isFullscreenToggle = false;
public bool IsFullscreen
{
get { return _isFullscreenToggle; }
set { _isFullscreenToggle = value; }
}
private Size _previousVideoContainerSize = new Size();
private void FullscreenToggle()
{
this.IsFullscreen = !this.IsFullscreen;
if (this.IsFullscreen)
{
TransportControlsPanel.Visibility = Visibility.Collapsed;
_previousVideoContainerSize.Width = videoContainer.ActualWidth;
_previousVideoContainerSize.Height = videoContainer.ActualHeight;
videoContainer.Width = Window.Current.Bounds.Width;
videoContainer.Height = Window.Current.Bounds.Height;
videoMediaElement.Width = Window.Current.Bounds.Width;
videoMediaElement.Height = Window.Current.Bounds.Height;
}
else
{
TransportControlsPanel.Visibility = Visibility.Visible;
videoContainer.Width = _previousVideoContainerSize.Width;
videoContainer.Height = _previousVideoContainerSize.Height;
videoMediaElement.Width = _previousVideoContainerSize.Width;
videoMediaElement.Height = _previousVideoContainerSize.Height;
}
}
private void btnFullScreenToggle_Click(object sender, RoutedEventArgs e)
{
FullscreenToggle();
}
private void VideoContainer_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (IsFullscreen && e.Key == Windows.System.VirtualKey.Escape)
{
FullscreenToggle();
}
e.Handled = true;
}
private void btnPlay_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.DefaultPlaybackRate != 1)
{
videoMediaElement.DefaultPlaybackRate = 1.0;
}
videoMediaElement.Play();
}
private void btnPause_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.Pause();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.Stop();
}
private void btnForward_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.DefaultPlaybackRate = 2.0;
videoMediaElement.Play();
}
private void btnReverse_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.DefaultPlaybackRate = -2;
videoMediaElement.Play();;
}
private void btnVolumeDown_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.IsMuted)
{
videoMediaElement.IsMuted = false;
}
if (videoMediaElement.Volume < 1)
{
videoMediaElement.Volume += .1;
}
}
private void btnMute_Click(object sender, RoutedEventArgs e)
{
videoMediaElement.IsMuted = !videoMediaElement.IsMuted;
}
private void btnVolumeUp_Click(object sender, RoutedEventArgs e)
{
if (videoMediaElement.IsMuted)
{
videoMediaElement.IsMuted = false;
}
if (videoMediaElement.Volume > 0)
{
videoMediaElement.Volume -= .1;
}
}
private void cbAudioTracks_SelectionChanged(
object sender, SelectionChangedEventArgs e)
{
videoMediaElement.AudioStreamIndex = cbAudioTracks.SelectedIndex;
}
private void PopulateAudioTracks(
MediaElement media, ComboBox audioSelection)
{
if (media.AudioStreamCount > 0)
{
for (int index = 0; index < media.AudioStreamCount; index++)
{
ComboBoxItem track = new ComboBoxItem();
track.Content = media.GetAudioStreamLanguage(index);
audioSelection.Items.Add(track);
}
}
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
timelineSlider.ValueChanged += timelineSlider_ValueChanged;
PointerEventHandler pointerpressedhandler = new PointerEventHandler(slider_PointerEntered);
timelineSlider.AddHandler(Control.PointerPressedEvent, pointerpressedhandler, true);
PointerEventHandler pointerreleasedhandler = new PointerEventHandler(slider_PointerCaptureLost);
timelineSlider.AddHandler(Control.PointerCaptureLostEvent, pointerreleasedhandler, true);
}
void videoElement_MediaOpened(object sender, RoutedEventArgs e)
{
double absvalue = (int)Math.Round(
videoMediaElement.NaturalDuration.TimeSpan.TotalSeconds,
MidpointRounding.AwayFromZero);
timelineSlider.Maximum = absvalue;
timelineSlider.StepFrequency =
SliderFrequency(videoMediaElement.NaturalDuration.TimeSpan);
SetupTimer();
// Helper method to populate the combobox with audio tracks.
PopulateAudioTracks(videoMediaElement, cbAudioTracks);
}
private bool _sliderpressed = false;
void slider_PointerEntered(object sender, PointerRoutedEventArgs e)
{
_sliderpressed = true;
}
void slider_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
{
videoMediaElement.Position = TimeSpan.FromSeconds(timelineSlider.Value);
_sliderpressed = false;
}
void timelineSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (!_sliderpressed)
{
videoMediaElement.Position = TimeSpan.FromSeconds(e.NewValue);
}
}
void videoMediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if (videoMediaElement.CurrentState == MediaElementState.Playing)
{
if (_sliderpressed)
{
_timer.Stop();
}
else
{
_timer.Start();
}
}
if (videoMediaElement.CurrentState == MediaElementState.Paused)
{
_timer.Stop();
}
if (videoMediaElement.CurrentState == MediaElementState.Stopped)
{
_timer.Stop();
timelineSlider.Value = 0;
}
}
void videoMediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
StopTimer();
timelineSlider.Value = 0.0;
}
private void videoMediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
// get HRESULT from event args
string hr = GetHresultFromErrorMessage(e);
// Handle media failed event appropriately
}
private string GetHresultFromErrorMessage(ExceptionRoutedEventArgs e)
{
String hr = String.Empty;
String token = "HRESULT - ";
const int hrLength = 10; // eg "0xFFFFFFFF"
int tokenPos = e.ErrorMessage.IndexOf(token, StringComparison.Ordinal);
if (tokenPos != -1)
{
hr = e.ErrorMessage.Substring(tokenPos + token.Length, hrLength);
}
return hr;
}
private DispatcherTimer _timer;
private void SetupTimer()
{
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(timelineSlider.StepFrequency);
StartTimer();
}
private void _timer_Tick(object sender, object e)
{
if (!_sliderpressed)
{
timelineSlider.Value = videoMediaElement.Position.TotalSeconds;
}
}
private void StartTimer()
{
_timer.Tick += _timer_Tick;
_timer.Start();
}
private void StopTimer()
{
_timer.Stop();
_timer.Tick -= _timer_Tick;
}
private double SliderFrequency(TimeSpan timevalue)
{
double stepfrequency = -1;
double absvalue = (int)Math.Round(
timevalue.TotalSeconds, MidpointRounding.AwayFromZero);
stepfrequency = (int)(Math.Round(absvalue / 100));
if (timevalue.TotalMinutes >= 10 && timevalue.TotalMinutes < 30)
{
stepfrequency = 10;
}
else if (timevalue.TotalMinutes >= 30 && timevalue.TotalMinutes < 60)
{
stepfrequency = 30;
}
else if (timevalue.TotalHours >= 1)
{
stepfrequency = 60;
}
if (stepfrequency == 0) stepfrequency += 1;
if (stepfrequency == 1)
{
stepfrequency = absvalue / 100;
}
return stepfrequency;
}
}
Related topics
Roadmaps
Roadmap for Windows Runtime apps using C# and Visual Basic
Roadmap for Windows Runtime apps using C++
Samples
Media playback, start to finish
Tasks
How to select audio tracks in different languages
How to open media files using the FileOpenPicker control
How to play media files from the network
Reference
Other resources