23
votes

How can I bind a ICommand object from my ViewModel (currently in BindingContext) to the ItemTapped or ItemSelected from a ListView in XAML?

This is a simple task when using a Button, I just set Command="MyViewModelCommand" and everything works.

5
isn't there a way to bind the ListView's SelectedItem to a property in the ViewModel? that's what you usually do in other XAML-based frameworks such as WPF. I didn't have the chance to get my hands on Xamarin.Forms yet haha - Federico Berasategui
Yes... I can bind properties easily, but commands seens to be a little hard. I can bind the SelectedItem property but I cant find a way to bind the "SelectedItemChanged" event. - MarcioGomes
usually I follow get the data in code behind, get the selected item and the pass this to ViewModel, like this ListView ls = new ListView (); ls.ItemSelected += (object sender, SelectedItemChangedEventArgs e) => { var param1 = e.SelectedItem; var currentVm = this.BindingContext as YourViewModel; currentVm.MethodName(param1); } ; - KirtiSagar
Hi KirtiSagar, I'm doing like you... I'm using the code behind to handle the SelectedItemChange event, but in XAML (WPF or Windows Phone) I can bind the event directly to the ViewModel - MarcioGomes

5 Answers

31
votes

I am not sure why you need to give Item Selected or Item Tapped event in your page when you can directly get the selected / tapped object directly in your ViewModel:

I think you've bind the listview with below types of code

<ListView ItemsSource="{Binding PermitDetails}" 
SelectedItem="{Binding objItemSelected, Mode=TwoWay}" x:Name="lst" 
RowHeight="35" HorizontalOptions="FillAndExpand" 
VerticalOptions="Fill">

And in the View Model which you've bind with the page, you've to define the property objItemSelected as mentioned in below code

private Permit _ItemSelected;
public Permit objItemSelected {
    get {
        return _ItemSelected;
    }
    set {
        if (_ItemSelected != value) {
            _ItemSelected = value;
            OnPropertyChanged ("ItemSelected");
        }
    }
}

And if you want to perform any additional functionality like navigating to detail page, you can do it from the set property after OnPropertyChanged statement is executed.

Hope this helps!

3
votes

My scenario is a keypad and images are the buttons and when one is clicked then the relevant character is showed in a label.

So..

Lets say you have the follow control(key 1):

<Image  x:Name="imgKey1"
        Source="key1.png"
        Aspect="AspectFit" />

And you want when the image is tapped to trigger a command from you "viewModel" object. My "viewModel" has the follow property:

public ICommand AddCharCommand { protected set; get; }

where in my class(view model) constructor i have:

  this.AddCharCommand = new Command<string>((key) =>
            {
                // Add the key to the input string.
                this.InputString += key;
            });

"InputString" is a string property...

So for binding the view and the mode,l i am doing the follow:

  imgKey1.GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = viewModel.AddCharCommand,
            CommandParameter = "1",
        });

I hope that i help you, if somebody need something and i can help; i am here...and there. JJ

IN XAML 17-11-14

      <Image  Source="n1normal.png"
              Aspect="AspectFit" >
        <Image.GestureRecognizers>
          <TapGestureRecognizer
            Command="{Binding AddCharCommand}"
            CommandParameter ="1"/>
        </Image.GestureRecognizers>
      </Image>

//-OR-

        <Image Source="magnifyglass.png"
               Aspect="AspectFit"
               HorizontalOptions="End">
          <Image.GestureRecognizers>
            <TapGestureRecognizer Tapped="Search"/>
          </Image.GestureRecognizers>  
        </Image>

//And for the Search create in your ViewModel

 private void Search(object sender, EventArgs e)
 {
      //Your code here
 }
3
votes

I used to take the approach of binding selected item into the view model, but that tended to raise trouble filtering out programmatic changes versus user action. My personal favorite is an attached property.

public static readonly BindableProperty ListItemTappedCommandProperty = BindableProperty.CreateAttached<AttachedProperties, ICommand>(
    staticgetter: o => (ICommand) o.GetValue(ListItemTappedCommandProperty), 
    defaultValue: default(ICommand),
    propertyChanged: (o, old, @new) =>
    {
        var lv = o as ListView;
        if (lv == null) return;

        lv.ItemTapped -= ListView_ItemTapped;
        lv.ItemTapped += ListView_ItemTapped;
    });

private static void ListView_ItemTapped(object sender, object item)
{
    var lv = sender as ListView;

    var command = (ICommand) lv?.GetValue(ListItemTappedCommandProperty);
    if (command == null) return;

    if (command.CanExecute(item))
        command.Execute(item);
}

...and then set on a ListView with:

controls:AttachedProperties.ListItemTappedCommand="{Binding ItemSelectedCommand}"

It's possible that this could even be extended to take the event name and apply more generally, but that is an exercise for another time.

2
votes

This is an old question, and maybe this is a fairly recent development, but you can use the Behaviors on any control to convert an event into an ICommand.

An example...

<ListView ItemsSource="{Binding Tags}"
            SelectedItem="{Binding SelectedTag}">
    <ListView.Behaviors>
        <behaviors:EventHandlerBehavior EventName="ItemSelected">
            <behaviors:InvokeCommandAction Command="{Binding SelectedTagChanged}" />
        </behaviors:EventHandlerBehavior>
    </ListView.Behaviors>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ContentView Padding="8">
                    <Label Text="{Binding DisplayValue}" />
                </ContentView>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

In this example, the ItemSelected event will map to the SelectedTagChanged command in your view model which looks like this...

public Command SelectedTagChanged
{
    get
    {
        return new Command(row =>
        {
            // do something
        });
    }
}
0
votes

Why not bind the selectedItem attribute of your listview to a viewModel property (lets say a 'selectedItem' property); then on the setter for the 'selectedItem' property in your viewModel you execute your command.