question

WeiWen-3421 avatar image
0 Votes"
WeiWen-3421 asked LeonLu-MSFT commented

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

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?

dotnet-xamarin
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Actually this seems to be a problem in Android. iOS devices are fine.

0 Votes 0 ·
LeonLu-MSFT avatar image
0 Votes"
LeonLu-MSFT answered LeonLu-MSFT edited

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.


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

WeiWen-4339 avatar image
0 Votes"
WeiWen-4339 answered LeonLu-MSFT commented

@LeonLu-MSFT 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.

· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@WeiWen-3421 Please see my updated answer. WeiWen-4339 is your account as well?

0 Votes 0 ·

@LeonLu-MSFT Yes, both WeiWen-3421 and WeiWen-4339 are my accounts. Because my outlook mail is associated with WeiWen-4339, if I do not specifically log in with the email associated with WeiWen-3421, it will always default to WeiWen-4339.

0 Votes 0 ·

@LeonLu-MSFT Thank you very much for help!

0 Votes 0 ·

Ok, you are welcome.

0 Votes 0 ·
WeiWen-4339 avatar image
0 Votes"
WeiWen-4339 answered WeiWen-4339 published

@LeonLu-MSFT 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">

  5. <StackLayout>

  6. <Label Text="Test" />

  7. <RefreshView Grid.Row="0" Grid.RowSpan="1" x:Name="InvitationRefreshView" Refreshing="Invitation_Refreshing">

  8. <ListView x:Name="InviteListView" SeparatorColor="LightGray" BackgroundColor="White" RowHeight="80" Margin="10,0,15,0" SelectionMode="None">

  9. <ListView.ItemsSource>

  10. <x:Array Type="{x:Type x:String}">

  11. <x:String>mono</x:String>

  12. <x:String>monodroid</x:String>

  13. <x:String>monotouch</x:String>

  14. <x:String>monorail</x:String>

  15. <x:String>monodevelop</x:String>

  16. <x:String>monotone</x:String>

  17. <x:String>monopoly</x:String>

  18. <x:String>monomodal</x:String>

  19. <x:String>mononucleosis</x:String>

  20. </x:Array>

  21. </ListView.ItemsSource>

  22. <ListView.ItemTemplate>

  23. <DataTemplate>

  24. <ViewCell>

  25. <Grid>

  26. <Grid.ColumnDefinitions>

  27. <ColumnDefinition Width="70" />

  28. <ColumnDefinition Width="*" />

  29. <ColumnDefinition Width="140" />

  30. </Grid.ColumnDefinitions>

  31. <StackLayout Orientation="Horizontal" VerticalOptions="Center" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1">

  32. <Frame CornerRadius="3" IsClippedToBounds="True" Padding="0" HasShadow="False">

  33. <StackLayout WidthRequest="55" MinimumWidthRequest="55" HeightRequest="55" Margin="0" VerticalOptions="Center" Padding="0">

  34. <Image IsVisible="true" Source="LargeAvatarDefault.png" VerticalOptions="CenterAndExpand" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="50" Aspect="AspectFill" />

  35. <Label BackgroundColor="Green" WidthRequest="55" MinimumWidthRequest="55" HeightRequest="5" Margin="0,-6,0,0"/>

  36. </StackLayout>

  37. </Frame>

  38. </StackLayout>

  39. <Label Text="{Binding .}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" HorizontalTextAlignment="Start"/>

  40. <StackLayout Orientation="Horizontal" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" Spacing="10">

  41. <Button Text="Accept" TextColor="White" BackgroundColor="Orange" Clicked="Accept_Clicked" HeightRequest="40" FontSize="12" Padding="0" WidthRequest="60"/>

  42. <Button Text="Decline" TextColor="White" BackgroundColor="#355466" Clicked="Decline_Clicked" HeightRequest="40" WidthRequest="60" FontSize="12" Padding="0" />

  43. </StackLayout>

  44. </Grid>

  45. </ViewCell>

  46. </DataTemplate>

  47. </ListView.ItemTemplate>

  48. </ListView>

  49. </RefreshView>

  50. </StackLayout>

  51. </ContentPage>
    57.


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;

  8. namespace TestProject

  9. {

  10. public partial class MainPage : ContentPage

  11. {

  12. public MainPage()

  13. {

  14. InitializeComponent();

  15. }

  16. private async void Accept_Clicked(object sender, EventArgs e)

  17. {

  18. await Task.Delay(1000);

  19. await DisplayAlert("Alert", "Acceopt Clicked", "OK");

  20. }

  21. private async void Decline_Clicked(object sender, EventArgs e)

  22. {

  23. await Task.Delay(1000);

  24. await DisplayAlert("Alert", "Declne Clicked", "OK");

  25. }

  26. private void Invitation_Refreshing(object sender, EventArgs e)

  27. {

  28. InvitationRefreshView.IsRefreshing = false;

  29. }

  30. }

  31. }

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.





5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.