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

.NET MAUI ObjectDisposedException异常求助:无法访问已释放对象

.NET MAUI应用崩溃:System.ObjectDisposedException异常

我的.NET MAUI应用崩溃,抛出异常:

System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'Microsoft.Maui.Platform.LayoutViewGroup'.'

我使用WeakReferenceMessenger实现视图刷新,自定义了RefreshManager类:

using CommunityToolkit.Mvvm.Messaging;

namespace NuesWarehouseMobile.Utilities
{
    internal static class RefreshManager
    {
        public static void RequestRefresh(string viewName)
        {
            WeakReferenceMessenger.Default.Send(new RefreshViewMessage(viewName));
        }

        public static void Register<TRecipient>(TRecipient recipient, Action<RefreshViewMessage> action) where TRecipient : class
        {
            WeakReferenceMessenger.Default.Register<RefreshViewMessage>(recipient, (r, m) => action(m));
        }
    
        public static void Unregister<TRecipient>(TRecipient recipient) where TRecipient : class
        {
            WeakReferenceMessenger.Default.Unregister<RefreshViewMessage>(recipient);
        }
    }
    
    public class RefreshViewMessage
    {
        public string ViewName { get; }
    
        public RefreshViewMessage(string viewName)
        {
            ViewName = viewName;
        }
    }
}

但弱引用机制触发了上述异常。尝试过缓存视图避免被释放、使用强引用等方法均未解决;其他视图更新方式无效,必须依赖消息机制。

以下是DocumentiDaEvadereView的视图逻辑代码:

using CommunityToolkit.Mvvm.Messaging;
using NuesWarehouse.Core.Dtos;
using NuesWarehouseMobile.Core.Interfaces.Services;
using NuesWarehouseMobile.Utilities;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;

namespace NuesWarehouseMobile.MenuView;

public partial class DocumentiDaEvadereView : ContentView
{
    private readonly IDocumentService _documentService;
    private int currentPage = 1;
    private int perPage = 20;
    private string stato = "Aperto-Picking-Parzialmente Evaso"; //SPAZIO VUOTO
    private string tipoDoc = "OrdineVendita";

    private bool _isLoading;
    public bool IsLoading
    {
        get => _isLoading;
        set
        {
            _isLoading = value;
            OnPropertyChanged(nameof(IsLoading));
        }
    }

    public ObservableCollection<DocumentiDto> Documents { get; set; }

    public ICommand LoadDocumentsCommand { get; }
    public ICommand NextPageCommand { get; }
    public ICommand PreviousPageCommand { get; }

    private int _currentPageDisplay;
    public int CurrentPageDisplay
    {
        get => _currentPageDisplay;
        set
        {
            _currentPageDisplay = value;
            OnPropertyChanged();
        }
    }

    public DocumentiDaEvadereView()
    {
        InitializeComponent();

        RefreshManager.Register(this, OnRefreshViewMessageReceived);

        IsLoading = true;
        _documentService = Application.Current.Handler.MauiContext.Services.GetService<IDocumentService>();
        Documents = new ObservableCollection<DocumentiDto>();
        LoadDocumentsAsync();

        LoadDocumentsCommand = new Command(async () => await LoadDocumentsAsync());
        NextPageCommand = new Command(async () => await LoadNextPageAsync());
        PreviousPageCommand = new Command(async () => await LoadPreviousPageAsync());

        double screenHeight = DeviceDisplay.MainDisplayInfo.Height / DeviceDisplay.MainDisplayInfo.Density;
        double gridHeight = screenHeight * 0.6;
        DocumentsDataGrid.HeightRequest = gridHeight;

        BindingContext = this;
        LoadDocumentsCommand.Execute(null);
        
        IsLoading = false;
    }

    private async Task LoadDocumentsAsync(int page = 1)
    {
        var documents = await _documentService.GetDocumentsByPage(page, perPage, tipoDoc, stato);
        Documents.Clear();
        foreach (var document in documents)
        {
            if (document.Stato == "Evaso")
            {
                document.StatoColor = "Green";
            }
            else if (document.Stato == "Parzialmente Evaso")
            {
                document.StatoColor = "Orange";
            }
            else
            {
                document.StatoColor = "Yellow";
            }
            Documents.Add(document);
        }
        CurrentPageDisplay = currentPage;

        DocumentsDataGrid.ItemsSource = null;
        DocumentsDataGrid.ItemsSource = Documents;
    }

    private async Task LoadNextPageAsync()
    {
        currentPage++;
        await LoadDocumentsAsync(currentPage);
    }

    private async Task LoadPreviousPageAsync()
    {
        if (currentPage > 1)
        {
            currentPage--;
            await LoadDocumentsAsync(currentPage);
        }
    }

    // 打开新视图的方法
    private void ApriNuovaPaginaCommand(object sender, EventArgs e)
    {
        IsLoading = true;
        var button = sender as Button;
        if (button?.CommandParameter is string documentId)
        {
            Debug.WriteLine(documentId);
            Navigation.PushAsync(new EvasioneDocumentoPage(documentId));
        }
        else
        {
            Debug.WriteLine("====ATTENZIONE, PARAMETRO documentId NON PASSATO CORRETTAMENTE====");
        }
        IsLoading = false;
    }

    private void OnRefreshViewMessageReceived(RefreshViewMessage message)
    {
        if (message.ViewName == "EvasioneDocumento") // 参数检查
        {
            Debug.WriteLine("Messaggio ricevuto in DocumentsView! Aggiorno i documenti...");
            _ = LoadDocumentsAsync(1); // 重新加载文档
        }
    }

    ~DocumentiDaEvadereView()
    {
        // 注销以避免内存泄漏
        RefreshManager.Unregister(this);
    }
}
解决方案

问题根源

  1. 析构函数不可靠:析构函数由GC在不确定时机调用,此时视图可能已被释放,WeakReferenceMessenger仍可能在视图处置后触发回调,导致访问已释放的LayoutViewGroup。
  2. 未检查视图状态:消息回调中直接执行UI更新,未验证视图是否处于可用(未处置)状态。

修复步骤

1. 用生命周期方法替换析构函数

对于ContentView,重写OnParentRemoved方法,确保视图被移除时立即注销消息订阅:

protected override void OnParentRemoved()
{
    base.OnParentRemoved();
    RefreshManager.Unregister(this);
}

如果该视图是导航页面的一部分,可结合OnDisappearing做更精准的注销:

protected override void OnDisappearing()
{
    base.OnDisappearing();
    if (!Navigation.NavigationStack.Contains(this))
    {
        RefreshManager.Unregister(this);
    }
}

2. 消息回调中检查视图状态

在接收消息时,先验证视图未被处置,再执行UI更新:

private void OnRefreshViewMessageReceived(RefreshViewMessage message)
{
    if (message.ViewName == "EvasioneDocumento" && !IsDisposed)
    {
        Debug.WriteLine("Messaggio ricevuto in DocumentsView! Aggiorno i documenti...");
        // 确保在主线程执行UI更新
        MainThread.BeginInvokeOnMainThread(async () => await LoadDocumentsAsync(1));
    }
}

3. 确保UI操作在主线程执行

优化LoadDocumentsAsync,将所有UI更新逻辑包裹在主线程调用中:

private async Task LoadDocumentsAsync(int page = 1)
{
    IsLoading = true;
    var documents = await _documentService.GetDocumentsByPage(page, perPage, tipoDoc, stato);
    
    await MainThread.InvokeOnMainThreadAsync(() =>
    {
        Documents.Clear();
        foreach (var document in documents)
        {
            document.StatoColor = document.Stato switch
            {
                "Evaso" => "Green",
                "Parzialmente Evaso" => "Orange",
                _ => "Yellow"
            };
            Documents.Add(document);
        }
        CurrentPageDisplay = currentPage;
        DocumentsDataGrid.ItemsSource = Documents;
    });
    
    IsLoading = false;
}

额外建议

避免在构造函数中直接调用异步方法,将初始化逻辑移至OnAppearing:

protected override async void OnAppearing()
{
    base.OnAppearing();
    if (Documents.Count == 0)
    {
        await LoadDocumentsAsync();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 21:35:54