Unhandled Exception when adding/removing items from ItemSource

18 posts, 0 answers
  1. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 15 Mar 2018 Link to this post

    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?

  2. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 15 Mar 2018 Link to this post

    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>

     

     

     

  3. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 15 Mar 2018 Link to this post

    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
  4. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 16 Mar 2018 Link to this post

    Your example works, but when you add a second field to MyItemModel, you get the same eror as I was getting. 
  5. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 16 Mar 2018 Link to this post

    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?
  6. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 16 Mar 2018 Link to this post

    Additionally, after adding and deleting a number of items using the Add and Remove buttons, the left/right slide buttons stop working.
  7. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 16 Mar 2018 Link to this post

    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
  8. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 19 Mar 2018 Link to this post

    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"

  9. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 19 Mar 2018 Link to this post

    That exception occurs when the collection is initially empty and I press the Add button.
  10. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 19 Mar 2018 Link to this post

    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
  11. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 21 Mar 2018 in reply to Lance | Technical Support Engineer, Principal Link to this post

    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.
  12. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 23 Mar 2018 Link to this post

    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
  13. Bill
    Bill avatar
    33 posts
    Member since:
    Nov 2017

    Posted 26 Mar 2018 Link to this post

    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.

  14. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 28 Mar 2018 Link to this post

    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
  15. Lance | Technical Support Engineer, Principal
    Admin
    Lance | Technical Support Engineer, Principal avatar
    1002 posts

    Posted 29 Mar 2018 Link to this post

    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
  16. Shawn
    Shawn avatar
    13 posts
    Member since:
    Feb 2012

    Posted 01 Aug 2018 Link to this post

    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.

  17. Shawn
    Shawn avatar
    13 posts
    Member since:
    Feb 2012

    Posted 01 Aug 2018 in reply to Shawn Link to this post

    I just tested on Android and it seems to work fine.  So this invalid state only occurs on iOS.
  18. Yana
    Admin
    Yana avatar
    4865 posts

    Posted 06 Aug 2018 Link to this post

    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
Back to Top