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

在第二个窗口中显示视频(UWP C#),c#,uwp,C#,Uwp,我有一个我正在开发的应用程序,我希望一个窗口是控制器,另一个窗口是视频播放器视图 这样做的目的是控制视频从一个窗口播放,我可以在笔记本电脑上观看,而视频在投影仪上播放。最后,我想设置它,这样我就可以拥有一组16个视频,我可以使用键盘在视频播放器视图中播放,当我按下与特定视频相关联的键(通过选择器选择)时停止当前播放的视频,并启动新视频 请注意,如果有更好的语言和/或系统来创建此Windows应用程序,我愿意接受任何超出我要求的建议 这是我的主页.xaml.cs: using System; us

我有一个我正在开发的应用程序,我希望一个窗口是控制器,另一个窗口是视频播放器视图

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

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

这是我的主页.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);
            }
        }
    }
}
这是我的主页.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:

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

我想将文件选择器按钮移动到主页面,但视频播放器中仍有视频输出

我该怎么做


谢谢

花了一点时间,但最终找到了解决方案!诀窍是使用自定义事件。。。棘手的是,我们正在使用多个线程(每个窗口都在自己的线程中运行)

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

让我们看看每个部分:

首先在
主页上声明自定义事件

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);
    }
}
CoreApplicationView VideoPlayerView { get; set; }
您还需要为事件参数声明类(适当地,它继承自
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
的事件。在
按钮内单击
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\u单击
处理程序调用此事件(现在在
MainPage
,然后传入从
FileOpenPicker
获取的
MediaSource
…但这就是您可能遇到麻烦的地方(也是我花了最长时间才弄清楚的)

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

解决方法是在运行第二个窗口的同一线程上调度获取该视频文件的工作

首先,我们有一个
Dispatcher
的概念。每个控件都有一个。您已经使用了与新视图关联的
Dispatcher
(您用
var myView
声明了它。这个
Dispatcher
需要用于安排视频文件的工作,因此我们需要维护对它的引用。只需将一个属性添加到您的
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);
    }
}
CoreApplicationView VideoPlayerView { get; set; }
并调整视图创建以创建该属性的新视图,而不是
var myView

VideoPlayerView = CoreApplication.CreateNewView();

await VideoPlayerView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
…
最后,我们可以设置
pickFileButton\u Click
处理程序。将按钮(按钮本身的XAML中不需要修改)移动到
MainPage.XAML。然后也将单击处理程序移动到MainPage,但需要进行以下两个修改:

  • 将引用
    mediaPlayerElement
    (现在位于
    VideoPlayer
    页面上的自定义事件处理程序中)的代码替换为引发自定义事件的调用
  • 通过使用相同的调度程序,将所有代码包装到另一个调度中,以在第二个窗口的线程上运行
现在位于
主页上的修改后的事件处理程序如下所示:

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);
         }
    });
}
我确实测试了代码,并在我的计算机上用一个真实的视频文件成功地运行了它

希望有帮助

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

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;

        // 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;
        }
    }
}
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();
        }

        // The event handler, which will listen for MainPage's VideoSelected
        //     event, after being subscribed to it on the MainPage.
        public void VideoSelected(object sender, VideoSelectionArgs e)
        {
            // Get the MediaSource from the event arguments, and set up and start
            //    the media player.
            mediaPlayerElement.MediaPlayer.Source = e.Source;
            mediaPlayerElement.MediaPlayer.RealTimePlayback = true;
            mediaPlayerElement.MediaPlayer.Play();
        }
    }
}

注意:此代码通过在第二个窗口打开后单击“拾取视频”按钮进行测试。您可能需要做更多的工作,以确保在第二个窗口打开前安全单击(或在第二个窗口打开前将其隐藏).

具有讽刺意味的是,这基本上是我在大学里的Win32 GUI项目……不知道在任何现代事物中实现这一点的最佳方式(特别是UWP,它限制了你很多)。我建议你至少去WPF,在那里你可以做得更多。@BradleyDotNET你能给我一些关于在WPF中从哪里开始做这件事的建议吗?我希望我能,就像我说的,我是在纯Win32中做的。如果你能同时显示两个窗口,尽管从那里开始应该很简单……完整的答案可能是:啊,这是一个如此宽泛的世界