如何在MAUI中通过依赖注入复用弹窗?
解决.NET MAUI中通过依赖注入复用弹窗的问题
问题根源
MAUI Community Toolkit的MauiPopup在关闭时会被框架自动调用Dispose()方法释放资源,同一个弹窗实例无法被重复展示——这就是你遇到Cannot access a disposed object错误的核心原因:不管是直接注入弹窗实例,还是封装成服务复用单一实例,第一次关闭后实例就已被释放,再次调用必然触发异常。
解决方案1:使用工厂模式注入弹窗创建逻辑
不要直接注入弹窗实例,而是注入一个能创建新弹窗的工厂,每次需要展示时生成全新实例:
1. 定义弹窗工厂接口与实现
public interface ILoadingPopupFactory { LoadingControl CreateLoadingControl(string message); } public class LoadingPopupFactory : ILoadingPopupFactory { public LoadingControl CreateLoadingControl(string message) { return new LoadingControl(message); } }
2. 在MauiProgram中注册工厂
builder.Services.AddTransient<ILoadingPopupFactory, LoadingPopupFactory>();
3. 在ViewModel中使用工厂创建弹窗
修改TopicSelectionViewModel:
public IApiService ApiService { get; } public IDatabaseRepository repository; private readonly ILoadingPopupFactory _loadingPopupFactory; public TopicSelectionViewModel(IApiService apiService, IDatabaseRepository _repo, ILoadingPopupFactory loadingPopupFactory) { ApiService = apiService; repository = _repo; _loadingPopupFactory = loadingPopupFactory; } [RelayCommand] async Task Download(Topic topic) { if (topic is null) return; // 每次需要时创建新的弹窗实例 var loadingControl = _loadingPopupFactory.CreateLoadingControl("Downloading your info..."); Shell.Current.ShowPopup(loadingControl); if (ApiService.InternetAvailable()) { try { await repository.SaveTopicAndQuestionsAsync(await ApiService.GetQuestionsAsync(topic.TopicRef), topic.TopicRef); } catch (Exception ex) { Debug.WriteLine(ex); Debug.WriteLine(ex.InnerException?.Message ?? "No inner exception"); } finally { Refresh(); loadingControl.Close(); } } else { await Shell.Current.DisplayAlert("No Internet", "Connect to the internet to download", "Ok"); loadingControl.Close(); } }
解决方案2:改进LoadingService,每次展示创建新实例
如果偏好用服务封装弹窗逻辑,修改服务代码,每次调用ShowLoading时创建新实例,避免复用已释放的对象:
1. 修改ILoadingService与LoadingService
public interface ILoadingService { void ShowLoading(string message); void CloseLoading(); } public class LoadingService : ILoadingService { private LoadingPopup _currentPopup; public void CloseLoading() { _currentPopup?.Close(); // 关闭后清空引用,避免持有已释放的实例 _currentPopup = null; } public void ShowLoading(string message) { // 每次展示都创建新的弹窗实例 _currentPopup = new LoadingPopup(message); Shell.Current.ShowPopup(_currentPopup); } }
2. 注册服务(瞬态或单例均可,因为每次都创建新实例)
builder.Services.AddSingleton<ILoadingService, LoadingService>();
3. 在ViewModel中使用服务
public IApiService ApiService { get; } public IDatabaseRepository repository; private readonly ILoadingService _loadingService; public TopicSelectionViewModel(IApiService apiService, IDatabaseRepository _repo, ILoadingService loadingService) { ApiService = apiService; repository = _repo; _loadingService = loadingService; } [RelayCommand] async Task Download(Topic topic) { if (topic is null) return; _loadingService.ShowLoading("Downloading your info..."); if (ApiService.InternetAvailable()) { try { await repository.SaveTopicAndQuestionsAsync(await ApiService.GetQuestionsAsync(topic.TopicRef), topic.TopicRef); } catch (Exception ex) { Debug.WriteLine(ex); Debug.WriteLine(ex.InnerException?.Message ?? "No inner exception"); } finally { Refresh(); _loadingService.CloseLoading(); } } else { await Shell.Current.DisplayAlert("No Internet", "Connect to the internet to download", "Ok"); _loadingService.CloseLoading(); } }
关键注意事项
- 绝对不要复用已关闭的弹窗实例,MAUI框架会在弹窗关闭后自动处置它
- 通过工厂或每次创建新实例的方式,确保每次展示的都是未被释放的全新实例
- 在异常分支或取消操作时,务必记得关闭弹窗,避免弹窗一直停留在界面上
内容的提问来源于stack exchange,提问作者Tanaka Mawere
相关产品推荐
相关产品推荐

