TOC

This article is currently in the process of being translated into Spanish (~55% done).

Audio y Vídeo:

How-to: Creating a complete Audio/Video player

Como conclusión a los capítulos anteriores sobre reproducción de vídeo y audio, he decidido crear un ejemplo más completo donde hacemos uso del hecho que las clases MediaPlayer/MediaElement pueden manejar tanto audio como vídeo.

Tomaré los conceptos utilizados en los artículos sobre reproducción de audio y vídeo y los combinaré con varios controles que hemos cubierto anteriormente en artículo, para convertirlo todo en un reproductor WPF. El resultado es algo parecido a esto:

Pero esto es solo cuando reproduces archivos de audio/MP3. Una vez un vídeo es cargado, la interfaz se expande automáticamente para mostrar el vídeo dentro de la ventana.

Dejadme explicaros un poco como ha sido construido. Al final podéis ver el código fuente, listo para jugar con él.

The interface

La interfaz ha sido dividida en tres áreas verticales: La superior, donde está localizada la barra de herramientas; la intermedia, donde el vídeo (si alguno ha sido cargado) se muestra; y la inferior, donde se encuentra la barra de estado, completa con un estado temporal, un Slider para ver y controlar el progreso y un ProgressBar para mostrar el volumen. Todos los controles utilizados han sido explicados previamente en este tutorial, así que no perderemos mucho tiempo en ello.

Fijaros en el uso de comandos WPF, en vez de eventos de click para los botones. Esto nos permitereutilizar la funcionalidad en caso que queramos añadir, por ejemplo, un menú principal o un menú de contexto con la misma funcionalidad. También nos facilita activar/desactivar la funcionalidad, dependiendo de el estado actual del reproductor.

Fijaos también que hemos seteado la propiedad del MediaElement Stretch a None, y la de la ventana SizeToContentMode a WidthAndHeight. Esto mantiene el tamaño mínimo de la ventana necesaria para mostrar la interfaz así como el vídeo, si alguno está siendo reproducido.

Para mostrar el volumen, hemos utilizado un control ProgressBar en la esquina inferior izquierda. Esto no permite al usuario controlar el volumen, simplemente refleja la propiedad Volume del MediaElement a través de un data binding clásico. Hemos implementado un pequeño truco ara permitir al usuario controlar el volumen - más sobre esto debajo.

The code

En el código debajo, reutilizamos varias técnicas ya utilizadas en ejemplos anteriores. Por ejemplo, iniciamos un DispatcherTimer y permite actualizarse cada segundo para mostrar el progreso de reproducción actual en la interfaz. En el evento Tick del contador, actualizamos el control Slider seteando Minimum, Maximum y Value relativos al archivo siendo reproducido; y registrándose al evento ValueChanged en el slider, actualizamos la etiqueta que muestra el progreso de reproducción actual en horas, minutos y segundos.

The Slider control also allows the user to skip to another part of the file, simply by dragging the "thumb" to another location. We handle this by implementing events for DragStarted and DragCompleted - the first one to set a variable ( userIsDraggingSlider) that tells the timer not to update the Slider while we drag, and the second one to skip to the designated part when the user releases the mouse button.

There are CanExecute and Executed handlers for the four commands we use and especially the ones for Pause and Stop are interesting. Since we can't get a current state from the MediaElement control, we have to keep track of the current state ourselves. This is done with a local variable called mediaPlayerIsPlaying, which we regularly check to see if the Pause and Stop buttons should be enabled.

The last little detail you should notice is the Grid_MouseWheel event. The main Grid covers the entire window, so by subscribing to this event, we get notified when the user scrolls the wheel. When that happens, as a little gimmick, we turn the volume up or down, depending on the direction (we get that by looking at the Delta property, which is negative when scrolling down and positive when scrolling up). This is immediately reflected in the user interface, where a ProgressBar control is bound to the Volume property of the MediaElement.

The complete source code

With all the theory behind the example described, here's the complete source code:

<Window x:Class="WpfTutorialSamples.Audio_and_Video.AudioVideoPlayerCompleteSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Media Player" Height="300" Width="300"
        MinWidth="300" SizeToContent="WidthAndHeight">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Open" CanExecute="Open_CanExecute" Executed="Open_Executed" />
        <CommandBinding Command="MediaCommands.Play" CanExecute="Play_CanExecute" Executed="Play_Executed" />
        <CommandBinding Command="MediaCommands.Pause" CanExecute="Pause_CanExecute" Executed="Pause_Executed" />
        <CommandBinding Command="MediaCommands.Stop" CanExecute="Stop_CanExecute" Executed="Stop_Executed" />
    </Window.CommandBindings>
    <Grid MouseWheel="Grid_MouseWheel">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ToolBar>
            <Button Command="ApplicationCommands.Open">
                <Image Source="/WpfTutorialSamples;component/Images/folder.png" />
            </Button>
            <Separator />
            <Button Command="MediaCommands.Play">
                <Image Source="/WpfTutorialSamples;component/Images/control_play_blue.png" />
            </Button>
            <Button Command="MediaCommands.Pause">
                <Image Source="/WpfTutorialSamples;component/Images/control_pause_blue.png" />
            </Button>
            <Button Command="MediaCommands.Stop">
                <Image Source="/WpfTutorialSamples;component/Images/control_stop_blue.png" />
            </Button>
        </ToolBar>

        <MediaElement Name="mePlayer" Grid.Row="1" LoadedBehavior="Manual" Stretch="None" />

        <StatusBar Grid.Row="2">
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </StatusBar.ItemsPanel>
            <StatusBarItem>
                <TextBlock Name="lblProgressStatus">00:00:00</TextBlock>
            </StatusBarItem>
            <StatusBarItem Grid.Column="1" HorizontalContentAlignment="Stretch">
                <Slider Name="sliProgress" Thumb.DragStarted="sliProgress_DragStarted"  Thumb.DragCompleted="sliProgress_DragCompleted" ValueChanged="sliProgress_ValueChanged" />
            </StatusBarItem>
            <StatusBarItem Grid.Column="2">
                <ProgressBar Name="pbVolume" Width="50" Height="12" Maximum="1" Value="{Binding ElementName=mePlayer, Path=Volume}" />
            </StatusBarItem>
        </StatusBar>
    </Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.Win32;

namespace WpfTutorialSamples.Audio_and_Video
{
	public partial class AudioVideoPlayerCompleteSample : Window
	{
		private bool mediaPlayerIsPlaying = false;
		private bool userIsDraggingSlider = false;

		public AudioVideoPlayerCompleteSample()
		{
			InitializeComponent();

			DispatcherTimer timer = new DispatcherTimer();
			timer.Interval = TimeSpan.FromSeconds(1);
			timer.Tick += timer_Tick;
			timer.Start();
		}

		private void timer_Tick(object sender, EventArgs e)
		{
			if((mePlayer.Source != null) && (mePlayer.NaturalDuration.HasTimeSpan) && (!userIsDraggingSlider))
			{
				sliProgress.Minimum = 0;
				sliProgress.Maximum = mePlayer.NaturalDuration.TimeSpan.TotalSeconds;
				sliProgress.Value = mePlayer.Position.TotalSeconds;
			}
		}

		private void Open_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			OpenFileDialog openFileDialog = new OpenFileDialog();
			openFileDialog.Filter = "Media files (*.mp3;*.mpg;*.mpeg)|*.mp3;*.mpg;*.mpeg|All files (*.*)|*.*";
			if(openFileDialog.ShowDialog() == true)
				mePlayer.Source = new Uri(openFileDialog.FileName);
		}

		private void Play_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = (mePlayer != null) && (mePlayer.Source != null);
		}

		private void Play_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			mePlayer.Play();
			mediaPlayerIsPlaying = true;
		}

		private void Pause_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = mediaPlayerIsPlaying;
		}

		private void Pause_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			mePlayer.Pause();
		}

		private void Stop_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = mediaPlayerIsPlaying;
		}

		private void Stop_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			mePlayer.Stop();
			mediaPlayerIsPlaying = false;
		}

		private void sliProgress_DragStarted(object sender, DragStartedEventArgs e)
		{
			userIsDraggingSlider = true;
		}

		private void sliProgress_DragCompleted(object sender, DragCompletedEventArgs e)
		{
			userIsDraggingSlider = false;
			mePlayer.Position = TimeSpan.FromSeconds(sliProgress.Value);
		}

		private void sliProgress_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
		{
			lblProgressStatus.Text = TimeSpan.FromSeconds(sliProgress.Value).ToString(@"hh\:mm\:ss");
		}

		private void Grid_MouseWheel(object sender, MouseWheelEventArgs e)
		{
			mePlayer.Volume += (e.Delta > 0) ? 0.1 : -0.1;
		}

	}
}

Summary

The code listing might look a bit overwhelming, but as you can see, there's a lot of repetition in it. If you take that out of the picture, you will soon realize that creating a pretty capable media player in WPF is really not that hard! Feel free to expand on this example for your own projects - how about implementing a playlist feature?

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!