get a value of the row or item in ListView using a Button in Xaml

Tom Meier 260 Reputation points
2024-03-18T16:01:46.2766667+00:00

Currently I am able to get the value I have set the SelectedItem property to of the .Net Maui ListView by selecting an item in the ListView. However, I now have a button on every row of the listview that I would like to be able to click on and get a value of the row or item without user selecting an item. I tried googling how to do this but I didnt see any examples specific to what I am trying to accomplish. I am using MVVM so I would highly prefer to only use methods in the View Model. Thanks !

Developer technologies .NET .NET MAUI
Developer technologies C#
{count} votes

Accepted answer
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 50,126 Reputation points Microsoft External Staff
    2024-03-20T02:37:17.8133333+00:00

    Hello,

    There are two main issues that need to be addressed to achieve this requirement.

    • The specific method of how to bind a button in a ListView to a ViewModel.
    • When clicking the button, how to pass the corresponding item in the ListView to the ViewModel.

    For problem one, you need to use relative bindings to bind the Command directly to the higher-level ViewModel instead of the ItemSource. You could refer to Bind to an ancestor for more details.

    For question two, you need to use CommandParameter to pass the parameters. Please refer to Using Command parameters for more details.

    The following is a minimized example of deleting the corresponding item in a ListView with a button.

     <ListView ItemsSource="{Binding Messages}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <HorizontalStackLayout>
                                <Label Text="{Binding Title}"/>
                                <Label Text="{Binding Description}"/>
                                <Button Text="Delete Item" Command="{Binding Source={RelativeSource AncestorType={x:Type c:MyViewModel}}, Path=DeleteCommand}"
                                CommandParameter="{Binding .}"/>
                            </HorizontalStackLayout>
                        </ViewCell>
                        
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    

    ViewModel:

    public class Item
        {
            public string Title { get; set; }
            public string Description { get; set; }
        }
        public class MyViewModel:INotifyPropertyChanged
        {
            
            private ObservableCollection<Item> _message = new ObservableCollection<Item>();
            public event PropertyChangedEventHandler? PropertyChanged;
            public ICommand DeleteCommand { get; private set; }
    
    
           public ObservableCollection<Item> Messages
            {
                get
                {
                    return _message;
                }
                set
                {
                    _message = value;
                    OnPropertyChanged();
                }
            }
            public void OnPropertyChanged([CallerMemberName] string name = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
            public MyViewModel()
            {
    
    
               PopulateData();
    
    
           }
            // implement delete item from list.
            private void DeleteDataCommand(object o)
            {
                var item = o as Item;
                if (Messages.Contains(item))
                {
                    Messages.Remove(item);
                }
            }
            void PopulateData()
    
           {
               Messages.Add(new Item { Title = "test1", Description = "1st Item" });
                Messages.Add(new Item { Title = "test1", Description = "2nd Item" });
                DeleteCommand = new Command(x => DeleteDataCommand(x));
            }
        }
    

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    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

1 additional answer

Sort by: Most helpful
  1. Albert Kallal 5,586 Reputation points
    2024-03-20T02:50:24.8433333+00:00

    You can use a simple button click event, and you should.

    Say some simple markup like this:

            <ListView x:Name="MyListView" 
                     SelectionMode="Single"
                      HasUnevenRows="True"
                      ItemSelected="MyListView_ItemSelected"
                      >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <HorizontalStackLayout>
    
                                <Label Text="{Binding HotelName}"
                                       MinimumWidthRequest="300"
                                       ></Label>
    
                                <Button x:Name="cmdView" Text="View"
                                    Clicked="cmdView_Clicked"
                                        Padding="0,15,0,15"
                                        />
    
                            </HorizontalStackLayout>
                            
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    
    

    So, code behind to load above:

    	public ListViewTest()
    	{
    		InitializeComponent();
            LoadGrid();
    	}
    
        void LoadGrid()
        {
            List<tblHotel> hList = dbLocal.con.Query<tblHotel>("SELECT * FROM tblHotel");
    
            MyListView.ItemsSource = hList;
    
        }
    
    

    And our standard ListView ItemSelected event:

        private void MyListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
    
            tblHotel MySelHotel = (tblHotel)MyListView.SelectedItem;
            DisplayAlert("Hotel selected", MySelHotel.HotelName, "ok");
    
        }
    
    

    And it turns out that the click button event works much the same. So, then this code:

        private async void cmdView_Clicked(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            tblHotel MySelHotel = (tblHotel)btn.BindingContext;
            await DisplayAlert("Hotel selected", MySelHotel.HotelName, "ok");
    
        }
    
    

    And the result is now this:

    maulistv

    In fact, placing a button on the row is a GREAT way to fix that if the user clicks on a item, then you can't click/select again, can you? (since it is already selected). You can see in the last button, I try to click again on the same row - I can't. But, the button works!

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.