.NET MAUI平台特定代码触发事件在主代码处理的问题排查
问题分析与解决方案
问题描述
定义了IAppConfig接口,包含PolicyDefaultChanged事件,iOS平台的AppConfigService实现该接口并尝试触发事件,但事件始终为null,导致Invoke()无法执行。已在MainPage中订阅事件,但未收到触发通知。后续通过PropertyMapper实现了功能,但不想依赖IView类,希望基于IAppConfig接口的事件机制完成需求。
错误原因
核心问题是事件触发时机早于事件订阅时机,具体执行顺序如下:
- 依赖注入解析
MainPage时,会先创建AppConfigService实例,其构造函数立即调用setAppConfigurationSettings()触发事件。 - 此时
MainPage尚未完成构造,appConfig.PolicyDefaultChanged += OnPolicyDefaultsChanged;这段订阅代码还未执行,因此PolicyDefaultChanged事件为null,无法触发。
解决方案
方案1:调整事件触发时机,避免构造函数内触发
修改AppConfigService,移除构造函数内的事件触发逻辑,改为提供初始化方法,让MainPage在订阅事件后手动调用:
修复后的AppConfigService代码
// iOS specific code // in Platforms / iOS /AppConfigService.cs public class AppConfigService : IAppConfig { private Dictionary<string, object> m_dict = new Dictionary<string, object>(); public event EventHandler<Controls.PolicyDefaultChangedEventArgs> PolicyDefaultChanged; public AppConfigService() { // 移除构造函数内的事件触发逻辑 } // 新增初始化方法,供外部调用 public void Initialize() { Dictionary<string, object> dict = new Dictionary<string, object>(); dict["keyString"] = "test"; setAppConfigurationSettings(dict); } public void setAppConfigurationSettings(Dictionary<string, object> dict) { m_dict = dict; Controls.PolicyDefaultChangedEventArgs ars = new Controls.PolicyDefaultChangedEventArgs(dict); OnPolicyDefaultChanged(ars); } protected virtual void OnPolicyDefaultChanged(Controls.PolicyDefaultChangedEventArgs e) { PolicyDefaultChanged?.Invoke(this, e); } }
修改MainPage代码,先订阅再触发
// Main Code // in MainPage.xaml.cs public partial class MainPage : ContentPage { public MainPage(IAppConfig appConfig) { InitializeComponent(); // 先完成事件订阅 appConfig.PolicyDefaultChanged += OnPolicyDefaultsChanged; // 再调用初始化触发事件 appConfig.Initialize(); } private void OnPolicyDefaultsChanged(object sender, Controls.PolicyDefaultChangedEventArgs e) { Debug.WriteLine("Policy changed"); Debug.WriteLine($"Received policy content: {e.DefaultPolicy["keyString"]}"); } }
方案2:调整服务生命周期为单例(可选)
如果AppConfigService需要全局唯一实例,将MauiProgram中的服务注册从AddTransient改为AddSingleton,确保整个应用生命周期内使用同一个实例,后续任何触发都能被订阅者接收:
// MAUIProgram.cs public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder.UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }) #if DEBUG builder.Logging.AddDebug(); #endif // 改为单例注册 builder.Services.AddSingleton<IAppConfig, AppConfigService>(); builder.Services.AddTransient<MainPage>(); return builder.Build(); } }
注意:即使使用单例,仍需确保事件触发在订阅之后,否则首次触发仍会丢失。
额外说明
你之前通过PropertyMapper实现的方式能工作,是因为事件触发时机绑定在BindableProperty变更时,此时MyClass控件已完成初始化,MainPage也完成了订阅。但这种方式依赖IView和MAUI控件生命周期,不符合你基于纯接口事件的需求,上述方案可以解决该问题。
内容的提问来源于stack exchange,提问作者Prabhav Mehra
相关产品推荐
相关产品推荐

