.NET8 MAUI中CommunityToolkit Popup报错及替代方案咨询
问题回顾
- 本地调试时CommunityToolkit Popup功能正常,发布至Google Store后触发
System.ObjectDisposedException,异常指向CommunityToolkit.Maui.Core.Views.MauiPopup已被释放。 - 调用
ShowPopup后立即执行Close(),Popup无法正常关闭。
异常信息
System.ObjectDisposedException: ObjectDisposed_Generic ObjectDisposed_ObjectName_Name, CommunityToolkit.Maui.Core.Views.MauiPopup
调用堆栈
Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable )
Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualBooleanMethod(String, IJavaPeerable , JniArgumentValue* )
Android.App.Dialog.get_IsShowing()
CommunityToolkit.Maui.Core.Handlers.PopupHandler.MapOnClosed(PopupHandler handler, IPopup view, Object result)
Microsoft.Maui.CommandMapper`2.<>c__DisplayClass6_0[[CommunityToolkit.Maui.Core.IPopup, CommunityToolkit.Maui.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[CommunityToolkit.Maui.Core.Handlers.PopupHandler, CommunityToolkit.Maui.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].b__0(IElementHandler h, IElement v, Object o)
Microsoft.Maui.CommandMapper.InvokeCore(String key, IElementHandler viewHandler, IElement virtualView, Object args)
Microsoft.Maui.CommandMapper.Invoke(IElementHandler viewHandler, IElement virtualView, String property, Object args)
Microsoft.Maui.Handlers.ElementHandler.Invoke(String command, Object args)
CommunityToolkit.Maui.Views.Popup.CommunityToolkit.Maui.Core.IPopup.OnClosed(Object result)
CommunityToolkit.Maui.Views.Popup.OnClosed(Object result, Boolean wasDismissedByTappingOutsideOfPopup, CancellationToken token)
CommunityToolkit.Maui.Views.Popup.CloseAsync(Object result, CancellationToken token)
CommunityToolkit.Maui.Views.Popup.Close(Object result)
System.Threading.Tasks.Task.<>c.b__128_0(Object state)
Android.App.SyncContext.<>c__DisplayClass2_0.b__0()
Java.Lang.Thread.RunnableImplementor.Run()
Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
响应异常场景
执行以下代码后立即调用Close(),Popup无法关闭:
var loadingPopup = new LoadingPopup(); Application.Current.MainPage.ShowPopup(loadingPopup); loadingPopup.Close();
问题排查建议(针对CommunityToolkit异常)
- 异步调用修正:
ShowPopup本质是异步初始化操作,立即调用Close()会导致Popup尚未完成渲染就被触发关闭,引发资源释放异常。改用异步等待逻辑:
var loadingPopup = new LoadingPopup(); // 使用ShowPopupAsync确保弹窗完成加载 await Application.Current.MainPage.ShowPopupAsync(loadingPopup); // 或在需要关闭时,通过绑定或延迟确保弹窗已初始化 loadingPopup.Close();
- 发布代码裁剪防护:检查Android发布配置,避免R8/ProGuard过度裁剪CommunityToolkit的Popup相关类,在
proguard.cfg中添加规则:
-keep class CommunityToolkit.Maui.Core.Views.MauiPopup { *; } -keep class CommunityToolkit.Maui.Core.Handlers.PopupHandler { *; }
- 实例引用维护:确保Popup实例在页面生命周期内保持强引用,避免被GC提前回收导致释放异常。
不依赖CommunityToolkit的跨平台Popup实现方案
方案1:自定义全屏覆盖式Popup(通用跨平台)
通过在当前页面添加半透明覆盖层+弹窗内容的方式实现,完全支持Windows、macOS及其他MAUI平台:
核心实现代码
public class CustomPopup : ContentView { public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(CustomPopup), false, propertyChanged: OnIsVisibleChanged); public bool IsVisible { get => (bool)GetValue(IsVisibleProperty); set => SetValue(IsVisibleProperty, value); } private static void OnIsVisibleChanged(BindableObject bindable, object oldValue, object newValue) { var popup = (CustomPopup)bindable; if ((bool)newValue) popup.Show(); else popup.Hide(); } private void Show() { if (Parent != null) return; var mainPage = Application.Current.MainPage; var overlay = new AbsoluteLayout { BackgroundColor = Color.FromRgba(0, 0, 0, 0.5), InputTransparent = false }; // 弹窗居中显示 AbsoluteLayout.SetLayoutBounds(this, new Rect(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize)); AbsoluteLayout.SetLayoutFlags(this, AbsoluteLayoutFlags.PositionProportional); overlay.Children.Add(this); // 将覆盖层添加到页面最上层 mainPage.Content = new AbsoluteLayout { Children = { mainPage.Content, overlay } }; } private void Hide() { if (Parent is not AbsoluteLayout overlay || overlay.Parent is not AbsoluteLayout parentLayout) return; parentLayout.Children.Remove(overlay); // 恢复原页面内容 parentLayout.Children.Add((View)parentLayout.Children[0]); } public void Close() => IsVisible = false; }
使用示例
// 创建带加载动画的弹窗 var loadingPopup = new CustomPopup { Content = new StackLayout { WidthRequest = 220, HeightRequest = 160, BackgroundColor = Colors.White, CornerRadius = 12, Padding = new Thickness(20), Children = { new ActivityIndicator { IsRunning = true, Color = Colors.Blue }, new Label { Text = "加载中...", Margin = new Thickness(0,10,0,0) } } } }; // 显示弹窗 loadingPopup.IsVisible = true; // 关闭弹窗 loadingPopup.Close();
方案2:平台原生API封装(依赖服务)
针对不同平台调用原生弹窗组件,通过MAUI依赖服务实现跨平台适配,适合简单文本类弹窗:
1. 定义接口
public interface IPopupService { Task ShowPopupAsync(string title, string content); Task ClosePopupAsync(); }
2. 各平台实现
- Windows:调用
ContentDialog - macOS:调用
NSAlert - Android:调用
AlertDialog - iOS:调用
UIAlertController
这种方案优势是原生体验,缺点是自定义内容灵活性较低,复杂弹窗推荐方案1。
内容的提问来源于stack exchange,提问作者Enrico

