预览

播放器Github地址

最终效果

最终效果

技术问题

1.选择文件

在页面的中间设计了一个按钮,它的作用是打开一个文件选择框,我使用搜索引擎搜索关键字 uwp 文件选择 通过老师制作的学习助手进入到了uwp文档。

因为我们需要仅仅播放 mp3 和 mp4 文件,所以 FileTypeFilter 需要添加两个后缀名,通过一个异步函数获取到选择的文件路径,播放媒体文件的代码放在示例代码中注释的位置。

资料链接 fileopenpicker#examples

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");

StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
    // Application now has read/write access to the picked file
    OutputTextBlock.Text = "Picked photo: " + file.Name;
}
else
{
    OutputTextBlock.Text = "Operation cancelled.";
}

我的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private async System.Threading.Tasks.Task openFileDialogAsync()
        {
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            openPicker.FileTypeFilter.Add(".mp3");
            openPicker.FileTypeFilter.Add(".mp4");
            StorageFile file = await openPicker.PickSingleFileAsync();
            if (file != null)
            {
                //在这里把获取到的文件发送给播放器
            } 
            else
            {
                text_file.Text = "未选择文件";
            }
        }

2.播放媒体文件

同样的手段,我又找到了如何播放媒体文件,代码很简单,将文件地址通过 CreateFromStorageFile 函数赋值给 MediaPlayerElement 的 MediaSource,再通过在xaml中设置AutoPlay为True,就可以实现播放了。

资料链接 小威威__’s blog

示例代码

1
2
3
4
5
 if (file != null) {
                var mediaSource = MediaSource.CreateFromStorageFile(file);
                mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;  
                _mediaPlayer.Source = mediaSource;
            }

我的代码

1
2
3
4
5
6
7
8
 
 if (file != null){//在这里把获取到的文件发送给播放器
      video_player.Source = MediaSource.CreateFromStorageFile(file);
      } 
 else{
      text_file.Text = "未选择文件";
 }

1
2
3
4
5
6
7
8
9
   <MediaPlayerElement 
            Name="video_player"
            AutoPlay="True"
            Grid.Row="0" 
            Grid.Column="0"
            Grid.RowSpan="3"
            Grid.ColumnSpan="3"
            Visibility="Collapsed"
            AreTransportControlsEnabled="True"/>

3.监听播放结束

由于希望在播放结束时候自动隐藏播放器,并将按钮显示出来,所以需要监听播放器播放结束事件,通过查阅文档我发现了这样一段话。

资料链接 handle-media-events

Handle media events

You can respond to common media events located on the underlying MediaPlayer such as MediaOpened, MediaEnded, and MediaFailed. If you have set the source to a MediaPlaybackItem or MediaPlaybackList, you should respond to the media events on those classes instead as they provide more information.

通过这段话,我们可以看出,可以监听 MediaPlayer 的 MediaEnded 事件来在播放器结束的时候做点什么,因为是初学c#,对这个语言中的event不是十分熟悉,通过分析实现的效果,看出有些类似java中interface的功能。

资料链接 c# event

示例代码节选

1
2
3
4
5
6
7
8
9
10
11
12
  public class MainClass
  {
    public static void Main()
    {
      EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
      subscribEvent v = new subscribEvent(); /* 实例化对象 */
      e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
      e.SetValue( 7 );
      e.SetValue( 11 );
    }
  }
}

我的代码

1
2
3
video_player.MediaPlayer.MediaEnded += new TypedEventHandler<Windows.Media.Playback.MediaPlayer, object>((player,obj)=> {
                   //在这里写播放停止后的操作 
                });

这里将事件函数写成了匿名函数的形式让代码看起来更加简洁。

4.在非UI线程中更新UI

当我在event的回调函数中直接修改UI组件的可视属性时,发现程序运行时报出了一个信息为“应用程序调用一个已为另一线程整理的接口”的错误,经过搜索引擎搜索,发现可能原因是c#中的event是开启了一个新的线程的,这和java中的interface貌似有区别,因为不能在其他线程中操作UI线程中的内容,所以在event回调函数中不能直接操作UI组件,但是我们可以调用Diapatcher将修改组件的信息传给UI线程。

资料链接 jiahuafu’s blog

资料链接 CoreDispatcher

示例代码

1
2
3
4
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
   rootPage.NotifyUser("The toast encountered an error", NotifyType.ErrorMessage);
});

我的代码

1
2
3
4
5
6
7
8
video_player.MediaPlayer.MediaEnded += new TypedEventHandler<Windows.Media.Playback.MediaPlayer, object>((player,obj)=> {
                   //在这里写播放停止后的操作 
button_open.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() => {
         button_open.Visibility = Visibility.Visible;
         video_player.Visibility = Visibility.Collapsed;
                    });
  
                }); 

总结

这次编写uwp的活动让我收获最大的就是学会了如何在其他线程更新UI组件,异步函数的使用方法也让人眼前一亮。

在编译过程中,因为有时是离线操作所以对老师的编译助手能否在有线时重新上传编译信息持怀疑态度。希望老师可以上线自查功能,让每个人都能看见自己编译了多少个组件。