Search code examples
xamlxamarin.formsxamarin.ios

Cant reselect previous selected item in CollectionView in Xamarin.Forms on iOS


TLDR

CollectionView broken in iOS, cant find any solution online. Weak Google fu or just not seeing the answer.

The problem

I have a CollectionView (Lets call it A) that contains items that are only strings values for filtering on a different CollectionView (Lets call it B) containing results from selecting a filter option in CollectionView A.

I have 3 different filtering options: "All", "Close to you", "Online" in CollectionView A.

The problem is that on iOS when I select for example filter option "All" in CollectionView A for the first time on the page it does the filtering and shows the results in CollectionView B, when I choose for example filtering option "Online" in CollectionView A it filters and shows the results in CollectionView B.

But when I choose filtering option "All" for the second time in CollectionView A its not responding, no event triggers, no commands runs and its not disabled. It is only showing, but I cant do anything with it.

Expected result

Can reselect the previous item on iOS, in Android no problem.

Actual result

Cant reselect previous item on iOS, need to back to previous page in stack and then navigate back to page to reset filtering.

The xaml markup

This is the xaml markup for the CollectionView A as explained above, the only holding string values to filter on.

<CollectionView ItemsSource="{Binding FilterLocations}"
                Grid.Row="2"
                SelectedItem="{Binding SelectedFilterLocation}"
                SelectionMode="Single"
                HeightRequest="50">
   <CollectionView.ItemsLayout>
      <LinearItemsLayout Orientation="Horizontal"
                         ItemSpacing="10" />
   </CollectionView.ItemsLayout>
      <CollectionView.ItemTemplate>
         <DataTemplate x:DataType="x:String">
            <StackLayout xct:TouchEffect.NativeAnimation="True">
               <Frame BorderColor="{StaticResource BorderColor}"
                      x:Name="subCategoryFrame"
                      Padding="14, 10">
                  <Label Text="{Binding .}"
                         x:Name="subCategoryName"
                         FontFamily="{StaticResource FontPoppinsLight}"
                         TextColor="{StaticResource PrimaryAlt}" />
               </Frame>
            </StackLayout>
         </DataTemplate>
      </CollectionView.ItemTemplate>
         <CollectionView.Header>
            <BoxView WidthRequest="0"
                     HeightRequest="1"
                     BackgroundColor="Transparent" />
         </CollectionView.Header>
         <CollectionView.Footer>
            <BoxView WidthRequest="{StaticResource NormalSpacingDouble}"
                     HeightRequest="1"
                     BackgroundColor="Transparent" />
         </CollectionView.Footer>
      </CollectionView>

The CollectionView A is in SelectionMode:Single, and the SelectedItem is bound to a ICommand on its bound ViewModel. And in the ViewModel the selection of a item in CollectionView A will trigger a filtering in CollectionView B

What I done so far

I set up so if a item in the CollectionView A is disabled it become Red, but it dont become Red.

I have tried to add a event in the Code behind, to try and set SelectedItem to null, but that back fired and just made all events go twice, one for selecting the item on the screen and second for altering the SelectedItem in the code behind.

Code:

private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
      if (Device.RuntimePlatform == Device.iOS)
      {
         var collectionView = sender as CollectionView;
         if (collectionView == null) return;

         if (collectionView.SelectedItem != null)
         {
             collectionView.SelectedItem = null;
         }
      }
}

(I know it is a big no no to do logic stuff in the Code Behind that is not design logic, but I need to get this solved or have a quick and dirty fix because of time pressure.)

Sorry for the wall of text


Solution

  • Use a TapGestureRecognizer.

    Below, MyItemCommand is defined in MyViewModel and {Binding .} refers to the item selected in ItemsSource.

    <DataTemplate ...>
      <StackLayout>
        <StackLayout.GestureRecognizers>
          <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}},
                                                  Path=MyItemCommand}"
                                CommandParameter="{Binding .}"/>
        </StackLayout.GestureRecognizers>
        ...