Button click event inside list view cell is hard to be invoked

Wei Wen 1,096 Reputation points
2021-09-01T13:30:36.933+00:00

I have a listview in which I customized each row to have two buttons. Even though I set SelectionMode to None, it is still the ItemSelected that seems to be invoked: I can see the cell is highlighted and then the highlight disappears. I have to click the button(s) many times to have the button(s) click event activated. In fact, whether I set SelectionMode to None or not, it is just very very hard to have the button(s)' click event invoked. Is there a solution to this?

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

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 72,251 Reputation points Microsoft Vendor
    2021-09-02T02:41:22.64+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    Firstly, I make a test in my side. I use listview and put two Buttons in the <ViewCell> like following xml. Button click event inside list view cell is works as well.

    My xamarin.Forms version is 5.0.0.2012, you can create a new project, then copy my following code to it, If it works, If following code worked as normal, this issue is related to your custom row, please share this part of code, or share a demo directly(recommanded).

       <ListView ItemsSource="{Binding Monkeys}" HasUnevenRows="True" ItemSelected="ListView_ItemSelected" SelectionMode="None">  
                   <ListView.ItemTemplate>  
                       <DataTemplate>  
                           <ViewCell>  
         
                               <StackLayout >  
                                   <Label   
                                              Text="{Binding Name}"  
                                              FontAttributes="Bold" />  
                                   <Label   
                                              Text="{Binding Location}"  
                                              FontAttributes="Italic"  
                                              VerticalOptions="End" />  
                                   <Button Text="Btn1" Clicked="Button_Clicked_1"/>  
                                   <Button Text="Btn2" Clicked="Button_Clicked_2"/>  
         
                               </StackLayout>  
                           </ViewCell>  
                       </DataTemplate>  
                   </ListView.ItemTemplate>  
         
               </ListView>  
    

    Then here is my background code. I will pop up a DisplayAlert when click a button to valiate the click event .

       public partial class MainPage : ContentPage  
           {  
               public MainPage()  
               {  
                   InitializeComponent();  
                   BindingContext = new MonkeysViewModel();  
         
         
               }  
         
               private void Button_Clicked(object sender, EventArgs e)  
               {  
         
               }  
         
               private void Button_Clicked_1(object sender, EventArgs e)  
               {  
                   DisplayAlert("info1", "Button_Clicked_1","OK");  
               }  
         
               private void Button_Clicked_2(object sender, EventArgs e)  
               {  
                   DisplayAlert("info2", "Button_Clicked_2", "OK");  
         
               }  
         
               private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)  
               {  
                   DisplayAlert("ItemSelected", "ListView_ItemSelected", "OK");  
         
               }  
           }  
    

    Here is my viewModel.

       public class MonkeysViewModel : INotifyPropertyChanged  
           {  
               readonly IList<Monkey> source;  
               Monkey selectedMonkey;  
               int selectionCount = 1;  
         
               public ObservableCollection<Monkey> Monkeys { get; private set; }  
               public IList<Monkey> EmptyMonkeys { get; private set; }  
         
               public Monkey SelectedMonkey  
               {  
                   get  
                   {  
                       return selectedMonkey;  
                   }  
                   set  
                   {  
                       if (selectedMonkey != value)  
                       {  
                           selectedMonkey = value;  
                       }  
                   }  
               }  
         
               ObservableCollection<object> selectedMonkeys;  
               public ObservableCollection<object> SelectedMonkeys  
               {  
                   get  
                   {  
                       return selectedMonkeys;  
                   }  
                   set  
                   {  
                       if (selectedMonkeys != value)  
                       {  
                           selectedMonkeys = value;  
                       }  
                   }  
               }  
         
               public string SelectedMonkeyMessage { get; private set; }  
         
               public ICommand DeleteCommand => new Command<Monkey>(RemoveMonkey);  
              // public ICommand FavoriteCommand => new Command<Monkey>(FavoriteMonkey);  
               public ICommand FilterCommand => new Command<string>(FilterItems);  
               public ICommand MonkeySelectionChangedCommand => new Command(MonkeySelectionChanged);  
         
               public MonkeysViewModel()  
               {  
                   source = new List<Monkey>();  
                   CreateMonkeyCollection();  
         
                   selectedMonkey = Monkeys.Skip(3).FirstOrDefault();  
                   MonkeySelectionChanged();  
         
                   SelectedMonkeys = new ObservableCollection<object>()  
                   {  
                       Monkeys[1], Monkeys[3], Monkeys[4]  
                   };  
               }  
         
               void CreateMonkeyCollection()  
               {  
                   source.Add(new Monkey  
                   {  
                       Name = "Baboon",  
                       Location = "Africa & Asia",  
                       Details = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",  
                       ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"  
                   });  
         
                     
                   Monkeys = new ObservableCollection<Monkey>(source);  
               }  
         
               void FilterItems(string filter)  
               {  
                   var filteredItems = source.Where(monkey => monkey.Name.ToLower().Contains(filter.ToLower())).ToList();  
                   foreach (var monkey in source)  
                   {  
                       if (!filteredItems.Contains(monkey))  
                       {  
                           Monkeys.Remove(monkey);  
                       }  
                       else  
                       {  
                           if (!Monkeys.Contains(monkey))  
                           {  
                               Monkeys.Add(monkey);  
                           }  
                       }  
                   }  
               }  
         
               void MonkeySelectionChanged()  
               {  
                   SelectedMonkeyMessage = $"Selection {selectionCount}: {SelectedMonkey.Name}";  
                   OnPropertyChanged("SelectedMonkeyMessage");  
                   selectionCount++;  
               }  
         
               void RemoveMonkey(Monkey monkey)  
               {  
                   if (Monkeys.Contains(monkey))  
                   {  
                       Monkeys.Remove(monkey);  
                   }  
               }  
         
               //void FavoriteMonkey(Monkey monkey)  
               //{  
               //    monkey.IsFavorite = !monkey.IsFavorite;  
               //}  
         
               #region INotifyPropertyChanged  
               public event PropertyChangedEventHandler PropertyChanged;  
         
               void OnPropertyChanged([CallerMemberName] string propertyName = null)  
               {  
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
               }  
               #endregion  
           }  
         
           public class Monkey  
           {  
               public string Name { get; set; }  
               public string Location { get; set; }  
               public string Details { get; set; }  
               public string ImageUrl { get; set; }  
           }  
    

    ========================
    Update==============================

    I find the <RefreshView> canot not wrap the <Listview>, And <ListView> have IsPullToRefreshEnabled, IsRefreshing and RefreshCommand function, you can move <RefreshView>. using following code.

       <StackLayout>  
               <Label Text="Test" />  
         
               <!--<RefreshView Grid.Row="0" Grid.RowSpan="1" x:Name="InvitationRefreshView" Refreshing="Invitation_Refreshing">-->  
               <ListView x:Name="InviteListView" SeparatorColor="LightGray" BackgroundColor="White" RowHeight="80" Margin="10,0,15,0"  IsPullToRefreshEnabled="True" IsRefreshing="{Binding IsRefreshing}" RefreshCommand="{Binding RefreshCommand}" SelectionMode="None">  
                       <ListView.ItemsSource>  
                           <x:Array Type="{x:Type x:String}">  
                               <x:String>mono</x:String>  
                               <x:String>monodroid</x:String>  
                               <x:String>monotouch</x:String>  
                               <x:String>monorail</x:String>  
                               <x:String>monodevelop</x:String>  
                               <x:String>monotone</x:String>  
                               <x:String>monopoly</x:String>  
                               <x:String>monomodal</x:String>  
                               <x:String>mononucleosis</x:String>  
                           </x:Array>  
                       </ListView.ItemsSource>  
                       <ListView.ItemTemplate>  
                           <DataTemplate>  
                               <ViewCell>  
                                   <Grid>  
                                       <Grid.ColumnDefinitions>  
                                           <ColumnDefinition Width="70" />  
                                           <ColumnDefinition Width="*" />  
                                           <ColumnDefinition Width="140" />  
                                       </Grid.ColumnDefinitions>  
         
                                       <StackLayout Orientation="Horizontal"  VerticalOptions="Center" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1">  
                                           <Frame CornerRadius="3" IsClippedToBounds="True" Padding="0" HasShadow="False">  
                                               <StackLayout  WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Margin="0"  VerticalOptions="Center" Padding="0">  
                                                   <Image  IsVisible="true" Source="LargeAvatarDefault.png"   VerticalOptions="CenterAndExpand" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="50"  Aspect="AspectFill" />  
                                                   <Label BackgroundColor="Green" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="5" Margin="0,-6,0,0"/>  
                                               </StackLayout>  
                                           </Frame>  
                                       </StackLayout>  
                                       <Label Text="{Binding .}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" HorizontalTextAlignment="Start"/>  
                                       <StackLayout Orientation="Horizontal" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" Spacing="10">  
                                           <Button Text="Accept" TextColor="White" BackgroundColor="Orange" Clicked="Accept_Clicked" HeightRequest="40" FontSize="12" Padding="0" WidthRequest="60"/>  
                                           <Button Text="Decline" TextColor="White" BackgroundColor="#355466" Clicked="Decline_Clicked" HeightRequest="40" WidthRequest="60" FontSize="12"  Padding="0" />  
                                       </StackLayout>  
         
                                   </Grid>  
                               </ViewCell>  
                           </DataTemplate>  
                       </ListView.ItemTemplate>  
                   </ListView>  
               <!--</RefreshView>-->  
           </StackLayout>  
    

    Here is layout backgrounc code.

       public partial class MainPage : ContentPage  
           {  
         
               private bool _isRefreshing = false;  
               public bool IsRefreshing  
               {  
                   get { return _isRefreshing; }  
                   set  
                   {  
                       _isRefreshing = value;  
                       OnPropertyChanged(nameof(IsRefreshing));  
                   }  
               }  
               public ICommand RefreshCommand  
               {  
                   get  
                   {  
                       return new Command(async () =>  
                       {  
                           IsRefreshing = true;  
         
                          // await RefreshData();  
         
                           IsRefreshing = false;  
                       });  
                   }  
               }  
         
               public MainPage()  
               {  
                   InitializeComponent();  
                   BindingContext =this;  
         
         
               }  
         
               private async void Accept_Clicked(object sender, EventArgs e)  
               {  
                   await Task.Delay(1000);  
                   await DisplayAlert("Alert", "Acceopt Clicked", "OK");  
               }  
         
               private async void Decline_Clicked(object sender, EventArgs e)  
               {  
                   await Task.Delay(1000);  
                   await DisplayAlert("Alert", "Declne Clicked", "OK");  
               }  
           }  
    

    Best Regards,

    Leon Lu


    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.

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Wei Wen 1 Reputation point
    2021-09-02T20:50:05.613+00:00

    @Leon Lu (Shanghai Wicresoft Co,.Ltd.) Your code works. It is something in my code which prevents the event from firing. I cannot identify what is wrong. I created a demo project. I attach the code here.

    Here is the MainPage.xaml:

    <?xml version="1.0" encoding="utf-8" ?>  
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
                 x:Class="TestProject.MainPage">  
      
        <StackLayout>  
            <Label Text="Test" />  
              
            <RefreshView Grid.Row="0" Grid.RowSpan="1" x:Name="InvitationRefreshView" Refreshing="Invitation_Refreshing">  
                <ListView x:Name="InviteListView" SeparatorColor="LightGray" BackgroundColor="White" RowHeight="80" Margin="10,0,15,0"  SelectionMode="None">  
                    <ListView.ItemsSource>  
                        <x:Array Type="{x:Type x:String}">  
                            <x:String>mono</x:String>  
                            <x:String>monodroid</x:String>  
                            <x:String>monotouch</x:String>  
                            <x:String>monorail</x:String>  
                            <x:String>monodevelop</x:String>  
                            <x:String>monotone</x:String>  
                            <x:String>monopoly</x:String>  
                            <x:String>monomodal</x:String>  
                            <x:String>mononucleosis</x:String>  
                        </x:Array>  
                    </ListView.ItemsSource>  
                    <ListView.ItemTemplate>  
                        <DataTemplate>  
                            <ViewCell>  
                                <Grid>  
                                    <Grid.ColumnDefinitions>  
                                        <ColumnDefinition Width="70" />  
                                        <ColumnDefinition Width="*" />  
                                        <ColumnDefinition Width="140" />  
                                    </Grid.ColumnDefinitions>  
      
                                    <StackLayout Orientation="Horizontal"  VerticalOptions="Center" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1">  
                                        <Frame CornerRadius="3" IsClippedToBounds="True" Padding="0" HasShadow="False">  
                                            <StackLayout  WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Margin="0"  VerticalOptions="Center" Padding="0">  
                                                <Image  IsVisible="true" Source="LargeAvatarDefault.png"   VerticalOptions="CenterAndExpand" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="50"  Aspect="AspectFill" />  
                                                <Label BackgroundColor="Green" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="5" Margin="0,-6,0,0"/>  
                                            </StackLayout>  
                                        </Frame>  
                                    </StackLayout>  
                                    <Label Text="{Binding .}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" HorizontalTextAlignment="Start"/>  
                                    <StackLayout Orientation="Horizontal" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" Spacing="10">  
                                        <Button Text="Accept" TextColor="White" BackgroundColor="Orange" Clicked="Accept_Clicked" HeightRequest="40" FontSize="12" Padding="0" WidthRequest="60"/>  
                                        <Button Text="Decline" TextColor="White" BackgroundColor="#355466" Clicked="Decline_Clicked" HeightRequest="40" WidthRequest="60" FontSize="12"  Padding="0" />  
                                    </StackLayout>  
      
                                </Grid>  
                            </ViewCell>  
                        </DataTemplate>  
                    </ListView.ItemTemplate>  
                </ListView>  
            </RefreshView>  
        </StackLayout>  
      
    </ContentPage>  
    

    Here is MainPage.xaml.cs:

    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel;  
    using System.Linq;  
    using System.Text;  
    using System.Threading.Tasks;  
    using Xamarin.Forms;  
      
    namespace TestProject  
    {  
      public partial class MainPage : ContentPage  
      {  
        public MainPage()  
        {  
          InitializeComponent();  
        }  
      
        private async void Accept_Clicked(object sender, EventArgs e)  
        {  
          await Task.Delay(1000);  
          await DisplayAlert("Alert", "Acceopt Clicked", "OK");  
        }  
      
        private async void Decline_Clicked(object sender, EventArgs e)  
        {  
          await Task.Delay(1000);  
          await DisplayAlert("Alert", "Declne Clicked", "OK");  
        }  
      
        private void Invitation_Refreshing(object sender, EventArgs e)  
        {  
          InvitationRefreshView.IsRefreshing = false;  
        }  
      
      }  
    }  
    

    The image source is a silhouette of a person. You can use any image. I don't think that is causing the problem. I think the issue is in the buttons. my event method has async keyword because in my project, clicking the button will initiate backend process which will take time.


  2. Wei Wen 1 Reputation point
    2021-09-02T20:53:10.347+00:00

    @Leon Lu (Shanghai Wicresoft Co,.Ltd.) Your code works. It is something in my code which prevents the event from firing. I cannot identify what is wrong. I created a demo project. I attach the code here.

    Here is the MainPage.xaml:

    1. <?xml version="1.0" encoding="utf-8" ?>
    2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    4. x:Class="TestProject.MainPage">
      1. <StackLayout>
    5. <Label Text="Test" />
      1. <RefreshView Grid.Row="0" Grid.RowSpan="1" x:Name="InvitationRefreshView" Refreshing="Invitation_Refreshing">
    6. <ListView x:Name="InviteListView" SeparatorColor="LightGray" BackgroundColor="White" RowHeight="80" Margin="10,0,15,0" SelectionMode="None">
    7. <ListView.ItemsSource>
    8. <x:Array Type="{x:Type x:String}">
    9. <x:String>mono</x:String>
    10. <x:String>monodroid</x:String>
    11. <x:String>monotouch</x:String>
    12. <x:String>monorail</x:String>
    13. <x:String>monodevelop</x:String>
    14. <x:String>monotone</x:String>
    15. <x:String>monopoly</x:String>
    16. <x:String>monomodal</x:String>
    17. <x:String>mononucleosis</x:String>
    18. </x:Array>
    19. </ListView.ItemsSource>
    20. <ListView.ItemTemplate>
    21. <DataTemplate>
    22. <ViewCell>
    23. <Grid>
    24. <Grid.ColumnDefinitions>
    25. <ColumnDefinition Width="70" />
    26. <ColumnDefinition Width="*" />
    27. <ColumnDefinition Width="140" />
    28. </Grid.ColumnDefinitions>
      1. <StackLayout Orientation="Horizontal" VerticalOptions="Center" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1">
    29. <Frame CornerRadius="3" IsClippedToBounds="True" Padding="0" HasShadow="False">
    30. <StackLayout WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Margin="0" VerticalOptions="Center" Padding="0">
    31. <Image IsVisible="true" Source="LargeAvatarDefault.png" VerticalOptions="CenterAndExpand" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="50" Aspect="AspectFill" />
    32. <Label BackgroundColor="Green" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="5" Margin="0,-6,0,0"/>
    33. </StackLayout>
    34. </Frame>
    35. </StackLayout>
    36. <Label Text="{Binding .}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" HorizontalTextAlignment="Start"/>
    37. <StackLayout Orientation="Horizontal" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" Spacing="10">
    38. <Button Text="Accept" TextColor="White" BackgroundColor="Orange" Clicked="Accept_Clicked" HeightRequest="40" FontSize="12" Padding="0" WidthRequest="60"/>
    39. <Button Text="Decline" TextColor="White" BackgroundColor="#355466" Clicked="Decline_Clicked" HeightRequest="40" WidthRequest="60" FontSize="12" Padding="0" />
    40. </StackLayout>
      1. </Grid>
    41. </ViewCell>
    42. </DataTemplate>
    43. </ListView.ItemTemplate>
    44. </ListView>
    45. </RefreshView>
    46. </StackLayout>
      1. </ContentPage>

    Here is MainPage.xaml.cs:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7. using Xamarin.Forms;
      1. namespace TestProject
    8. {
    9. public partial class MainPage : ContentPage
    10. {
    11. public MainPage()
    12. {
    13. InitializeComponent();
    14. }
      1. private async void Accept_Clicked(object sender, EventArgs e)
    15. {
    16. await Task.Delay(1000);
    17. await DisplayAlert("Alert", "Acceopt Clicked", "OK");
    18. }
      1. private async void Decline_Clicked(object sender, EventArgs e)
    19. {
    20. await Task.Delay(1000);
    21. await DisplayAlert("Alert", "Declne Clicked", "OK");
    22. }
      1. private void Invitation_Refreshing(object sender, EventArgs e)
    23. {
    24. InvitationRefreshView.IsRefreshing = false;
    25. }
      1. }
    26. }

    The image source is a silhouette of a person. You can use any image. I don't think that is causing the problem. I think the issue is in the buttons. my event method has async keyword because in my project, clicking the button will initiate backend process which will take time.

    0 comments No comments