1

I have a Xamarin Forms App with a Carousel View and my plan is to filter the result by the Priorities shown in a Bottom Tap Bar.

When the View is on the first card, everything works fine, but if you hit one of the filters from a different card, then the first one, a System.ArgumentOutOfRangeException exception got fired.

I tried already a lot of things to set the position of the Carousel View to 0, but in vain.

  • using the CarouselView_PositionChanged event => not working
  • using the TabHost_SelectedTabIndexChanged event => not working
  • setting the property of the CarouselView by the selectedIndex changed from the Tab View from the ViewModel => not working
  • turn it into a CollectionView and everything is working => just looks ugly

Here the page

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
             xmlns:viewModels="clr-namespace:AmsXamarin.ViewModels"
             xmlns:fontAwesome="clr-namespace:FontAwesome"
             xmlns:cells="clr-namespace:AmsXamarin.Cells"
             xmlns:tabs="http://sharpnado.com"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="AmsXamarin.Views.SnagListX.SnagListPage"
             x:Name="mySnagListPage">

    <ContentPage.BindingContext>
        <viewModels:SnagListViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:TabSelectionChangedEventArgs x:Key="TabSelectionChangedEventArgs" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.ToolbarItems>
        <ToolbarItem Command="{Binding ShowMyJobsCommand}"
                     Order="Primary"
                     Priority="0"
                     Text="{Binding MyJobsTitle,Mode=TwoWay}">
        </ToolbarItem>
        <ToolbarItem Command="{Binding AddFaultCommand}"
                     Order="Primary"
                     Priority="0">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FAS"
                                 Glyph="{x:Static fontAwesome:FontAwesomeIcons.Plus}"
                                 Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
                                 Size="Large" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>
        <ToolbarItem Command="{Binding AcceptCommand}"
                     Order="Primary"
                     Priority="0">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FAS"
                                 Glyph="{x:Static fontAwesome:FontAwesomeIcons.UserCheck}"
                                 Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
                                 Size="Large" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <StackLayout>
            <Label Style="{StaticResource LabelLarge}"
                   Text="Snag List"
                   VerticalOptions="CenterAndExpand"
                   HorizontalOptions="CenterAndExpand" />

            <StackLayout>
                <IndicatorView x:Name="snagListIndicator"
                               Margin="0,10,0,0"
                               IndicatorColor="LightGray"
                               SelectedIndicatorColor="LightGray"
                               IndicatorSize="6" />
                <RefreshView Command="{Binding RefreshCommand}"
                             IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                             Style="{StaticResource BaseRefreshView}">
                    <CarouselView x:Name="snagListCV"
                                  Margin="10"
                                  IndicatorView="snagListIndicator"
                                  ItemsLayout="HorizontalList"
                                  ItemsSource="{Binding SnagListPriorities.SnagListApps}"
                                  CurrentItem="{Binding SnagListItem}"
                                  Position="{Binding PositionIdx,Mode=OneWay}"
                                  PositionChanged="CarouselView_PositionChanged"
                                  Loop="False"
                                  IsEnabled="{Binding IsNotBusy}">
                        <CarouselView.EmptyView>
                            <StackLayout Padding="12">
                                <Label Style="{StaticResource LabelMedium}"
                                       HorizontalOptions="Center"
                                       Text="{Binding EmptyMessage,Mode=TwoWay}" />
                            </StackLayout>
                        </CarouselView.EmptyView>

                        <CarouselView.ItemTemplate>
                            <DataTemplate>
                                <cells:SnagListCardX />
                            </DataTemplate>
                        </CarouselView.ItemTemplate>
                    </CarouselView>
                </RefreshView>
            </StackLayout>

            <tabs:TabHostView x:Name="TabHost"
                              Margin="13,0,13,10"
                              BackgroundColor="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Fourth}}"
                              CornerRadius="30"
                              IsSegmented="True"
                              Orientation="Horizontal"
                              TabType="Fixed"
                              SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}"
                              Shades="{StaticResource LightBottomShadow}">
                <tabs:TabHostView.Behaviors>
                    <xct:EventToCommandBehavior EventName="SelectedTabIndexChanged"
                                                Command="{Binding SelectedCommand}"
                                                EventArgsConverter="{StaticResource TabSelectionChangedEventArgs}" />
                </tabs:TabHostView.Behaviors>
                <tabs:TabHostView.Tabs>
                    <tabs:BottomTabItem  Style="{StaticResource BottomTabsMedium}"
                                         Label="All">
                        <tabs:BottomTabItem.Badge>
                            <tabs:BadgeView Style="{StaticResource BadgeViewBase}"
                                            BackgroundColor="{StaticResource All}"
                                            TextColor="White"
                                            Text="{Binding SnagListPriorities.All,Mode=TwoWay}" />
                        </tabs:BottomTabItem.Badge>
                    </tabs:BottomTabItem>
                    <tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
                                        Label="High"
                                        StyleClass="">
                        <tabs:BottomTabItem.Badge>
                            <tabs:BadgeView Style="{StaticResource BadgeViewBase}"
                                            BackgroundColor="{StaticResource High}"
                                            TextColor="White"
                                            Text="{Binding SnagListPriorities.High,Mode=OneWay}" />
                        </tabs:BottomTabItem.Badge>
                    </tabs:BottomTabItem>
                    <tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
                                        Label="Medium">
                        <tabs:BottomTabItem.Badge>
                            <tabs:BadgeView Style="{StaticResource BadgeViewBase}"
                                            BackgroundColor="{StaticResource Medium}"
                                            TextColor="Black"
                                            Text="{Binding SnagListPriorities.Medium,Mode=TwoWay}" />
                        </tabs:BottomTabItem.Badge>
                    </tabs:BottomTabItem>
                    <tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
                                        Label="Low">
                        <tabs:BottomTabItem.Badge>
                            <tabs:BadgeView Style="{StaticResource BadgeViewBase}"
                                            BackgroundColor="{StaticResource Low}"
                                            TextColor="Black"
                                            Text="{Binding SnagListPriorities.Low,Mode=TwoWay}" />
                        </tabs:BottomTabItem.Badge>
                    </tabs:BottomTabItem>
                </tabs:TabHostView.Tabs>
            </tabs:TabHostView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

And the view model

using AmsXamarin.Helpers;
using AmsXamarin.Models;
using AmsXamarin.Services;
using MvvmHelpers;
using MvvmHelpers.Commands;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Command = MvvmHelpers.Commands.Command;

namespace AmsXamarin.ViewModels
{
    public class SnagListViewModel : BaseViewModel
    {
        readonly ISnagListService snagListService;
        List<SnagListAppX> SnagListAppXs;
        SnagListAppX snagListItem;
        public SnagListAppX SnagListItem
        {
            get { return snagListItem; }
            set
            {
                snagListItem = value;
                OnPropertyChanged();
            }
        }
        SnagListPriorities snagListPriorities;
        public SnagListPriorities SnagListPriorities
        {
            get { return snagListPriorities; }
            set
            {
                snagListPriorities = value;
                OnPropertyChanged();
            }
        }
        public AsyncCommand AddFaultCommand { get; }
        public AsyncCommand AcceptCommand { get; }
        public Command RefreshCommand { get; }
        public Command SelectedCommand { get; }
        public Command ShowMyJobsCommand { get; }
        string emptyMessage;
        public string EmptyMessage
        {
            get { return emptyMessage; }
            set
            {
                emptyMessage = value;
                OnPropertyChanged();
            }
        }
        int positionIdx;
        public int PositionIdx
        {
            get { return positionIdx; }
            set
            {
                positionIdx = value;
                OnPropertyChanged();
            }
        }
        int selectedIndex;
        public int SelectedIndex
        {
            get => selectedIndex;
            set => SetProperty(ref selectedIndex, value);
        }
        string myJobsTitle = "My Jobs";
        int staffId;
        public string MyJobsTitle
        {
            get { return myJobsTitle; }
            set
            {
                myJobsTitle = value;
                OnPropertyChanged();
            }
        }
        bool myJobsEnabled = true;
        bool updateProperty = false;
        public bool AcceptToolBar { get; set; }

        public SnagListViewModel()
        {
            SnagListItem = new SnagListAppX();
            SnagListPriorities = new SnagListPriorities()
            {
                SnagListApps = new ObservableRangeCollection<SnagListAppX>()
            };
            SnagListAppXs = new List<SnagListAppX>();

            AddFaultCommand = new AsyncCommand(ShowAddSnagListItem);
            AcceptCommand = new AsyncCommand(ShowModalAccept);
            SelectedCommand = new Command(Selected);
            RefreshCommand = new Command(Refresh);

            staffId = 0;
            ShowMyJobsCommand = new Command(ShowModalMyJobs);

            snagListService = DependencyService.Get<ISnagListService>();

            Device.BeginInvokeOnMainThread(async () =>
            {
                await GetSnagListItems();
            });
        }

        async Task GetSnagListItems()
        {
            try
            {
                IsBusy = true;

                EmptyMessage = "Loading...";

                SnagListPriorities slp = await snagListService.GetSnagLists(false, true);

                SnagListPriorities.SnagListApps.Clear();
                SnagListPriorities = slp;

                SnagListAppXs.Clear();
                SnagListAppXs.AddRange(slp.SnagListApps);

                EmptyMessage = SnagListAppXs.Count == 0 ? "Good Job! \r\n\r\nCurrently nothing to fix" : "";

                IsBusy = false;
            }
            catch (Exception ex)
            {
                await Application.Current.MainPage.DisplayAlert("Error", ex.Message, "Ok");
            }
        }

        void Selected()
        {
            //PositionIdx = 0;
            //OnPropertyChanged(nameof(PositionIdx));
            //FilterSnagList();
            Refresh();
        }

        void FilterSnagList()
        {
            var allJobs = SnagListAppXs;

            if (staffId != 0) allJobs = allJobs.Where(x => x.StaffId == staffId).ToList();

            if (updateProperty) OnPropertyChanged(nameof(SnagListPriorities));

            if (selectedIndex > 0) allJobs = allJobs.Where(sli => sli.Priority == selectedIndex).ToList();

            SnagListPriorities.SnagListApps.Clear();
            //SnagListPriorities.SnagListApps = new ObservableRangeCollection<SnagListAppX>(allJobs);
            SnagListPriorities.SnagListApps.AddRange(allJobs);
            //OnPropertyChanged(nameof(SnagListPriorities.SnagListApps));
            updateProperty = false;
        }

        void ShowModalMyJobs()
        {
            staffId = myJobsEnabled ? int.Parse(Settings.StaffIdSetting) : 0;
            MyJobsTitle = myJobsEnabled ? "All Jobs" : "My Jobs";
            AcceptToolBar = !myJobsEnabled;
            myJobsEnabled = !myJobsEnabled;
            SelectedIndex = 0;
            updateProperty = true;
        }

        void Refresh()
        {
            IsBusy = true;

            var allJobs = SnagListAppXs.Where(s => s.StaffId == 115);

            SnagListPriorities.SnagListApps.Clear();
            SnagListPriorities.SnagListApps.AddRange(allJobs);

            IsBusy = false;
        }

    }
}

looks a bit messy, but wanted to show, that I tried already a lot of things.

Last try was a simple RefreshView. It works as long as the itemsSource is not changing. Once it changes and the first card is not shown, it crashes.

Any ideas? Many thanks

debug output

app page

Shaw
  • 907
  • 1
  • 8
  • 20
TS71M
  • 33
  • 1
  • 6
  • I can't repeat your error becuase your code isn't complete. You can add a breakpoint and try catch to your code and find which line couse the issue. – Liyun Zhang - MSFT Jan 31 '22 at 08:43
  • I have the same error. OutOfRangeException when position was greater then new number of items of CarouselView. Looks like CarouselView keeps position after ItemsSource changed. Dont know how to "reset" it. – user3125174 Sep 27 '22 at 14:54

0 Answers0