-
音频与视频:

如何创建完整的音频/视频播放器

在音频视频播放的最后几章,我决定做一个更完整的示例,利用MediaPlayer / MediaElement类来处理音频和视频。

在前几章播放音频和视频基础上,我们将其中用到的几个控件组合起来,显示到一个WPF媒体播放器上,如下图所示:

但是这只是播放音频是它所展示的状态。一旦载入视频,这个界面里面就会自动展开一块区域来显示视频的内容,如下图所示:

让我来告诉你这个东西是怎样做出来的吧!在这篇文章最后,你能看到完整的源代码,你准备好了吗?

界面

这个接口可以分为三块区域:顶部放置工具条,中间放置视频(如果视频导入的话),下方放置一个状态栏,里面用一个文本控件来显示播放时间,一个Slider来显示和控制播放进度,用一个ProgressBar来显示音量。所有的这些控件在之前的教程已经介绍过,所有我们不会过多的描述它。

注意要将功能包装在单独的函数中,不是直接写在按钮的点击事件上。这样可以很容易地复用一些功能而不用再次添加它,比如说一些主菜单或者右键菜单上具有相同功能的选项。这样也很容易根据当前播放器状态来控制功能的开关。

同时请注意我们已经设置MediaElement控件的Stretch属性为None,Window 窗体的SizeToContentMode属性为WidthAndHeight,这样就能使窗体根据内容保持最小尺寸。

为了显示音量,我们在右下角添加了一个ProgressBar 控件。 它不能让用户控制音量,而只是通过数据绑定的方式反映MediaElement控件上的Volume属性。 我们已经实现了一个小而巧妙的方法,让用户无论如何都能控制音量,更多内容请继续观看。

代码

在后端代码中,我们重复使用了前面的几个例子中的技术。比如说,我们初始化了一个DispatcherTimer,让他每秒响应一次来实时显示播放进度。在响应事件中,我们通过设置MinimumMaximum和当前文件已播放Value更新Slider控件。并通过滑块上的ValueChanged事件更新文本标签的时分秒。

滑块控件也允许用户跳到文件的另一部分,只要简单的拖拽"thumb"到另一个位置,我们利用DragStartedDragCompleted来实现这个功能,首先是要设置一个变量userIsDraggingSlider告诉计时器当我们拖拽的时候不要更新Slider;当用户释放鼠标按键时,要跳转到指定位置。

我们使用的四个功能命令都有CanExecuteExecuted处理程序,特别是Pause和Stop的命令很有意思。 由于我们无法从MediaElement控件获取当前状态,因此我们必须自己跟踪当前状态。 这是通过局部变量mediaPlayerIsPlaying完成的,我们会定期检查是否应启用“暂停”和“停止”按钮。

你应该注意的最后一个细节是Grid_MouseWheel事件。 主网格Grid覆盖整个窗口,因此通过此事件,我们会在用户滚动滚轮时收到通知。 当发生这种情况时,作为一个小技巧,我们根据方向上调或下调音量(我们通过查看Delta属性得到它,向下滚动时为负,向上滚动时为正)。 这会立即反映在用户界面中,其中ProgressBar控件绑定到MediaElement的Volume属性。

完整源代码

根据上述所有理论,下面是对应的完整源代码:

<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;
		}

	}
}

小结

代码清单可能看起来有点压抑,但正如您所看到的,其中有很多重复。 如果你把它从图片中拿出来,你很快就会意识到在WPF中创建一个非常强大的媒体播放器并不是那么难! 您可以随意为自己的项目扩展此示例 ,接下来如何实现播放列表的功能呢?