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

如何从ViewModel关闭MauiCommunityToolkit Popup并解决二次调用异常

解决方案

一、修复WeakReferenceMessenger的空引用问题

问题核心是Popup关闭后消息订阅未注销,第二次发送消息时会触发已释放内部资源的Popup实例执行Close操作,导致CleanUp方法中出现空引用。

1. 关闭时注销消息订阅

在Popup的后台代码中,重写OnClosed方法,在Popup关闭后立即注销消息订阅,避免失效实例接收消息:

public class CustomPopup : Popup
{
    public CustomPopup()
    {
        // 注册关闭消息
        WeakReferenceMessenger.Default.Register<ClosePopupMsg>(this, (_, __) =>
        {
            MainThread.BeginInvokeOnMainThread(() =>
            {
                if (IsOpen) // 先校验Popup状态,避免无效操作
                {
                    Close();
                }
            });
        });
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);
        // 注销当前实例的消息订阅
        WeakReferenceMessenger.Default.Unregister<ClosePopupMsg>(this);
    }
}

// 定义关闭消息类
public class ClosePopupMsg { }

如果存在多个Popup实例,可给消息类添加PopupId字段,接收时判断标识,避免误关其他弹窗。

2. 发送消息时指定目标(可选)

若需精准控制特定Popup关闭,可在消息中携带标识,接收时匹配:

// 带标识的消息类
public class ClosePopupMsg
{
    public string PopupId { get; set; }
}

// Popup构造时传入标识
public CustomPopup(string popupId)
{
    WeakReferenceMessenger.Default.Register<ClosePopupMsg>(this, (_, msg) =>
    {
        if (msg.PopupId == popupId && IsOpen)
        {
            MainThread.BeginInvokeOnMainThread(Close);
        }
    });
}

// ViewModel中发送指定消息
WeakReferenceMessenger.Default.Send(new ClosePopupMsg { PopupId = "MyTargetPopup" });

二、无需传递句柄的更优关闭方式

1. 绑定IsOpen属性(最贴合MVVM)

CommunityToolkit.Maui的Popup支持双向绑定IsOpen属性,直接在ViewModel中通过属性控制弹窗显隐:

ViewModel代码:

private bool _isPopupOpen;
public bool IsPopupOpen
{
    get => _isPopupOpen;
    set => SetProperty(ref _isPopupOpen, value);
}

// 打开弹窗命令
public IRelayCommand OpenPopupCmd => new RelayCommand(() => IsPopupOpen = true);

// 关闭弹窗命令
public IRelayCommand ClosePopupCmd => new RelayCommand(() => IsPopupOpen = false);

Popup的XAML代码:

<toolkit:Popup IsOpen="{Binding IsPopupOpen, Mode=TwoWay}">
    <!-- 弹窗内容 -->
</toolkit:Popup>

2. 利用PopupService跟踪实例

通过IPopupService打开弹窗时,在ViewModel中持有当前弹窗实例,无需对外传递句柄即可主动关闭:

private readonly IPopupService _popupService;
private Popup _currentPopup;

public MyViewModel(IPopupService popupService)
{
    _popupService = popupService;
    OpenPopupCmd = new AsyncRelayCommand(OpenPopupAsync);
    ClosePopupCmd = new RelayCommand(ClosePopup);
}

public IAsyncRelayCommand OpenPopupCmd { get; }
public IRelayCommand ClosePopupCmd { get; }

private async Task OpenPopupAsync()
{
    _currentPopup = new CustomPopup();
    await _popupService.ShowPopupAsync(_currentPopup);
    _currentPopup = null; // 关闭后清空引用,避免内存泄漏
}

private void ClosePopup()
{
    if (_currentPopup != null && _currentPopup.IsOpen)
    {
        MainThread.BeginInvokeOnMainThread(() => _currentPopup.Close());
    }
}

内容的提问来源于stack exchange,提问作者ss1969

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:51:02