MAUI自定义ContentView中MediaElement的事件如何在外部页面订阅
我明白你现在的困扰——你封装了一个带MediaElement的自定义ContentView,绑定属性都正常工作,但想在使用这个控件的页面里监听MediaElement的事件(比如StateChanged、MediaOpened),直接访问内部的mediaElement会报CS1061错误,试了用Action属性又没触发效果。别着急,咱们可以通过在自定义控件中暴露对应的事件,把内部MediaElement的事件转发给外部页面,完美解决这个问题。
解决方案步骤
1. 在自定义控件MediaControl中暴露对应事件
打开MediaControl.xaml.cs,在类里声明和内部MediaElement匹配的事件,这样外部页面就能订阅这些事件了:
namespace NerdNewsNavigator2.Controls; public partial class MediaControl : ContentView { // 新增:暴露供外部订阅的事件,和MediaElement的事件一致 public event EventHandler<MediaStateChangedEventArgs>? StateChanged; public event EventHandler? MediaOpened; // 你的现有属性、构造函数等代码... }
2. 绑定内部MediaElement事件到自定义事件
在MediaControl的构造函数里,把内部mediaElement的事件触发逻辑绑定到我们刚声明的自定义事件上,这样内部事件触发时,外部订阅的事件也会跟着触发:
public MediaControl() { InitializeComponent(); PlayPosition = string.Empty; mediaElement.PropertyChanged += MediaElement_PropertyChanged; mediaElement.PositionChanged += ChangedPosition; CurrentPage = Shell.Current.CurrentPage; // 新增:转发内部MediaElement的事件到自定义事件 mediaElement.StateChanged += (sender, e) => StateChanged?.Invoke(sender, e); mediaElement.MediaOpened += (sender, e) => MediaOpened?.Invoke(sender, e); }
3. 在外部页面(比如TabletPlayPodcastPage)订阅事件
首先在页面的XAML里确保你给MediaControl设置了命名(比如x:Name="myMediaControl"):
<controls:MediaControl x:Name="myMediaControl" />
然后修改你的Load方法,直接订阅myMediaControl暴露的事件即可,不会再出现CS1061错误:
public void Load() { #if WINDOWS || ANDROID myMediaControl.MediaOpened += Seek; #endif #if IOS || MACCATALYST myMediaControl.StateChanged += SeekIOS; #endif }
为什么之前的Action属性没用?
你之前尝试添加的Action<object, EventArgs> MediaOpened只是一个委托属性,但你没有在内部mediaElement.MediaOpened触发时主动调用这个委托,所以外部订阅了也不会收到任何通知。而用.NET标准的事件模型,我们通过转发逻辑确保内部事件触发时,外部的订阅者能及时收到回调。
额外提醒:避免内存泄漏
记得在页面卸载时取消订阅事件,防止内存泄漏:
protected override void OnDisappearing() { base.OnDisappearing(); // 取消订阅事件 myMediaControl.StateChanged -= SeekIOS; myMediaControl.MediaOpened -= Seek; }
这样修改后,你就能在外部页面正常监听自定义控件中MediaElement的事件了,既保持了代码的封装性,又实现了你想要的事件回调功能。
备注:内容来源于stack exchange,提问作者James Crutchley

