How to change a property of a element on a Tap event, and to revert the property of the 'other' element when this happens

Nicolas 1 Reputation point
2021-02-12T10:41:45.167+00:00

I have the following usecase. My page (ContentView), has severale CardView elements. These CardViews are listed in a WrapPanel element (sort of list) and thus supplied by a List<> with items.

When the user clicks one of these CardViews, it's background color should change to mimic an 'Selected' status. The way I thought I could best achieve this was by setting up a 'TapGestureRecognizer' event on the CardView to catch the 'click', then on click I could change the background color.
This all worked fine, but when one CardView get's clicked the previous clicked CardView should revert back to normal, so the backgroundcolor of the previous clicked CardView should revert back to the default value. So it should act like a RadioButton in a group.

To achieve the latter I thought I could 'store' the x:Name of the clicked element in C# to a property, and on a Tap event I would check if the property had a value and the value was different of the clicked element, if so then I would get the previous element by Name with FindByName and change the backgroundcolor porperty when found.

Though, after reading up on some other post I found out this is bad practice.

Any ideas how I can easily achieve these requirement? Perhaps with XAML allone? As a not so experient Xamarin programmer this current solutionw as the onyl one which came to mind.

The code:

   <grial:WrapPanel  
   Grid.Row="1"  
   HorizontalOptions="Center"  
   ItemsSource="{Binding CurrentFloors}"  
   VerticalOptions="Start">  
   <grial:WrapPanel.ItemTemplate>  
   <DataTemplate>  
   <grial:CardView  
   x:Name="{Binding DepartmentId}"  
   Margin="5,0,0,0"  
   Padding="7"  
   ClassId="{Binding DepartmentId}"  
   ColumnSpacing="6"  
   CornerRadius="16"  
   HasShadow="True"  
   RowSpacing="6">  
     
   <!-- omitted other code / elements -->  
     
   <grial:CardView.GestureRecognizers>  
   <TapGestureRecognizer NumberOfTapsRequired="1" Tapped="OnTapGestureRecognizerTapped" />  
   </grial:CardView.GestureRecognizers>  
     
   </grial:CardView>  
   </DataTemplate>  
   </grial:WrapPanel.ItemTemplate>  
   </grial:WrapPanel>  



   private void OnTapGestureRecognizerTapped(object sender, EventArgs args)  
   {  
   var clickedCardView = (CardView)sender;  
   var floorID = clickedCardView.ClassId;  
   clickedCardView.BackgroundColor = Color.Blue;  
     
   if(floorID != PreviousCLickedFloor && PreviousCLickedFloor != null)  
   {  
   // Bad practice here..  
   // omitted code to find previous element by name   
   }  
     
   PreviousCLickedFloor = floorID;  
     
   }  

UPDATE
For some reason I am not allowed to post code / more text. I keep getting the Access denied redirect so I uploaded it all as images:

68416-cap1.png

68375-cap2.png

68376-cap3.png

And finally the Model being used as the datasource:

68377-cap4.png

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,297 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Cole Xia (Shanghai Wicresoft Co,.Ltd.) 6,751 Reputation points
    2021-02-15T06:21:33.547+00:00

    Hello,

    Welcome to Microsoft Q&A!

    It can be easily achieved if we create a bool property named Selected inside ViewModel , and implement INotifyPropertyChanged .

       public class ViewModel : INotifyPropertyChanged  
       	{  
       		//xxxxxx  
         
       		private bool selected;  
       		public bool Selected { get {  
       				return selected;  
       			} set {  
       				selected = value;  
       				NotifyPropertyChanged();  
       			}  
       		}  
         
       		public event PropertyChangedEventHandler PropertyChanged;  
         
       		// This method is called by the Set accessor of each property.    
       		// The CallerMemberName attribute that is applied to the optional propertyName    
       		// parameter causes the property name of the caller to be substituted as an argument.    
       		private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")  
       		{  
       			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
       		}  
         
       	}  
    

    Create binding on the BackgroundColor of the cardView with Selected , and of course we need to use a Converter .

       <ContentPage.Resources>  
               <ResourceDictionary>  
                   <local:MyConverter x:Key="ccc" />  
               </ResourceDictionary>  
           </ContentPage.Resources>  
         
       <grial:CardView BackgroundColor="{Binding Selected,Converter={StaticResource ccc}}">  
    

    At last in tap event we need to highlight the selected one and reset the previous one .

       private void TapGestureRecognizer_Tapped(object sender, EventArgs e)  
               {  
                   var model = ((StackLayout)sender).BindingContext as ViewModel;  
         
                   if(model.Selected == false)  
                   {  
                       model.Selected = true;  
         
                       foreach(var item in CurrentFloors)  
                       {  
                           if(item != model)  
                           {  
                               item.Selected = false;  
                           }                  
                       }  
                   }  
               }  
    
     
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.