Home > Software design >  System.ArgumentOutOfRangeException: 'Index must be within the bounds of the List. when adding a
System.ArgumentOutOfRangeException: 'Index must be within the bounds of the List. when adding a

Time:05-01

This happens on a Xamarin.Forms app. It is a simple app about making lists.

I got two pages: one with the lists and another to show the list's items. The error happens on the latter when I try to add a new item to an ObservableCollection.

This is a simplified version of the 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;
        });
    }

The error happens on the Items.Add(listITem); call.

Tried wrapping the call on Device.InvokeOnMainThreadAsync with no luck.

The curious thing is it just happens on the second time I access the page.

The full project can be found on my GitHub: https://github.com/JeffersonAmori/ListApp

CodePudding user response:

It wouldn't surprise me if the error stopped happening if you lessen the chance for XForms to intervene before you've done the Add. (I'm hypothesizing that the underlying problem is a latent XForms bug.):

    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);
    }

CAVEAT #1: This doesn't fix any underlying problem, it just might make it happen less often. If its an XF problem, you might have to wrap your code in try..catch. In catch, determine if the item got added. If not try adding it again. Messy.

CAVEAT #2: This assumes OnAddItem is only called from MainThread. That will be true, if you never call it directly yourself - UI code will invoke the command on main thread.

CAVEAT #3: Assumes that all of the types involved (especially ListItem and _currentList.ListItems) are not UI types - they have no dependencies on Xamarin.Forms View classes.

  • Related