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

.NET MAUI平台特定代码触发事件在主代码处理的问题排查

问题分析与解决方案

问题描述

定义了IAppConfig接口,包含PolicyDefaultChanged事件,iOS平台的AppConfigService实现该接口并尝试触发事件,但事件始终为null,导致Invoke()无法执行。已在MainPage中订阅事件,但未收到触发通知。后续通过PropertyMapper实现了功能,但不想依赖IView类,希望基于IAppConfig接口的事件机制完成需求。

错误原因

核心问题是事件触发时机早于事件订阅时机,具体执行顺序如下:

  1. 依赖注入解析MainPage时,会先创建AppConfigService实例,其构造函数立即调用setAppConfigurationSettings()触发事件。
  2. 此时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:57:07