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

.NET MAUI迁移后查看历史日志触发COM组件未检测到异常

.NET MAUI迁移后切换日志页触发COMException异常

我们将旧Xamarin.Forms项目迁移至.NET MAUI后,应用能正常启动并完成登录,但在登录后的用户操作日志记录页点击箭头查看前一日志时,SetModelBinding方法中触发System.Runtime.InteropServices.COMException异常,错误信息为No installed components were detected,错误发生在代码标注位置。


脱敏代码

public DailyLogPage(int dailylogid = 0)
{
    dlog = new DailyLogControl();
    myid = Guid.NewGuid();
    loadingControl = new LoadingControl();
    this.Title = "Daily Log";
    IconImageSource = "Navigation_Add.png";
    this.Disappearing += MainViewPage_Disappearing;
    BasePage.InitPage(this);

    SetDataRepository();
    InitializeCriteria();
    CreateDailyLogUI();
    CreatePageActions();
    SetMessagingCenterSubscriptions();
    SetHomeOpco();
    AutoSaveAndSync();
}

#region UICode
private void CreateDailyLogUI()
{
    CreateTxtDevice();
    CreateGrid();
    CreateMainScrollView();

    stackBody.SetBinding(StackLayout.BackgroundColorProperty, "PageColor.BodyBackground");
    this.SetBinding(ContentPage.BackgroundColorProperty, "PageColor.PageBackground");
    SetModelBinding(dailylogCriteria.DailyLog);
}

private void CreateTxtDevice()
{
    txtDevice = new AutoCompleteView
    {
        Placeholder = "Enter text",
        ShowSearchButton = false,
        SuggestionsHeightRequest = 120,
        SelectedCommand = new Command(() => { }),
    };
    txtDevice.EntText.IsVisible = false;
}

private void CreateGrid()
{
    AddButton = new Button
    {
        HeightRequest = 45,
        ImageSource = "add_data.png",
        BackgroundColor = Colors.Transparent
    };
    AddButton.Clicked += AddButton_Clicked;

    sideStack = new StackLayout();
    grid = new Grid
    {
        ColumnDefinitions =
            {
            new ColumnDefinition { Width = new GridLength(.05, GridUnitType.Star) },
            new ColumnDefinition { Width = new GridLength(.5, GridUnitType.Star) },
            new ColumnDefinition { Width = new GridLength(9.45, GridUnitType.Star) },
            }
    };

    grid.Children.Add(sideStack);
    Grid.SetColumn(sideStack, 0);

    sideStack.Children.Add(AddButton);
    grid.Children.Add(sideStack);
    Grid.SetColumn(sideStack, 1);
}

private void CreateMainScrollView()
{
    dailyLogHeader = new DailyLogStepControlHeader() { IsClippedToBounds = true };
    stack = new StackLayout { };
    var scrollView = new StackLayout { Children = { stack } };
    dailyLogSyncControl = new DailyLogSyncControl { VerticalOptions = LayoutOptions.End, IsClippedToBounds = true };

    //Create StackBody
    stackBody = new StackLayout
    {
        Padding = new Thickness(5, 5, 5, 30),
        Children =
        {
            dailyLogHeader,
            scrollView
        }
    };

    //Create StackMain
    stackmain = new StackLayout
    {
        Padding = new Thickness(7),
        Children =
        {
            dlog,
            stackBody,
            dailyLogSyncControl
        }
    };

    //Create MainScrollView
    mainScrollView = new ScrollView { Content = stackmain };
    Content = mainScrollView;
}
private void CreatePageActions()
{
    #region actions
    //wire up the next & previous page action button commands here
    Action NextPage = async () =>
    {
        if (!model.DailyLog.ValidData)
        {
            await DisplayAlert(UIConstants.TITLE, "Location & Purpose are required.", "Ok");
            return;
        }

        await model.SaveData(true);

        if (dailylogCriteria.HasNext)
        {
            dailylogCriteria = _data.GetNextDailyLog(model.DailyLog.DailyLogId);
        }
        else
        {
            var newDlog = CreateDailyLog(false);
            await _data.SaveDailyLog(newDlog);
            dailylogCriteria = _data.GetLastDailyLog();
        }
        SetModelBinding(dailylogCriteria.DailyLog);

    };

    Action PreviousPage = async () =>
     {
         await model.SaveData(true);

         if (dailylogCriteria.HasPrevious)
         {
             dailylogCriteria = _data.GetPreviousDailyLog(model.DailyLog.DailyLogId);
         }
         else
         {
             return;
         }

         SetModelBinding(dailylogCriteria.DailyLog);
     };

    Action<int> GotoPage = (pageid) =>
    {
        //CAN THIS FUNCTION EVER BE RUN?
        //await model.SaveData(true);
        //if (pageid > 0 && pageid <= dailylogCriteria.TotalDailyLogs)
        //{
        //    SetModelBinding(model.DailyLog);
        //}

    };
    #endregion

    dlog.SetNextPage(NextPage);
    dlog.SetPreviousPage(PreviousPage);
    dlog.SetGotoPage(GotoPage);
    dlog.IsClippedToBounds = true;
}
private DailyLog CreateDailyLog(bool isLogin)
{
    var newDlog = new DailyLog();
    //This is redacted data. just setting DailyLog properties to user data(ie, username, opco, etc)

    return newDlog;
}

private void SetModelBinding(DailyLog dailyLog)
{
    stackmain.Children.Remove(dlog);

    model = new DailyLogViewModel(dailyLog)
    {
        IsFetching = true,
        PageCount = dailylogCriteria.TotalDailyLogs,
        PageNo = dailylogCriteria.CurrentPage
    };

    BindingContext = model;
    
    stackmain.Children.Insert(0, dlog);   <---- 错误触发位置
    stack.Children.Clear();
    ToggleLoading();
    //remove old controls
    Task.Delay(300).ContinueWith((t) =>
    {
        this.Dispatcher.Dispatch(async () =>
        {
            try
            {
                await FetchData();
            }
            catch
            { //try one more attempt from sql lite
                await ReFetchData();
            }
        });
    });
}

问题分析与修复方案

核心原因

这个异常源于MAUI控件生命周期管理的兼容性问题:

  1. 复用同一个DailyLogControl实例反复从可视化树中移除/插入,导致MAUI底层COM容器的控件状态混乱;
  2. 控件操作未严格遵循MAUI的UI线程规范,即使当前操作在主线程,异步回调的时序也可能引发控件状态不一致。

修复步骤

  1. 创建新控件实例替代复用
    在移除旧dlog后,重新创建DailyLogControl实例并绑定命令,避免复用旧实例的生命周期问题:
    private void SetModelBinding(DailyLog dailyLog)
    {
        stackmain.Children.Remove(dlog);
        // 重新创建控件实例
        dlog = new DailyLogControl();
        // 重新绑定操作命令
        dlog.SetNextPage(NextPage);
        dlog.SetPreviousPage(PreviousPage);
        dlog.SetGotoPage(GotoPage);
        dlog.IsClippedToBounds = true;
    
        model = new DailyLogViewModel(dailyLog)
        {
            IsFetching = true,
            PageCount = dailylogCriteria.TotalDailyLogs,
            PageNo = dailylogCriteria.CurrentPage
        };
    
        BindingContext = model;
        
        stackmain.Children.Insert(0, dlog);
        stack.Children.Clear();
        ToggleLoading();
        
        Task.Delay(300).ContinueWith((t) =>
        {
            this.Dispatcher.Dispatch(async () =>
            {
                try
                {
                    await FetchData();
                }
                catch
                {
                    await ReFetchData();
                }
            });
        });
    }
    
  2. 强制UI操作在主线程执行
    若SetModelBinding可能被异步逻辑触发,将所有控件操作包裹在Dispatcher.Dispatch中:
    this.Dispatcher.Dispatch(() => {
        stackmain.Children.Remove(dlog);
        // ...其他UI操作
        stackmain.Children.Insert(0, dlog);
    });
    
  3. 排查DailyLogControl内部适配问题
    检查DailyLogControl是否使用了Xamarin专属API(如Renderer、特定原生控件绑定),需替换为MAUI对应的实现(如Handler机制)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:14:52