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 的见解。
干杯。
这发生在 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 的见解。
干杯。