Reputation: 1388
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
}
}
Upvotes: 1
Views: 1245
Reputation: 4893
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>
Upvotes: 2