[英]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
。 主窗口中的Content
( MediaElement
)。 這將再次觸發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.