This is a migrated thread and some comments may be shown as answers.

Unhandled Exception when adding/removing items from ItemSource

17 Answers 1460 Views
SlideView
This is a migrated thread and some comments may be shown as answers.
Bill
Top achievements
Rank 1
Bill asked on 15 Mar 2018, 02:45 PM

When I add or remove items from my ObservableCollection that is used as the ItemSource for a SlideView, I get the following error:

"System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index"

Is there some specific process I should be following when adding/removing items from the bound ObservableCollection for a SlideView that is already rendered?

17 Answers, 1 is accepted

Sort by
0
Bill
Top achievements
Rank 1
answered on 15 Mar 2018, 04:16 PM

Here's my viewmodel:

public class Photo : INotifyPropertyChanged
    {
        public string FileName
        {
            get => FileName;
            set
            {
                FileName = value;
                OnPropertyChanged("FileName");
            }
        }
 
        public long DateTime
        {
            get => DateTime;
            set
            {
                DateTime = value;
                OnPropertyChanged("DateTime");
            }
        }
 
        public Photo(string filename, long dateTime)
        {
            FileName = filename;
            DateTime = dateTime;
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

 

In my form constructor:

_photos = new ObservableCollection<Photo>();

SlideViewPhotos.ItemsSource = _photos;

 

And in my XAML:

<primitives:RadSlideView x:Name="SlideViewPhotos" HeightRequest="360" Padding="0,15,0,0" IsInfiniteScrollingEnabled="True">
            <primitives:RadSlideView.ItemTemplate>
                <DataTemplate>
                    <ContentView>
                        <Grid WidthRequest="200">
                            <Image Source="{Binding FileName}" Margin="0,0,0,50"/>
                        </Grid>
                    </ContentView>
                </DataTemplate>
            </primitives:RadSlideView.ItemTemplate>
        </primitives:RadSlideView>

 

 

 

0
Lance | Manager Technical Support
Telerik team
answered on 15 Mar 2018, 05:52 PM
Hello Bill,

I am not able to replicate what you're seeing, here's an animated GIF of my test at runtime:




Here's the code:

<ContentPage ...>
 
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
         
        <telerikPrimitives:RadSlideView x:Name="slideView" >
            <telerikPrimitives:RadSlideView.ItemTemplate>
                <DataTemplate>
                    <ContentView>
                        <Label Text="{Binding DisplayText}" TextColor="#007ACC" HorizontalTextAlignment="Center"/>
                    </ContentView>
                </DataTemplate>
            </telerikPrimitives:RadSlideView.ItemTemplate>
        </telerikPrimitives:RadSlideView>
         
        <StackLayout Orientation="Horizontal" Grid.Row="1">
            <Button x:Name="AddButton" Text="Add Item" Clicked="AddButton_OnClicked"/>
            <Button x:Name="RemoveButton" Text="Remove Last Item" Clicked="RemoveButton_OnClicked"/>
        </StackLayout>
    </Grid>
</ContentPage>

namespace DynamicItems.Portable
{
    public partial class StartPage : ContentPage
    {
        public StartPage()
        {
            InitializeComponent();
 
            slideView.ItemsSource = MySlideViewItems;
        }
 
        public ObservableCollection<MyItemModel> MySlideViewItems { get; } = new ObservableCollection<MyItemModel>
        {
            new MyItemModel { DisplayText = "Item 1" },
            new MyItemModel { DisplayText = "Item 2" },
            new MyItemModel { DisplayText = "Item 3" }
        };
 
        private void AddButton_OnClicked(object sender, EventArgs e)
        {
            MySlideViewItems.Add(new MyItemModel { DisplayText = $"Item {MySlideViewItems.Count + 1} (added)" });
        }
 
        private void RemoveButton_OnClicked(object sender, EventArgs e)
        {
            MySlideViewItems.Remove(MySlideViewItems.LastOrDefault());
        }
    }
 
    public class MyItemModel
    {
        public string DisplayText { get; set; }
    }
}


Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 16 Mar 2018, 03:54 PM
Your example works, but when you add a second field to MyItemModel, you get the same eror as I was getting. 
0
Bill
Top achievements
Rank 1
answered on 16 Mar 2018, 04:07 PM
Sorry, I got that wrong. The problem is when MySlideViewItems is initialised with no items. That's when the exception is thrown. So, how to handle this situation where MySlideViewItems initially contains no items?
0
Bill
Top achievements
Rank 1
answered on 16 Mar 2018, 04:44 PM
Additionally, after adding and deleting a number of items using the Add and Remove buttons, the left/right slide buttons stop working.
0
Lance | Manager Technical Support
Telerik team
answered on 16 Mar 2018, 10:10 PM
Hi Bill,

I noticed you have syntax error in your data model, your property accessors are recursive.

The highlighted code below will infinitely reference itself when you first try to show the FileName and cause an exception

public class Photo : INotifyPropertyChanged
{
    public string FileName
    {
        get => FileName;
        set
        {
            FileName = value;
            OnPropertyChanged("FileName");
        }
    }
....
}

If you're not familiar with the new C# Expression Body syntax "=>", it's the same as doing this:

public string FileName
{
 get
 {
    return FileName;
 }
}

Now you can see the recursion easier. If you get FileName, it will get FileName infinitely.


Fix

To fix this, use a property backing field,:

private string _fileName;
public string FileName
{
    get => _fileName;
    set
    {
        _fileName = value;
        OnPropertyChanged();
    }
}


Also to prevent multiple UI bindings, which can lead to flickering an unnecessary updates, don't call OnPropertyChanged unless the value is updated. 

private string _fileName;
public string FileName
{
    get => _fileName;
    set
    {
        if (_fileName == value)
            return;
 
        _fileName = value;
        OnPropertyChanged();
    }
}


Lastly, I strongly discourage you from using a property name of  "DateTime" because it's a C# type already. Try something like "Timestamp" instead

private long _timestamp;
public long Timestamp
{
    get => _timestamp;
    set
    {
        if (_timestamp == value)
            return;
 
        _timestamp = value;
        OnPropertyChanged();
    }
}


Complete Updated Model

Here is the complete, refactored model (you do not need to change other code because the class instantiates the same)

public class Photo : INotifyPropertyChanged
{
    // backing fields
    private string _fileName;
    private long _timestamp;
 
    // constructor
    public Photo(string filename, long timestamp)
    {
        FileName = filename;
        Timestamp = timestamp;
    }
 
    // Properties
    public string FileName
    {
        get => _fileName;
        set
        {
            if (_fileName == value)
                return;
 
            _fileName = value;
            OnPropertyChanged();
        }
    }
 
    public long Timestamp
    {
        get => _timestamp;
        set
        {
            if (_timestamp == value)
                return;
 
            _timestamp = value;
            OnPropertyChanged();
        }
    }
 
    // Property Changed
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

After making that change, I was able to get your original code working.

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 19 Mar 2018, 10:29 AM

Thanks for the recommendations. I  made the changes, and the code works, however, I still have this exception when the MySlideView observable collection is initially empty:

"System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index"

0
Bill
Top achievements
Rank 1
answered on 19 Mar 2018, 10:31 AM
That exception occurs when the collection is initially empty and I press the Add button.
0
Lance | Manager Technical Support
Telerik team
answered on 19 Mar 2018, 03:31 PM
Hello Bill,

I tested this again and was not able to replicate the issue you're seeing, adding an item when the collection is empty works as expected in my test.  Here is the relevant code:

public partial class StartPage : ContentPage
{
    private readonly ObservableCollection<Photo> _photos;
 
    public StartPage()
    {
        InitializeComponent();
 
        _photos = new ObservableCollection<Photo>();
 
        slideView.ItemsSource = _photos;
    }
 
    private void AddButton_OnClicked(object sender, EventArgs e)
    {
        _photos.Add(new Photo("Photo", 1234));
    }
}

Are you sure you're using the latest version of UI for Xamarin? If you right click on any Telerik reference and select "Properties", you should see 2018.1.315 in the "Version" box.

Next Steps

So that I can get you a solution as fast as possible, can you please update my attached demo with your code so that it replicates the problem and send it back to me? With that, I'll be able directly debug it and determine the cause.

Once you're replicated it, you have two option s to get me the changes

Option 1: 

Because this is a forum thread, you can't attach a ZIP. Instead you can use a file service (i.e. DropBox, OneDrive) and share the download link. Just be sure to delete the bin and obj folders from each project before zipping up the application, this will significantly reduce the file size).

Option 2:

Reply back using a code block for each class/page, like I did above for StartPage.xaml.cs.  If you choose this option, please make sure to let me know all of the platform specifics (target platform, what SDK level did you choose, etc).

I look forward to your reply.


Side Note: During my investigation I did notice another issue, but it is unrelated to your problem. I've logged it here SlideView: Initial item not binding correctly.

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 21 Mar 2018, 10:46 AM
I was using older DLLs. I updated to the latest version and it now works OK, except that after adding, deleting, then adding items, the left/right slider buttons stop working. No error occurs, they just stop working.
0
Lance | Manager Technical Support
Telerik team
answered on 23 Mar 2018, 07:55 PM
Hello Bill,

I've tried a variety of order-of-operations (add 5 & remove 2 or add 6 & remove 4, etc), I was not able to get the buttons to stop working. after each different test, the button still navigate the remaining items in the SlideView.

Can you please run my demo (attached to my last reply) and see if your steps replicate the behavior.

- If you were able to reproduce it:  Let me know the order of operations that you took so I can reproduce it on my side.

- If you are not able to reproduce it: The issue lies elsewhere in your application. In order me to help find you a solution, please share the rest of your code, or update my demo, so that I can reproduce it.

I understand and appreciate that it takes a little effort to put together a reproducible, however it's almost always the fastest way for me to get you to a solution (or workaround).

I look forward to your reply.

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 26 Mar 2018, 10:16 AM

I downloaded you solution files and I was able to break it. Follow these steps:

1. Add 2 items

2. Press >

3. Press remove button

4. Add 1 item

5. Press >

6. Press remove button

7. Add 1 item

8. Press >

9. Press remove button

Notice that the remaining dot is no longer coloured, which indicates that it hasn't been selected. From this point onwards, any items added can not be navigated to using the < and > buttons.

0
Lance | Manager Technical Support
Telerik team
answered on 28 Mar 2018, 09:20 PM
Hello Bill,

Thank you for the steps. With that information, I was able to reproduce the issue with fewer steps and no items removed:

1-  Add 2 items (Observe: bound content doesn't show the values after being added)
2 - Click right arrow (Observe: bound content now appears)
3 - Click left arrow (Observe: SlideView goes to 1st item)
4 - Observe: buttons no longer work

I have escalated this to the SlideView developers for further investigation, thank you for taking the time to help reproduce the issue.


Workaround

I've created a workaround for you to use in the meantime, see the updated demo attached. To summarize the workaround, we disabled the SlideView's buttons and replaced them with your own buttons:

- The SlideView has hidden buttons
- The left button has a left arrow
- The right button has a right arrow

<Grid x:Name="SlideViewGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
             
    <Button Text="&lt;"
            Grid.Column="0"
            VerticalOptions="Center"
            Clicked="LeftButton_OnClicked"/>
             
    <telerikPrimitives:RadSlideView x:Name="slideView"
                                    ShowButtons="False"
                                    Grid.Column="1">
        <telerikPrimitives:RadSlideView.ItemTemplate>
            <DataTemplate>
                <ContentView>
                    <Label Text="{Binding FileName}" TextColor="#007ACC" HorizontalTextAlignment="Center"/>
                </ContentView>
            </DataTemplate>
        </telerikPrimitives:RadSlideView.ItemTemplate>
    </telerikPrimitives:RadSlideView>
             
    <Button Text=">"
            Grid.Column="2"
            VerticalOptions="Center"
            Clicked="RightButton_OnClicked"/>
 
</Grid>

The button's click event increases or decreases the SelectedIndex, which will also provide the same visual effect as you'd get with the built-in buttons.

private void LeftButton_OnClicked(object sender, EventArgs e)
{
    if (slideView.SelectedIndex != 0)
    {
        slideView.SelectedIndex = slideView.SelectedIndex - 1;
    }
    else
    {
        // if you want "infinite scrolling", uncomment this
        // slideView.SelectedIndex = _photos.Count - 1;
    }
}
 
private void RightButton_OnClicked(object sender, EventArgs e)
{
    if (slideView.SelectedIndex < _photos.Count - 1)
    {
        slideView.SelectedIndex = slideView.SelectedIndex + 1;
    }
    else
    {
        // if you want "infinite scrolling", uncomment this
        // slideView.SelectedIndex = 0;
    }
}

NOTE:  If you need infinite scrolling, just wrap the SelectedIndex back to the beginning/end of the indices (uncomment the lines above):

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Lance | Manager Technical Support
Telerik team
answered on 29 Mar 2018, 03:50 PM
Hello Bill,

I wanted to follow up let you know that the developers have identified and fixed the problem. It will be included in an upcoming release (expected next week). I have updated your Telerik points for bringing this to our attention.

Thank you for your patience and understanding while we worked to resolve this.

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Shawn
Top achievements
Rank 2
answered on 01 Aug 2018, 10:52 PM

I think the buttons not working is a symptom of a bigger problem.  I have turned off the buttons and rely on the user to swipe left and right.  If I swipe to the very right to see the last item (let's say item #4 for example), then push a delete button to delete that last item in the ObservableCollection, the RadSlideView updates correctly and now I see item #3, the SlidedIndex event fires with the correct SelectedIndex and the circle indicators show the correct dot.  However, if I now try to swipe left to see Item #2, I see Item#2, but the circle indicators stay on item #3 and the SlidedIndex event fires but still says SelectedIndex is for Item #3 which I use to update my page title.

 

So in summary, deleting an item in the ObservableCollection ItemsSource of RadSlideView leaves the RadSlideView in an invalid state.  You have to swipe a few times to get it back in sync.

0
Shawn
Top achievements
Rank 2
answered on 01 Aug 2018, 11:14 PM
I just tested on Android and it seems to work fine.  So this invalid state only occurs on iOS.
0
Yana
Telerik team
answered on 06 Aug 2018, 03:21 PM
Hi Shawn,

Thank you for the detailed description.

I have quickly managed to reproduce the issue with the indicators and I confirm this is an issue on our side, I have logged in our Feedback Portal, you could follow the item at the link below:
https://feedback.telerik.com/Project/168/Feedback/Details/255062-slideview-ios-removing-an-item-from-the-itemssource-leaves-the-slideview-in-in

I am afraid there isn't a suitable workaround I could suggest.

I am sorry for the caused inconvenience.

Regards,
Yana
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Tags
SlideView
Asked by
Bill
Top achievements
Rank 1
Answers by
Bill
Top achievements
Rank 1
Lance | Manager Technical Support
Telerik team
Shawn
Top achievements
Rank 2
Yana
Telerik team
Share this question
or