Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

send bound ListView item as command parameter to ViewModel

Andre_S_CAndre_S_C Member ✭✭
edited October 19 in Xamarin.Forms

I have a a listview, populated from the ViewModel, and I can call a command in the VM. I can send e.g. an string id from the current item's binding as the command parameter, but can't seem to work out how to send the etire item. E.g. i might update the selected item from the command as well as perform as REST post using that item.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local ="clr-namespace:AgentApp"
             x:Class="AgentApp.Test" Title="Test"
             x:Name="Page" >
    <ContentPage.BindingContext>
        <local:TestViewModel/>
    </ContentPage.BindingContext>
    <ContentPage.Content>
        <StackLayout  >
            <Label Text="{Binding SelectedItem.Term_id}"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
            <BoxView HeightRequest="1" BackgroundColor="Black" HorizontalOptions="FillAndExpand" />
            <ListView x:Name="propListView"
                SelectedItem="{Binding SelectedItem}"
                ItemsSource="{Binding SimpleProperties}"  >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell x:Name="Item" >
                            <StackLayout Orientation="Horizontal">
                                <Button Text="{Binding Agt_name}"
                                        CommandParameter="{Binding Term_id}"
                                        Command="{Binding BindingContext.doCheckin, Source={x:Reference Page}}" />
                                <Label Text="{Binding MilesDistance, StringFormat='~{0:f2} mi.'},
                            Converter={ConvertToDouble}}" HorizontalOptions="CenterAndExpand" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

...

using AgentApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace AgentApp
{
    class TestViewModel : BaseViewModel
    {
        private ObservableCollection<Property> simpleProperties;
        public ObservableCollection<Property> SimpleProperties { set { SetProperty(ref simpleProperties, value); } get { return simpleProperties; } }

        private Property selectedItem;
        public Property SelectedItem { get { return selectedItem; } set { SetProperty(ref selectedItem, value); } }

        public ICommand setSelection { private set; get; }
        public ICommand doCheckin { private set; get; }
        public TestViewModel()
        {
            var SimplePropertiesList = new List<Property>();
            for (int i = 1; i <= 5; i++)
            {
                Property prop = new Property();
                prop.Term_id = i.ToString();
                prop.Agt_name = "Prop " + i.ToString();
                prop.MilesDistance = (double)i * 2.5;
                SimplePropertiesList.Add(prop);
            }
            SimpleProperties = new ObservableCollection<Property>(SimplePropertiesList);

            /* for buttons:*/
            doCheckin = new Command<Property>(
                execute: (Property item) =>
                {
                    Console.WriteLine("doCheckin() executes: " + item.Term_id);
                    // some logic and async work
                },
            canExecute: (Property item) =>
            {
                Console.WriteLine("doCheckin() canExecute return" + item.Term_id);
                return true; //some logic 
            }
                );
        }
    }
}

Best Answer

Answers

  • Andre_S_CAndre_S_C Member ✭✭

    I've tried things like CommandParameter="{Binding Source={x:Reference propListView}}" in the button...

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    edited October 19

    Please use following command. in the Button

       <Button " Text="More" VerticalOptions="StartAndExpand" Command="{ Binding BindingContext.doCheckin, Source={x:Reference Name=propListView} }"  CommandParameter="{Binding .}"  />
    

    Here is a similar thread;
    https://stackoverflow.com/questions/60437560/send-id-dynamically-on-button-click-xamarin

  • Andre_S_CAndre_S_C Member ✭✭

    That does not work, but it fails in an interesting way.
    When I have the Command="{Binding .}" and try to receive my Property class object the app crashes as soon as I open the view. Receiving the command parameter as a String, the buttons are all disabled even thoughcanExecute:` is hard-coded to return true.
    Basically what I want to do in the commend is something like

     doCheckin = new Command<Property>(
                    execute: (Property item) =>
                    {
                        Console.WriteLine("doCheckin() executes: " + item.Term_id);
                        SelectedItem = item;
                    },
                canExecute: (Property item) =>
                {
                    Console.WriteLine("doCheckin() canExecute return" + item.Term_id);
                    return (SelectedItem != item); 
                }
                    );
    

    where in my view what I imagine the button should look like is something like

    <Button Text="{Binding Agt_name}"
        Command="{Binding BindingContext.doCheckin, 
            Source={x:Reference Page}}"
            CommandParameter="{Binding BindingContext.doCheckin,
                Source={x:Reference Item},
                Mode=OneWayToSource}"
                                            />
    
  • Andre_S_CAndre_S_C Member ✭✭

    Awesome, thanks, you rock! :^)
    One thing that confuses me now, although it might not be critical, is how to get the clicked button to be disabled. Perhaps I misunderstand the functionality of can execute... more specifically I wanted to test if the canExecute to return
    item != SelectedItem
    but selectedItem seems to be inacesible or null when I test it :-|

     canExecute: (Property item) =>
                    {
                        if (item == null)
                        {
                            return true;
                        }
                        else
                        {
                            if (SelectedItem != null)
                            {
                                Console.WriteLine("have  selection [" + SelectedItem.Term_id + " ]");
                                return (SelectedItem != item);
                            }
                            else
                            {
                                Console.WriteLine("no selection");
                                return true;
                            }
                        }
                    }
    

    always shows no selection.

    PS. I also changed the view to

    <ListView 
                    SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                    ItemsSource="{Binding SimpleProperties}"  >...
    

    so hitting the button selects the listview row.

  • Andre_S_CAndre_S_C Member ✭✭

    hmm, tomorrow I'll try putting a selected flag in inside the item class, my brain is giving up for now

  • Andre_S_CAndre_S_C Member ✭✭

    In case anyone else ever comes trawling through this, for the button to be disabled canExecute: seems to have to be explicitly triggered when the command's execute changes a relevant property, so in the command code above, rather something like :

    execute: (Property item) =>
                    {
                        SelectedItem = item;
                        (doCheckin as Command).ChangeCanExecute();
                    },
    

    as per here

Sign In or Register to comment.