You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止WPF MediaKit的MediaClosed事件触发并恢复视频播放?

这个问题我之前做WPF视频播放功能时也踩过坑!核心原因很简单:当你切换到另一个用户控件时,承载MediaUriElement的那个用户控件会被从可视化树中移除,WPF会自动触发MediaClosed事件并释放媒体资源,等你切回去的时候,控件状态或资源已经丢失,自然没法继续播放。下面给你几个实用的解决方案,按需选择:

方案1:保留用户控件实例,避免重复销毁/加载

这是我最推荐的方案,体验最好也最省心。思路是提前把两个用户控件的实例创建好,切换时只是让ContentPresenter切换显示的实例,而不是销毁旧的、创建新的。这样MediaUriElement的实例一直存在,播放状态也会完整保留。

示例代码:

// 提前初始化两个用户控件实例,作为类的成员变量
private readonly MediaUserControl _mediaControl = new MediaUserControl();
private readonly OtherUserControl _otherControl = new OtherUserControl();

// 切换到媒体控件的按钮点击事件
private void SwitchToMediaBtn_Click(object sender, RoutedEventArgs e)
{
    MyContentPresenter.Content = _mediaControl;
    
    // 第一次播放就设置Uri,后续切回来直接续播
    if (_mediaControl.MediaElement.Source == null)
    {
        _mediaControl.MediaElement.Source = new Uri(@"C:\你的视频路径.mp4");
    }
    else
    {
        _mediaControl.MediaElement.Play();
    }
}

// 切换到其他控件的按钮点击事件
private void SwitchToOtherBtn_Click(object sender, RoutedEventArgs e)
{
    // 切换前先暂停视频,保留当前播放位置
    _mediaControl.MediaElement.Pause();
    MyContentPresenter.Content = _otherControl;
}

这个方案的好处是视频的播放位置、音量、进度等状态全都会保留,切回来一点就续播,不需要重新加载资源,用户体验拉满。唯一要注意的是如果控件占用内存特别大,可能会有轻微内存开销,但一般视频控件完全没问题。

方案2:记录播放状态,切回时重新加载恢复

如果因为资源限制或特殊逻辑必须销毁用户控件,那可以在切换前记录当前播放位置和视频Uri,切回来时重新创建控件,加载视频后跳转到之前的位置继续播放。

示例代码:

// 记录当前视频的Uri和播放位置
private Uri _currentVideoUri;
private TimeSpan _lastPlayPosition = TimeSpan.Zero;

// 切换到其他控件的事件
private void SwitchToOtherBtn_Click(object sender, RoutedEventArgs e)
{
    var mediaControl = MyContentPresenter.Content as MediaUserControl;
    if (mediaControl != null)
    {
        // 先暂停,再记录状态
        mediaControl.MediaElement.Pause();
        _lastPlayPosition = mediaControl.MediaElement.Position;
        _currentVideoUri = mediaControl.MediaElement.Source;
    }
    // 销毁旧的媒体控件,创建新的其他控件
    MyContentPresenter.Content = new OtherUserControl();
}

// 切回媒体控件的事件
private void SwitchToMediaBtn_Click(object sender, RoutedEventArgs e)
{
    var mediaControl = new MediaUserControl();
    mediaControl.MediaElement.Source = _currentVideoUri;
    
    // 必须等视频加载完成(MediaOpened触发)才能设置播放位置
    mediaControl.MediaElement.MediaOpened += (s, args) =>
    {
        mediaControl.MediaElement.Position = _lastPlayPosition;
        mediaControl.MediaElement.Play();
    };
    
    MyContentPresenter.Content = mediaControl;
}

注意:一定要在MediaOpened事件里设置播放位置,因为视频还没加载完成时,设置Position是无效的。如果希望每次切回来都从头播放,只需要把_lastPlayPosition重置为TimeSpan.Zero就行。

方案3:拦截MediaClosed事件,临时保留资源(不推荐)

如果以上两种方案都没法用,比如不能修改控件创建逻辑,那可以尝试在MediaClosed事件里做拦截,但这个方法有潜在的内存泄漏风险,除非万不得已别用。

示例代码:

private void MediaUriElement_MediaClosed(object sender, EventArgs e)
{
    var mediaElement = sender as MediaUriElement;
    // 重新设置Source,避免资源被完全释放
    if (_currentVideoUri != null)
    {
        mediaElement.Source = _currentVideoUri;
        mediaElement.Position = _lastPlayPosition;
        mediaElement.Pause(); // 先暂停,等切回来再播放
    }
}

这个方法相当于强制让媒体资源不被释放,但可能会导致内存占用一直居高不下,所以谨慎使用。


内容的提问来源于stack exchange,提问作者James Hao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:28:38