If you’ve been following my Twitter or you happen to know me, you might know that I’m developing an app for Windows 8/ Windows RT. I’ll leave the announcements for another day, but just know this: it’s gon’ be good.
—
One of the main problems I had when starting out on my app was when trying to play audio in the background – i.e. both when the app is running and when suspended. Googling for a complete guide was (and is) futile, so I’m going to share what I came up with in this post.
In WinRT, the way to play media is using the MediaElement. It’s a visual object, which means it needs to be instantiated in the XAML in order to work. This makes sense, given that the object is capable of playing both audio and video, but causes problems when navigating away from the page the MediaElement is on – for obvious reasons. So how can we use the MediaElement when we’re not on the page it was created on? There are a couple of workarounds out there, but essentially they’re the same thing. The answer to the question is: put the element on every page at the same time.
The simplest way to do this is to create an intermediary page between App.xaml and the first page we navigate to and stick the MediaElement in there. The cleanest (and my preferred) way is to use a style on the App.xaml’s Frame, as detailed by JimMan over on this Answers Thread:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<Style x:Key="RootFrameStyle" TargetType="Frame"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Frame"> <Grid> <MediaElement x:Name="MediaPlayer" AudioCategory="BackgroundCapableMedia" AutoPlay="True" /> <ContentPresenter /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> |
We stick that XAML in our StandardStyles.xaml file (and merge it with LayoutRootStyle if it’s still around) – notice the AudioCategory attribute “BackgroundCapableMedia.” We then put this line in App.xaml.cs, just before we navigate to our first page:
|
1 2 3 4 |
RootFrame = Window.Current.Content as Frame; // create the media element for background audio RootFrame.Style = Resources["RootFrameStyle"] as Style; |
Finally, we can play media in the codebehind of any page, using this:
|
1 2 3 4 |
var rootGrid = VisualTreeHelper.GetChild(Window.Current.Content, 0); var mediaElement = (MediaElement)VisualTreeHelper.GetChild(rootGrid, 0); mediaElement.Source = new Uri("ms-appx:///relative/path/to/file.mp3"); |
If we try out the above, we find we can play media throughout our app. To enable actual background audio we have to tell Windows that our app can support it. All we need to do is edit our Package.appxmanifest file and declare we use background audio in the Declarations tab, like so:
So now we can play songs, audiobooks, podcasts and what have you both throughout our app and when our app is in the background (note: to test this, we need to use the suspend and resume debugging states in Visual Studio – the app isn’t actually in the background when we debug).
But WAIT! There’s more we have to do to make it maintainable from a development perspective. Don’t worry, it’s simple.
[Hopefully your app is built according to the MVVM pattern - i.e. your code isn't all in .xaml.cs files. If not, go fix that before continuing with the post, because it won't apply to your app.]
As mentioned earlier, the MediaElement is something which has to be present in the visual tree in order to work. We solved that by applying a style containing a MediaElement to our app’s root frame, but there’s another problem we didn’t come across: how can we control the media from a page which has no visual tree – like a model or viewmodel? We might need to do this if we want our users to be able to use their play/ pause button or play a playlist without requiring their input.
Going back to this code:
|
1 2 3 4 |
var rootGrid = VisualTreeHelper.GetChild(Window.Current.Content, 0); var mediaElement = (MediaElement)VisualTreeHelper.GetChild(rootGrid, 0); mediaElement.Source = new Uri("ms-appx:///relative/path/to/file.mp3"); |
We’ll find that it won’t work in a viewmodel. A solution to this is keeping the MediaElement in a static field in your App.xaml.cs class -
|
1 |
public static MediaElement Player; |
- having all of your MediaElement event handlers as static methods in the same file, like so -
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// most of the handlers omitted for brevity - we need to support Play, Pause, PlayPauseToggle and Stop for any of them to work, however private static async void MediaControlPlayPressed(object sender, object e) { var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher; await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Player.Play()); } public static void SetUpBackgroundAudio() { MediaControl.PlayPressed += MediaControlPlayPressed; //MediaControl.PausePressed += MediaControlPausePressed; //MediaControl.PlayPauseTogglePressed += MediaControlPlayPauseTogglePressed; //MediaControl.StopPressed += MediaControlStopPressed; } |
- and setting up the static MediaElement – but from our first page (i.e. the first we navigate to).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public MainPage() { vm = new MainPageViewModel(); DataContext = vm; InitializeComponent(); this.Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { var rootGrid = VisualTreeHelper.GetChild(Window.Current.Content, 0); App.Player = (MediaElement)VisualTreeHelper.GetChild(rootGrid, 0); App.SetUpBackgroundAudio(); } |
That should be all we need to play, pause, skip, shuffle and everything else we could possibly want to do in the background! Any questions or opinions you can leave in the comment section below.
—
P.S. a quick note on these lines:
|
1 2 |
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher; await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Player.Play()); |
This runs the code Player.Play(); on the UI thread. The reason this is necessary is again down to the MediaElement needing to be in the visual tree – so be wary of any heavy code you put in this lambda as it can easily gum up your UI if you’re not careful.
P.P.S. and keep an eye out for my app, from which the above code samples were taken, coming out in the next month or so. It really is gon’ be good.

