简体   繁体   中英

Switching a Control over different windows inside ContentControl

I am making a video player. In is application I have a full screen window and a normal window.

I am using a ContentControl to bind a MediaElement inside it, from my ViewModel. The instance of my MediaElement is created inside the ViewModel so it easier to control this control via code.

Now I have a Button to show another window with another ContentControl where I can assigned this very same MediaElement . It works. The element switch from my normal window to my FullScreen.

Problem is when I close my full screen window it does not switch back, to my normal window ContentControl .

I tried rebinding the control, it works the control is seen inside the control control but does not appear to work, to any of the commands.

FullScreenView

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

PlayerBaseView

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

Call to Fullscreen

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

Base

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
}

}

The reason why the original window is losing the video is because when you are closing the full screen window; MediaElement is unloading as it was part of that window's ui tree. To fix this you will need to first change MediaElement loading/unloading behavior to Manual. By default it is in automatic mode. This is why Media starts playing right after source is loaded. That also means when Unloaded event fires from UI tree, MediaElement will close.

Once you set the behavior to manual, you will still need to handle full screen window closing event, so that you can rebind the ContentControl . Content ( MediaElement ) in the main window. This will trigger the "Loaded" event in the MediaElement again, which will then allow the MediaElement to continue the playback in the main window.

Here is the sample code; very straight forward. I am showing the ViewModel constructor and relevant methods.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM