Steve Andrews
Steve Andrews

Reputation: 559

Xamarin Forms - Ordered List and ArgumentOutOfRange

I've been fighting with this exception (and subsequent hard crash) for about a day now. I have a ListView in Xamarin Forms that does grouping headers. As much as I can gather from the Call Stack, the error is occurring because of the grouping. I verified this by removing the grouping from the ListView and the app displays the data without crashing. I've even diff'd the code back a few weeks and nothing stands out. This also only occurs on a new launch and subsequent launches don't throw this exception or crash.

How else might I go about further debugging and resolving this?

Exception stack trace

List View XAML

<ListView x:Name="ListViewItems"
            ItemsSource="{Binding ItemsGrouped}"
            GroupDisplayBinding="{Binding Key.DisplayText}"
            IsGroupingEnabled="true">
    <ListView.GroupHeaderTemplate>
        <DataTemplate>
            <ViewCell>
                <cells:MyGroupHeaderView/>
            </ViewCell>
        </DataTemplate>
    </ListView.GroupHeaderTemplate>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <cells:ItemCellView />
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

MyGroupHeaderView XAML

<StackLayout VerticalOptions="Center">
    <Label Text="{Binding Key.DisplayText}">
    </Label>
</StackLayout>

Grouping C# Code

var result = from s in myItems
    orderby s.ItemDateTime
    group s by s.GetSortGroupInfo()
    into itemGroup
    select new Grouping<GroupInfo, ItemViewModel>(itemGroup.Key, itemGroup);

GroupInfo class (used for sorting and display)

public class GroupInfo
{
    public string DisplayText { get; set; }

    public int SortValue { get; set; }
}

Bound Property and Setter

public ObservableRangeCollection<Grouping<GroupInfo, ItemViewModel>> ItemsGrouped { get; }
    = new ObservableRangeCollection<Grouping<GroupInfo, StopViewModel>>();

this.ItemsGrouped.ReplaceRange(
    this.MyItems.GroupByDate()
        .OrderBy(f => f.Key.SortValue)
        .ToList());

Upvotes: 4

Views: 874

Answers (2)

Steve Andrews
Steve Andrews

Reputation: 559

I found the problem. Turns out when I updated James Montemagno's Xam.Plugin.Geolocator from 3.04 to 4.01, it also switched from PCL to .NET Standard. When I rolled it back, ListView grouping is working again. Unfortunately, I can't get GeoLocation to work with 3.04, and somehow, of all things, I get the above error with 4.01.

Upvotes: 0

Damian
Damian

Reputation: 4773

The underlying issue is that TemplatedItemsList isn't handling the Reset generated by ReplaceRange well.

As a short-term workaround, you can avoid the crash by not using ReplaceRange:

ItemsGrouped.Clear();
foreach (var item in MyItems.GroupByDate().OrderBy(f => f.Key.SortValue))
{
    ItemsGrouped.Add(item);
}

Or set the caching strategy to recycle:

<ListView x:Name="ListViewItems"
        ItemsSource="{Binding ItemsGrouped}"
        IsGroupingEnabled="true"
        CachingStrategy="RecycleElement">

Interestingly if you follow the call chain you come to UnhookItem which has this comment:

//Hack: the cell could still be visible on iOS because the cells are reloaded after this unhook 
//this causes some visual updates caused by a null datacontext and default values like IsVisible
if (Device.RuntimePlatform == Device.iOS && CachingStrategy == ListViewCachingStrategy.RetainElement)
    await Task.Delay(100);
item.BindingContext = null;

The crash to me looks to be caused by an attempt to re-render the template because the binding context has changed ... so maybe this hack is also applicable to Android ...

You are setting both the GroupDisplayBinding and the GroupHeaderTemplate in the sample code above ... I assume because you were experimenting ... you should only have one set at a time ... using GroupDisplayBinding would also avoid the issue I expect.

Upvotes: 1

Related Questions