Bram Sikkens
Bram Sikkens

Reputation: 31

How to bind an ItemSelected Event from a Re-Usable Listview to a ViewModal Command?

I have a UserList that I want to Reuse on multiple pages in my Xamarin Application, but on each of the different pages a ItemSelected event should do something different.

I know about Bindable properties. I use them to bind a list from my Viewmodel to the Reusable Component. But I don't know how to do this with events

Let me show you some code!

This is the XAML of my Reusable ListView. It contains a list of Users

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="KineAppForms.Views.ReusableViews.UserTable"
             x:Name="this">
    <ContentView.Content>


                                    <ListView ItemsSource="{Binding Source={x:Reference this},Path=Users, Mode=TwoWay}">
                                      <ListView.ItemTemplate>
                                          <DataTemplate>
                                            <TextCell Text="{Binding Name.GivenName}"/>
                                          </DataTemplate>
                                        </ListView.ItemTemplate>
                                    </ListView>


    </ContentView.Content>
</ContentView>

The .cs file where I do the binding looks as follows

namespace KineAppForms.Views.ReusableViews
{
    public partial class UserTable : ContentView
    {

        public ObservableCollection<Patient> Users
        {
            get
            {
                return (ObservableCollection<Patient>)GetValue(UsersProperty);
            }
            set
            {
                SetValue(UsersProperty, value);
            }

        }


        public static readonly BindableProperty UsersProperty =
                  BindableProperty.Create("Users", typeof(ObservableCollection<Patient>), typeof(UserTable), null,
                                     BindingMode.Default, null, OnItemsSourceChanged);


        static void OnItemsSourceChanged(BindableObject bindable, object oldvalue, object newvalue)
        {
            System.Diagnostics.Debug.WriteLine("source changed");
        }

        public UserTable()
        {
            InitializeComponent();
        }
    }
}

Now the way how I Use my Reusable Component

 <views:UserTable  Users="{Binding PatientList ,Mode=TwoWay}" />

The Binding PatientList comes from a ViewModel.

No to conclude the question: How Can I Bind an ItemSelected Event To a Command in a ViewModel.

Say I have 2 pages. 1 page with Patients and 1 page with Doctors. They both use the Same table but the Patients Table should go to a Detailed page of Patients ( Link it to goToPatientDetailCommand in PatientListViewModel) and the doctors table should go to a Detailed Page of a Doctor ( Link it to goToPatientDetailCommand in DoctorListViewModel)

It should be something like this

 <views:UserTable  Users="{Binding PatientList ,Mode=TwoWay}" OnItemSelected="{Binding GoToPatientDetailed, Mode=TwoWay }/>

or

<views:UserTable  Users="{Binding DoctorList ,Mode=TwoWay}" OnItemSelected="{Binding GoToDoctorDetailed, Mode=TwoWay }/>

Thank you!

Upvotes: 0

Views: 455

Answers (1)

lawiluk
lawiluk

Reputation: 597

There are several approaches in here.

  1. You can add ItemSelectedCommand to UserTable class
    public partial class UserTable : ContentView
    {

        public ObservableCollection<Patient> Users
        {
            get
            {
                return (ObservableCollection<Patient>)GetValue(UsersProperty);
            }
            set
            {
                SetValue(UsersProperty, value);
            }
        }


        public static readonly BindableProperty UsersProperty =
            BindableProperty.Create("Users", typeof(ObservableCollection<Patient>), typeof(UserTable), null,
                BindingMode.Default, null, OnItemsSourceChanged);

        public static BindableProperty ItemSelectedCommandProperty = BindableProperty.Create(
            propertyName: nameof(ItemSelectedCommand),
            returnType: typeof(ICommand),
            declaringType: typeof(UserTable),
            defaultValue: null);

        public ICommand ItemSelectedCommand
        {
            get { return (ICommand)GetValue(ItemSelectedCommandProperty); }
            set { SetValue(ItemSelectedCommandProperty, value); }
        }

        static void OnItemsSourceChanged(BindableObject bindable, object oldvalue, object newvalue)
        {
            System.Diagnostics.Debug.WriteLine("source changed");
        }

        public UserTable()
        {
            InitializeComponent();
        }
    }

Then in Xaml you can either use EventToCommandBehaviour by adding

<ListView.Behaviors>
    <behaviors:EventToCommandBehavior EventName="ItemSelected" Command="{Binding Source={x:Reference this},Path=ItemSelectedCommand}"/>
</ListView.Behaviors>

or you can create your own CustomListView (that inherites from ListView) that has ItemSelected in it like in this example

  1. Second approach is that instead of creating a control with ListView you can create a data template. Here you have documentation on how to do it.

If you ask for my opinion, I would say go with second approach - later you can reuse that View in other places.

Upvotes: 1

Related Questions