Getting current item by clicking button within a ListView (Xamarin.Forms)

In principle i think the answer given by @Krzysztof Skowronek is right, I will simply try to elaborate on it, and avoid the use of ViewModel since it seems you are not using it (although the use of it is a Best Practice on Xamarin Forms).

Following your own code, i wrote the following code in 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"
             xmlns:local="clr-namespace:DeleteButton"
             x:Class="DeleteButton.MainPage">

    <ListView x:Name="listView"
              HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout>
                        <Label Text="{Binding .}"/>
                        <Button Text="Delete" Clicked="Delete"/>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>    
</ContentPage>

About this part of the solution i would give the following comments:

  • Note the use of ConntentPage instead of ListView at the top of the XAML, is that intentional?

  • Then, note the addition of x:Name on ListView. It will be used to communicate with the ListView from the code behind.

  • Additionally notice the use of HasUnevenRows set to True. This causes the ListView to adjust automatically the height of the rows.

  • Finally see that in Button i have set the event Clicked to "Delete", which is the name of the event handler in the code behind as you will see.

On the code behind i wrote:

using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;

namespace DeleteButton
{
    public partial class MainPage : ContentPage
    {
        ObservableCollection<String> list;

        public MainPage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();

            list = new ObservableCollection<string>()
            {
                "Task 1", "Task 2", "Task 3", "Task 4", "Task 5",
                "Task 6", "Task 7", "Task 8", "Task 9", "Task 10"                    
            };

            listView.ItemsSource = list;
        }

        public void Delete(Object Sender, EventArgs args)
        {
            Button button = (Button)Sender;
            StackLayout listViewItem = (StackLayout)button.Parent;
            Label label = (Label)listViewItem.Children[0];

            String text = label.Text;

            list.Remove(text);
        }
    }
}

There i define the list of strings as an ObservableCollection (ObservableCollection causes the ListView to get a notification each time it changes, so that the ListView updates its contents, see the docu for more details).

Then i set the ItemSource property of the ListView to the collection of Strings, as you have already done.

Finally comes the EventHandler Delete, called by the Click event on Button, in XAML. The algorithm here is quite simple:

First the sender is cast to a Button (we know that the object firing the event is a Button).

Then we walk up the hierarchy tree up to the StackLayout containing the Button and the Label and retrieve the first child of it, which we know is the Label.

Once we have the Label we retrieve its Text property and call the Remove method of the collection to get ride of that item.

And that's it.

Note: If i would implement this functionality myself i would rather define a collection of objects which would containt a Text property as well as an Id property in order to remove exactly the element tapped. In the code above, if the collection contains two identical strings, the EventHandler would go simply for the first occurrence.

I hope this helps you to find the right way to solve your problem.


If you don't want to use Commands, you can use the Button's Clicked event. IE,

<Button Text="Delete" Clicked="HandleDeleteButtonClicked" />

Then in your code behind file,

private void HandleDeleteButtonClicked(object sender, EventArgs e)
{
    // Assuming your list ItemsSource is a list of strings
    // (If its a list of some other type of object, just change the type in the (cast)):
    var stringInThisCell = (string)((Button)sender).BindingContext;

    // Now you can delete stringInThisCell from your list.
    myList.Remove(stringInThisCell);
}

Create a DeleteItem command with a parameter in your control (or it's ViewModel preferably) and then in xaml:

 <DataTemplate>
            <ViewCell>
                <StackLayout>
                    <Label Text="{Binding .}"/>
                    <Button Text="Delete" Command="{Binding Source={this should be your VM for the whole thing}, Path=DeleteItem}" CommandParameter="{Binding}"/>
                </StackLayout>
            </ViewCell>
        </DataTemplate>

In command: list.Remove(parameter);

If the the list is obeservable, it will disappear.


If we are willing to keep MVVM approach, then in your View, name the ContentPage (or whichever root element is there) and use it as Source to bind command:

<?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="canaraydash.View.InviteListPage"
   x:Name="InvitesView">

   <ListView x:Class="XXX.EditItemsList">
      <ListView.ItemTemplate>
         <DataTemplate>
            <ViewCell>
               <StackLayout>
                  <Label Text="{Binding .}" />
                  <Button Text="Delete"
                   Command="{Binding Path=BindingContext.AcceptRequestCommand, Source={x:Reference InvitesView}}"
                   CommandParameter="{Binding .}" />
               </StackLayout>
            </ViewCell>
         </DataTemplate>
      </ListView.ItemTemplate>
   </ListView>
</ContentPage>

and in your ViewModel, define 'AcceptRequestCommand' command!