在第二个窗口中显示视频(UWP C#)

时间:2018-06-18 19:55:26

标签: c# uwp

我有一个我正在处理的应用程序,我想要一个窗口作为控制器,另一个窗口是视频播放器视图。

这样做的目的是在视频在投影机上播放时,从我可以在笔记本电脑上看到的一个窗口控制视频播放。最终我想设置它以便我可以使用键盘在视频播放器视图中播放一组16个视频,当我按下与特定视频相关联的键(通过选择器选择)时停止当前播放的视频)并开始新视频。

请注意,如果有更好的语言和/或系统来创建此Windows应用程序,我会接受任何超出我要求的建议。

这是我的MainPage.xaml.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace VideoSamplePlayer
{

    public sealed partial class MainPage : Page
    {
        int newViewID = 0;

        Window VideoPlayerWindow;

        public MainPage()
        {
            this.InitializeComponent();

        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            if (newViewID == 0) {
                var myView = CoreApplication.CreateNewView();

                await myView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    Frame newFrame = new Frame();
                    newFrame.Navigate(typeof(VideoPlayer), null);

                    Window.Current.Content = newFrame;
                    Window.Current.Activate();
                    VideoPlayerWindow = Window.Current;

                    newViewID = ApplicationView.GetForCurrentView().Id;
                });

                await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewID, ViewSizePreference.UseMinimum);
            }
        }
    }
}

这是我的MainPage.xaml:

<Page
    x:Class="VideoSamplePlayer.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:VideoSamplePlayer"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Button Content="Open Video Output" HorizontalAlignment="Center" Margin="0,55,0,0" VerticalAlignment="Top" Height="52" Width="173" Click="Button_Click"/>

    </Grid>
</Page>

这是我的VideoPlayer.xaml.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Core;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace VideoSamplePlayer
{
    public sealed partial class VideoPlayer : Page
    {           
        public VideoPlayer()
        {
            this.InitializeComponent();
        }
        private async void pickFileButton_Click(object sender, RoutedEventArgs e)
        {

            // Create and open the file picker
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;
            openPicker.FileTypeFilter.Add(".mp4");
            openPicker.FileTypeFilter.Add(".mkv");
            openPicker.FileTypeFilter.Add(".avi");

            StorageFile file = await openPicker.PickSingleFileAsync();
            if (file != null)
            {
                mediaPlayerElement.MediaPlayer.Source = MediaSource.CreateFromStorageFile(file);
                mediaPlayerElement.MediaPlayer.RealTimePlayback = true;
                mediaPlayerElement.MediaPlayer.Play();
            }
        }
    }
}

这是我的VideoPlayer.xaml:

<Page
    x:Class="VideoSamplePlayer.VideoPlayer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:VideoSamplePlayer"
    xmlns:mediacore="using:Windows.Media.Core"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <StackPanel Orientation = "Vertical" >

            <StackPanel Orientation="Horizontal">
                <Button x:Name="pickFileButton" Content="Pick video" Margin="0,0,0,10" Click="pickFileButton_Click"/>
            </StackPanel>
            <MediaPlayerElement x:Name="mediaPlayerElement" 
                                AutoPlay="False" Margin="5" 
                                HorizontalAlignment="Stretch" 
                                Height="300" 
                                AreTransportControlsEnabled="True"/>
        </StackPanel>
    </Grid>
</Page>

我想将文件选择器按钮移动到MainPage,但仍然在VideoPlayer中有视频输出。

我该怎么做?

谢谢!

1 个答案:

答案 0 :(得分:2)

玩了一下,但终于找到了解决方案!诀窍是使用自定义事件......而棘手的部分是我们正在玩多个线程(每个窗口都在自己的线程中运行)。

基本上,我们将您的按钮及其处理程序移动到MainPage,但保留与VideoPlayer页面上的视频播放器交互的行。然后我们需要声明一个自定义事件,允许MainPage告诉VideoPlayer选择了视频,并将选定的视频传递到VideoPlayer页面。最后,VideoPlayer页面可以负责设置其中包含的实际MediaPlayerElement的来源和详细信息,并实际播放视频。

让我们来看看每个部分:

首先在MainPage上声明自定义事件:

public delegate void VideoSelectedHandler(object sender, VideoSelectionArgs e);
public event VideoSelectedHandler VideoSelected;

private void RaiseVideoSelectedEvent(MediaSource source)
{         
    // Ensure that something is listening to the event.
    if (this.VideoSelected != null)
    {
        // Create the args, and call the listening event handlers.
        VideoSelectionArgs args = new VideoSelectionArgs(source);
        this.VideoSelected(this, args);
    }
}

您还需要声明事件参数的类(并且适当地,它继承自EventArgs。您可以在同一个文件中声明它(但在MainPage类之外)或者另一个文件:

public class VideoSelectionArgs : EventArgs
{
    public MediaSource Source { get; private set; }

    public VideoSelectionArgs(MediaSource source)
    {
        this.Source = source;
    }
}

接下来,我们需要VideoPlayer页面来订阅该事件,以便它正在侦听正在引发的事件。将事件处理程序添加到VideoPlayer页面:

public void VideoSelected(object sender, VideoSelectionArgs e)
{
    mediaPlayerElement.MediaPlayer.Source = e.Source;
    mediaPlayerElement.MediaPlayer.RealTimePlayback = true;
    mediaPlayerElement.MediaPlayer.Play();
}

请注意,这是负责设置MediaPlayerElement的代码,并接受MediaSource作为事件的参数。

接下来让VideoPlayer页面订阅MainPage的活动。在Button_Click的{​​{1}}事件处理程序内,我们需要添加两行:

MainPage

现在,您只需从Frame newFrame = new Frame(); newFrame.Navigate(typeof(VideoPlayer), null); // These are the two new lines... the others are shown for reference of where to place these. VideoPlayer videoPlayerPage = newFrame.Content as VideoPlayer; this.VideoSelected += videoPlayerPage.VideoSelected; Window.Current.Content = newFrame; Window.Current.Activate(); VideoPlayerWindow = Window.Current; 处理程序(现在位于pickFileButton_Click上)调用此事件,然后传入从MainPage获得的MediaSource ...但这是你可以遇到麻烦的地方(也是我花费最长的时间才弄明白的。)

我们必须记住每个窗口都在自己的线程上运行。即使您从其他窗口调用代码,该代码仍然在调用它的线程上运行。这意味着如果您只是执行上述操作,代码将编译并运行,但是当您尝试选择视频文件时,您将收到运行时错误,并显示一条消息,指出资源已在另一个线程上编组。

解决方法是安排将视频文件放在与第二个窗口运行相同的线程上的工作。因此,我们需要再做两次修改才能实现这一目标。

首先,我们有FileOpenPicker的概念。每个控制都有一个。您已经使用了与新视图关联的Dispatcher(您使用Dispatcher声明了这一点。需要使用相同的var myView来安排视频文件的工作,因此我们需要维护一个参考只需在Dispatcher

中添加一个属性即可
MainPage

并调整视图创建以创建该属性的新视图,而不是CoreApplicationView VideoPlayerView { get; set; }

var myView

最后,我们可以设置VideoPlayerView = CoreApplication.CreateNewView(); await VideoPlayerView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => … 处理程序。将按钮(按钮本身的XAML中无需修改)移动到pickFileButton_Click。然后将您的单击处理程序移动到MainPage,但具有以下两个修改:

  • 替换引用MainPage.xaml的代码(现在位于mediaPlayerElement页面上的自定义事件处理程序中)。通过调用来提升自定义事件。
  • 使用相同的调度程序将所有代码包装到另一个调度中,以便在第二个窗口的线程上运行。

现在在VideoPlayer上的修改后的事件处理程序如下所示:

MainPage

应该这样做!我测试了代码,并让它在我的计算机上成功运行了一个真实的视频文件。

希望有所帮助!

完整代码,最终形式如下:

MainPage.xaml.cs中

private async void pickFileButton_Click(object sender, RoutedEventArgs e)
{
    // Schedule the work here on the same thread as the VideoPlayer window,
    //    so that it has access to the file and MediaSource to play.
    await this.VideoPlayerView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    {
        // Create and open the file picker
        FileOpenPicker openPicker = new FileOpenPicker();
        openPicker.ViewMode = PickerViewMode.Thumbnail;
        openPicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;
        openPicker.FileTypeFilter.Add(".mp4");
        openPicker.FileTypeFilter.Add(".mkv");
        openPicker.FileTypeFilter.Add(".avi");

        StorageFile file = await openPicker.PickSingleFileAsync();
        if (file != null)
        {
            MediaSource sourceFromFile = MediaSource.CreateFromStorageFile(file);

            // Raise the event declaring that a video was selected.
            this.RaiseVideoSelectedEvent(sourceFromFile);
         }
    });
}

VideoPlayer.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace VideoSamplePlayer
{

    public sealed partial class MainPage : Page
    {
        int newViewID = 0;

        Window VideoPlayerWindow;

        // To store the reference to the view control, so that it's dispatcher
        //    can be used to schedule more work on its thread.
        CoreApplicationView VideoPlayerView { get; set; }

        // The custom event declaration, to be raised when a media source for the video
        //      is selected.
        public delegate void VideoSelectedHandler(object sender, VideoSelectionArgs e);
        public event VideoSelectedHandler VideoSelected;

        private void RaiseVideoSelectedEvent(MediaSource source)
        {
            // Ensure that something is listening to the event.
            if (this.VideoSelected != null)
            {
                // Create the args, and call the listening event handlers.
                VideoSelectionArgs args = new VideoSelectionArgs(source);
                this.VideoSelected(this, args);
            }
        }

        public MainPage()
        {
            this.InitializeComponent();

        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            if (newViewID == 0)
            {
                // Store the newly created view control.
                this.VideoPlayerView = CoreApplication.CreateNewView();

                await this.VideoPlayerView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    Frame newFrame = new Frame();
                    newFrame.Navigate(typeof(VideoPlayer), null);

                    // Have the new VideoPlayer page subscribe to the media source selection event on this page.
                    VideoPlayer videoPlayerPage = newFrame.Content as VideoPlayer;
                    this.VideoSelected += videoPlayerPage.VideoSelected;

                    Window.Current.Content = newFrame;
                    Window.Current.Activate();
                    VideoPlayerWindow = Window.Current;

                    newViewID = ApplicationView.GetForCurrentView().Id;
                });

                await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewID, ViewSizePreference.UseMinimum);
            }
        }

        private async void pickFileButton_Click(object sender, RoutedEventArgs e)
        {
            // Schedule the work here on the same thread as the VideoPlayer window,
            //    so that it has access to the file and MediaSource to play.
            await this.VideoPlayerView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                // Create and open the file picker
                FileOpenPicker openPicker = new FileOpenPicker();
                openPicker.ViewMode = PickerViewMode.Thumbnail;
                openPicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;
                openPicker.FileTypeFilter.Add(".mp4");
                openPicker.FileTypeFilter.Add(".mkv");
                openPicker.FileTypeFilter.Add(".avi");

                StorageFile file = await openPicker.PickSingleFileAsync();
                if (file != null)
                {
                    MediaSource sourceFromFile = MediaSource.CreateFromStorageFile(file);

                    // Raise the event declaring that a video was selected.
                    this.RaiseVideoSelectedEvent(sourceFromFile);
                }
            });
        }
    }

    // Class definition for the custom event args, which allows a 
    //     media source to be passed to any event handlers that are listening.
    public class VideoSelectionArgs : EventArgs
    {
        public MediaSource Source { get; private set; }

        public VideoSelectionArgs(MediaSource source)
        {
            this.Source = source;
        }
    }
}

注意:此代码经过测试,点击了&#34;选择视频&#34;按钮 AFTER 第二个窗口打开。在打开第二个窗口之前,您可能需要做更多工作才能安全地点击(或者只是在第二个窗口打开之前隐藏它)。