簡體   English   中英

在ContentControl內部的不同窗口上切換控件

[英]Switching a Control over different windows inside ContentControl

我正在制作視頻播放器。 在應用程序中,我有一個全屏窗口和一個普通窗口。

我正在使用ContentControl從ViewModel綁定其中的MediaElement 我的MediaElement實例是在ViewModel內部創建的,因此可以更輕松地通過代碼控制此控件。

現在,我有一個Button來顯示帶有另一個ContentControl另一個窗口,可以在其中分配這個完全相同的MediaElement 有用。 元素從我的普通窗口切換到我的全屏。

問題是,當我關閉全屏窗口時,它不會切換回普通窗口ContentControl

我嘗試重新綁定該控件,它可以在任何控件中看到該控件,但該控件似乎在該控件的內部,但似乎不起作用。

FullScreenView

<Grid>
   <ContentControl Content="{Binding PlayerBaseViewModel.MediaControl, Mode=OneWay}" />
</Grid>

PlayerBaseView

<Grid>
   <ContentControl Content="{Binding MediaControl}" />
</Grid>

致電全屏

internal void ShowFullScreen(PlayerBaseViewModel playerBaseViewModel)
{
    var fullScreenView = new FullScreenView();

    var fullScreenViewModel = new FullScreenViewModel(
        playerBaseViewModel, 
        this.PlayControlViewModel, 
        this.source);

    fullScreenView.DataContext = fullScreenViewModel;
    fullScreenView.ShowDialog();
    fullScreenViewModel.Dispose();
}

基礎

public FrameworkElement MediaControl
{
    get
    {
        return this.mediaControl;
    }

    set
    {
        this.mediaControl = value;
        if (this.mediaControl != null)
        {
            this.mediaControl.HorizontalAlignment = HorizontalAlignment.Center;
        }

        this.RaisePropertyChanged(() => this.MediaControl);
    }
}
private void FullScreen()
{
    if (this.ShowFullScreen != null)
    {
        MessageHelper.ShowMessage(
            string.Format(StaticData.MessagePlayerBaseViewModelFullScreen, this.Device.Name),
            string.Format(StaticData.MessagePlayerBaseViewModelFullScreenDetails, this.Device.Name),
            3);
        var control = this.mediaControl;
        this.ShowFullScreen(this);
        this.MediaControl = null;
        control.DataContext = this;
        this.MediaControl = control;
        this.Device.ResetAllDevice();
    }
}

PlayerAudioVideoViewModel

public class PlayerAudioVideoViewModel : PlayerBaseViewModel
{
    #region Static Fields

    private static readonly Stopwatch Watch = Stopwatch.StartNew();

    #endregion

    #region Fields

    private readonly DependencyPropertyDescriptor mediaPositionDependencyPropertyDescriptor =
        DependencyPropertyDescriptor.FromProperty(MediaSeekingElement.MediaPositionProperty, typeof(UIElement));

    private readonly DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Render);

    private MediaElement mediaElement = new MediaElement();

    #endregion

    #region Constructors and Destructors

    public PlayerAudioVideoViewModel(DeviceModel deviceModel)
        : base(deviceModel)
    {
        this.MediaControl = this.mediaElement;
        this.mediaElement.BeginInit();
        this.timer.Interval = TimeSpan.FromMilliseconds(200);
        this.timer.Tick += this.TimerTick;
        this.mediaElement.LoadedBehavior = MediaState.Manual;
        this.mediaElement.ScrubbingEnabled = true;
        this.mediaElement.MediaOpened += this.MediaOpened;
        this.mediaElement.MediaEnded += this.MediaEnded;
        this.mediaPositionDependencyPropertyDescriptor.AddValueChanged(this.mediaElement, this.PositionChanged);
        this.mediaElement.EndInit();
    }

    #endregion

    #region Public Methods and Operators

    public override void CleanUp()
    {
        this.MediaControl = null;
        this.mediaElement.Source = null;
        this.mediaElement.MediaOpened -= this.MediaOpened;
        this.mediaElement.MediaEnded -= this.MediaEnded;
        this.mediaElement.LoadedBehavior = MediaState.Manual;
        this.mediaElement.UnloadedBehavior = MediaState.Manual;
        this.timer.Tick -= this.TimerTick;
        this.mediaElement.Source = null;
        this.mediaElement = null;
    }

    public override void LoadData()
    {
        if (this.Data != null)
        {
            var cont = (ContentMoving)this.Data.Content;
            if (this.mediaElement.Source == null || !this.mediaElement.Source.LocalPath.Equals(cont.Path.FullName))
            {
                this.mediaElement.Source = new Uri(cont.Path.FullName, UriKind.Absolute);
                this.mediaElement.Pause();
            }
            else
            {
                this.mediaElement.Position =
                    cont.Position.Subtract(new TimeSpan(0, 0, 0, 0, cont.Position.Milliseconds));

                // this.ChangePlaySpeed();
                this.timer.Start();
            }
        }
        else
        {
            this.mediaElement.Source = null;
        }
        Debug.Print("D {0} Load.", this.DeviceName);

    }

    public override void MuteAudio(bool mute)
    {
        this.mediaElement.IsMuted = mute;
    }

    public override void Pause()
    {
        Debug.Print("D {0} Pause.", this.DeviceName);
        this.mediaElement.Pause();
    }

    public override void Play()
    {
        Debug.Print("D {0} Play.", this.DeviceName);
        this.mediaElement.Play();
    }





    #endregion

    #region Methods


    private void MediaEnded(object sender, RoutedEventArgs e)
    {
        if (this.mediaElement.Position == this.mediaElement.NaturalDuration)
        {
            this.DataEndReached();
        }
    }

    private void MediaOpened(object sender, RoutedEventArgs e)
    {
        if (this.Data != null)
        {
            var cont = (ContentMoving)this.Data.Content;
            if (this.Device.Status == PlayStatus.Play)
            {
                this.Device.ResetAllDevice();
            }
            else
            {
                this.mediaElement.Position =
                    cont.Position.Subtract(new TimeSpan(0, 0, 0, 0, cont.Position.Milliseconds));
                this.ChangePlaySpeed();
            }
        }

        this.DataLoaded();
    }




    #endregion
}

}

原始窗口丟失視頻的原因是因為當您關閉全屏窗口時; MediaElement正在卸載,因為它是該窗口ui樹的一部分。 要解決此問題,您首先需要將MediaElement加載/卸載行為更改為“手動”。 默認情況下,它處於自動模式。 這就是為什么媒體在加載源后立即開始播放的原因。 這也意味着,當從UI樹觸發Unloaded事件時, MediaElement將關閉。

將行為設置為手動后,您仍將需要處理全屏窗口關閉事件,以便可以重新綁定ContentControl 主窗口中的ContentMediaElement )。 這將再次觸發MediaElement的“已加載”事件,這將允許MediaElement在主窗口中繼續播放。

這是示例代碼; 非常簡單。 我正在展示ViewModel構造函數和相關方法。

ViewModel.cs的部分:

public RelayCommand Maximize { get; set; }

public RelayCommand Play { get; set; }

private MediaElement media;

// Bound media property with notification
public MediaElement Media
{
    get
    {
        return this.media;
    }
    set
    {
        this.media = value;

        // Modify following line as needed
        // to any custom Property changed handler
        this.Changed("Media");
    }
}

// Constructor, setup the command, media element etc.
public ViewModel()
{
    // Two button commands
    this.Maximize = new RelayCommand(this.ExecuteMaximizeCommand);
    this.Play = new RelayCommand(this.ExecutePlayCommand);

    // The Media element
    this.Media = new MediaElement();

    // This is where we need to set Loaded and Unloaded behavior to Manual
    // This means video won't play right-away after loading
    // You will have to call .Play() method to start playback
    // Add a play button if you don't have one and bind the Play command
    this.Media.LoadedBehavior = MediaState.Manual;
    this.Media.UnloadedBehavior = MediaState.Manual;

    // Following is just an example. Doesn't need to be here
    this.Media.Source = new Uri("test.mp4", UriKind.Relative);

}


public void ExecuteMaximizeCommand()
{            
    var fullscreen = new FullScreen();

    // We need to handle the Closing event
    // So that we can trigger the playback
    // to continue in the smaller window
    fullscreen.Closing += fullscreen_Closing;
    fullscreen.DataContext = this;
    fullscreen.ShowDialog();
}

public void ExecutePlayCommand()
{
    this.Media.Play();
}

// Event handler for fullscreen window closing
void fullscreen_Closing(object sender, CancelEventArgs e)
{
    // Trigger the Property Change Notification
    // by forcing null and then back to original value
    // This will trigger the binding to kick-in again
    // which will redraw the MediaElement UI and continue
    // the playback
    var temp = this.Media;
    this.Media = null;
    this.Media = temp;
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>     
        <ContentControl Content="{Binding Media}" />
        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <Button  Command="{Binding Maximize}" Content="Maximize" Margin="2" />
            <Button  Command="{Binding Play}" Content="Play" Margin="2" />
        </StackPanel>
    </Grid>
</Window>

MainWindow.cs

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new ViewModel();
}

FullScreen.xaml

<Window x:Class="WpfApplication1.FullScreen"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="FullScreen" Height="300" Width="300" WindowState="Maximized">
    <Grid>
        <ContentControl x:Name="MediaContentControl" Content="{Binding Path=Media}">
        </ContentControl>
    </Grid>
</Window>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM