This article has been localized into German by the community.
How-to: Erstellung eines vollständigen Audio/Video Players
Zum Abschluss der vorhergehenden Kapitel über das Abspielen von Audio und Video habe ich mich entschieden ein vollständiges Beispiel zu erstellen. Hier werden wir auch ausnutzen, dass die MediaPlayer/MediaElement Klasse sowohl Video wie auch Audio abspielen kann.
Ich werde die Konzepte aus den Artikeln über Audio und Video aufgreifen und mit verschiedenen Controls, die wir schon in vorhergehenden Artikeln angesprochen haben, kombinieren und alles zu einem WPF Media Player zusammenfügen. Das Endergebnis wird in etwa so aussehen:
Aber das ist nur das Aussehen wenn Audio/MP3 abgespielt wird. Sobald ein Video geladen wird, expandiert das Fenster automatisch um das Video im Fenster anzuzeigen:
Lasst uns ein wenig darüber sprechen wie alles gebaut wurde. Am Ende kann man natürlich den kompletten Quellcode sehen, fertig für euch damit zu spielen.
Die Benutzeroberfläche
Die Benutzeroberfläche ist in 3 vertikale Bereiche aufgeteilt: - Top, wo die Toolbar angeordnet wird - Middle, wo das Video (falls eins geladen ist) angezeigt wird - Bottom, wo eine Statuszeile ist. Die Statuszeile besteht aus einem Status, einem Slider um den Fortschritt zu sehen und kontrollieren und einer ProgressBar um die Lautstärke anzuzeigen. Alle verwendeten Steuerelemente wurden bereits in vorangegangenen Artikeln behandelt und deshalb werden wir hier nicht mehr explizit darauf eingehen.
Beachtet die Verwendung von WPF Commands statt click events für die Buttons. Das erlaubt eine einfache Wiederverwendung der Funktionalitäten falls wir zum Beispiel ein Hauptmenü oder ein Kontextmenü mit den gleichen Funktionen hinzufügen wollen. Es macht es uns auch einfacher die Funktionalitäten je nach Status des Players ein- und auszuschalten.
Beachtet auch, das ie Eigenschaft Stretch des MediaElement auf None gesetzt wurde. Ebenso wurde das Window auf SizeToContentMode und WidthAndHeight gesetzt. Das bewirkt, dass das Fenster mit minimaler Größe, so wie grade benötigt, angezeigt wird, während ein Video abgespielt wird.
Um die Lautstärke anzuzeigen haben wir eine ProgressBar in der unteren rechten Ecke. Derzeit kann der Nutzer darüber keine Laustärke verändern, aber es zeigt die Volume Eigenschaft des MediaElement durch ein klassisches Binding an. Aber dennoch wurde durch einen kleine Trick implementiert, dass der Benutzer die Lautstärke dennoch verändern kann. Mehr dazu später.
Der Code
Im Code-behind verwenden wir mehrere Techniken aus unseren vorangegangenen Beispielen. Zum Beispiel initiieren wir einen DispatcherTimer und lassen ihn jede Sekunde zählen um den aktuellen Wiedergabestand anzeigen zu lassen. Im Tick-Event des Timers wird auch der Slider über die Eigenschaften Minimum, Maximum, je nach abgespielter Datei gesetzt. Über den Wert Value der mit dem Event ValueChanged verbunden wird, können wir die aktuelle Abspielzeit in Stunden, Minuten und Sekunden anzeigen.
Das Slider Control erlaubt es uns auch, durch ziehen der Schaltfläche, zu einem anderen Teil des Videos vorzuspulen. Das wird durch die Events DragStarted und DragCompleted abgearbeitet. Das erste Event wird genutzt um die Variable (userIsDraggingSlider) zu setzen, die dem Slider sagt, das während dem Vorgang des Ziehens nicht geupdatet werden soll. Das zweite Event um zu dem ausgewählten Punkt zu springen, an dem der User aufgehört hat den Slider zu ziehen um dort weiter zu schauen.
Es gibt CanExecute und Executed Handler für die vier Commands die benutzt werden. Hier sind Play und Pause besonders interessant, da wir keinen aktuellen Status durch das MediaElement erhalten und den aktuellen Status selbst nachverfolgen müssen. Dies wird über eine lokale Variable mediaPlayerIsPlaying gemacht, die wir regelmäßig abprüfen um zu sehen ob Pause oder Stop gedrückt wurden.
Das letzte Detail was man sich anschauen sollte ist das Grid_MouseWheel Event. Das Haupt Grid umfasst das gesamte Fenster und mit diesem Event kann man abfangen wenn der Nutzer das Mausrad benutzt. Als kleines extra für den Nutzer kann dieser somit die Lautstärke erhöhen oder verringern, je nachdem in welche Richtung das Mausrad gerollt wird. (Dies wird über den Delta Wert bestimmt, der negativ ist wenn man nach unten und positiv wenn man nach oben rollt). Die Aktion wird sofort auf dem UI dargestellt, wo ein ProgressBar an die Volume Eigenschaft des MediaElement gekoppelt ist.
Der komplette Quellcode
Mit der ganzen Theorie die wir durchlaufen haben, hier nun der komplette Quellcode:
<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;
}
}
}
Zusammenfassung
Der gesamte Code mag etwas erschlagend wirken, aber wenn man genauer hinschaut ist dort jede Menge Wiederholung aus vorangegangenen Kapiteln des Tutorials. Wenn man das in Betracht zieht sieht man schnell, dass die Erstellung eines Media Players in WPF doch nicht so schwer ist! Nehmt gerne das Beispiel in eure eigenen Projekte auf und erweitert es dort - vielleicht mir einer Playlist?