System.ArgumentOutOfRangeException: '索引必须在列表的范围内。向 ObservableCollection 添加新项目时

System.ArgumentOutOfRangeException: 'Index must be within the bounds of the List. when adding a new item to ObservableCollection

这发生在 Xamarin.Forms 应用上。 这是一个关于制作清单的简单应用程序。

我有两页:一页包含列表,另一页显示列表的项目。当我尝试将新项目添加到 ObservableCollection.

时,错误发生在后者

这是 ViewModel 的简化版本:

    public ObservableCollection<ListItem> Items { get; }
    public Command AddItemCommand { get; }

    public ItemsViewModel()
    {  
        Items = new ObservableCollection<ListItem>();

        AddItemCommand = new Command(OnAddItem);;
    }

    private async void OnAddItem()
    {
        await Device.InvokeOnMainThreadAsync(async () =>
        {
            if (string.IsNullOrEmpty(NewItemText))
                return;

            ListItem listITem = new ListItem()
            {
                ListId = _currentList.ListId,
                Id = Guid.NewGuid().ToString(),
                Text = NewItemText
            };

            Items.Add(listITem);

            _currentList.ListItems.Add(listITem);
            await DataStore.UpdateItemAsync(_currentList);

            NewItemText = string.Empty;
        });
    }

错误发生在 Items.Add(listITem); 调用中。

尝试在 Device.InvokeOnMainThreadAsync 结束通话,但没有成功。

奇怪的是它发生在我第二次访问该页面时。

完整的项目可以在我的 GitHub 上找到: https://github.com/JeffersonAmori/ListApp

如果您在完成 Add 之前减少 XForms 干预的机会,那么如果错误不再发生,我也不会感到惊讶。 (我假设根本问题是潜在的 XForms 错误。):

    private async void OnAddItem()
    {
        // --- ASSUME we are already on MainThread. ---
        // --- Avoid "await" (and any "..Invoke..Async") until after "Items.Add". ---
        
        if (string.IsNullOrEmpty(NewItemText))
            return;

        ListItem listITem = new ListItem()
        {
            ListId = _currentList.ListId,
            Id = Guid.NewGuid().ToString(),
            Text = NewItemText
        };
        
        Items.Add(listITem);
        
        _currentList.ListItems.Add(listITem);
        NewItemText = string.Empty;

        // ----- await Potentially slow operation(s) AFTER all quick UI calls. -----
        
        await DataStore.UpdateItemAsync(_currentList);
    }

注意事项 #1:这并不能解决任何潜在的问题,它只是可能会减少它发生的频率。如果它是 XF 问题,您可能必须将代码包装在 try..catch 中。在 catch 中,确定该项目是否已添加。如果没有尝试再次添加它。凌乱。

CAVEAT #2:假设 OnAddItem 仅从 MainThread 调用。如果您从未自己直接调用它,那将是正确的 - UI 代码将在主线程上调用该命令。

注意事项 #3:假设所有涉及的类型(尤其是 ListItem 和 _currentList.ListItems)都不是 UI 类型——它们不依赖于 Xamarin.Forms View 类.

我认为问题在于应用程序在页面之间导航的方式。 之前 - 当错误发生时 - 它是通过弹出菜单使用弹出路由导航的。现在,我在代码隐藏中显式注册路由,并在用户单击 ColletcionView 上的其中一项时以编程方式将它们推送到导航堆栈中。这样我就不会再重复使用页面了。

    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute(nameof(ItemsPage), typeof(ItemsPage));
    }

然后

    async private void OnListSelected(List list)
    {
        if (list == null)
            return;

        await Shell.Current.GoToAsync($"{nameof(ItemsPage)}?{nameof(ItemsViewModel.ListId)}={list.ListId}");
    }

通过这种方式,我似乎可以让线程正常运行,并且在页面之间导航时总是获得一个新列表,因为它们是 pushed/popped into/from 导航堆栈。

请记住,这个答案有一些猜测,因为我刚刚回到 Xamari.Forms,我对 Shell 还很陌生。

再次感谢 toolmakersteve 的见解。

干杯。