locked
Listview selectedItem using MVVM and XAML RRS feed

  • Question

  • User219939 posted

    I am so frustrated that I cannot find one single WORKING example of how to get the selected item from a listview using XAML and MVVM. I have found numerous incomplete examples using different behaviors. I did find one promising one using Xamarin.Forms.Behaviors but again, couldn't get it to work. I'm new to Xamarin but I have been coding for 15 years and I have to say the learning curve here has be brutal.

    I would really appreciate any help!

    Monday, August 8, 2016 8:42 PM

All replies

  • User74386 posted

    Hi.

    I dont know if you already solved to show the ListView with DataSource, but If you problem is just to take the selectedItem, you need to Implement the Event called : ItemSelected, and implemente the method on code behind. For example:

    XAML: <ListView ItemSelected="OnSelectedItem" />

    Code Behind: void OnSelectedItem(object sender,SelectedItemChangedEventArgs e) { // Them cast the object SENDER to your Datasource Object, my case House House myHouse = sender as House; }

    Monday, August 8, 2016 10:42 PM
  • User57869 posted

    The MVVM way is to just bind the SelectedItem to a property in your ViewModel. It will be set automatically, when the user selects a new item.

    Tuesday, August 9, 2016 9:29 AM
  • User105885 posted

    In XAML you should have something like this:

    <ListView
        ItemsSource="{Binding YourItems}"
        SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}" >
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding ItemProperty}" Detail="{Binding AnotherItemProperty}"/>
            </DataTemplate>
        </ListView.ItemTemplate>    
    </ListView>
    

    Remember to set the binding context for your page in your constructor

    BindingContext = new YourBindingViewModel();
    

    In your viewmodel (remember your viewmodel must implement INotifyProperyChanged You should also create a property for YourItems which should be an ObservableCollection

    YourDataObject _yourSelectedItem;
    public YourDataObject YourSelectedItem
    {
        get
        {
            return _yourSelectedItem;
        }
    
        set
        {
            _yourSelectedItem = value;
            OnPropertyChanged("YourSelectedItem");
    
        }
    }
    

    Now it should work for you :smile:

    Tuesday, August 9, 2016 9:52 AM
  • User234341 posted

    Hi ! What do you want to do with the selected item ? If you want to execute a commande or a Method i can tell you how.

    otherwise, Jacob's answer should help you :)

    Tuesday, August 9, 2016 12:02 PM
  • User219939 posted

    I'm populating a listview (I can do this no problem). I don't want to use code behind as I would like to keep everything in my viewmodel. I'd like to execute a command in my viewmodel using an ID parameter from my listview. I can get to the "PeepDetails" method in my viewmodel but I'm not sure how to see the parameters from the listview's selected item. Not sure if it matters but I am using Prism as my MVVM framework.

    Here is basically what I have in my XAML

    <ListView x:Name="lstPeople" ItemsSource="{Binding peoplelist}" HasUnevenRows="True"> <b:Interaction.Behaviors> <b:BehaviorCollection> <b:EventToCommand EventName="ItemTapped" Command="{Binding PeopleDetailCommand}" CommandParameter="{Binding ID}"/> /> </b:BehaviorCollection> </b:Interaction.Behaviors> <ListView.ItemTemplate> <DataTemplate> <ViewCell Height="100" > <ViewCell.View> <Grid Padding="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" > <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Source="{Binding ImageUrl}" Aspect="AspectFill" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" /> <Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" HorizontalOptions="Center" Font="Large" /> <Label Grid.Row="1" Grid.Column="1" Text="{Binding Description}" HorizontalOptions="Center" Font="Large" /> <Label Grid.Row="0" Grid.Column="3" Text="{Binding ID}" HorizontalOptions="Center" />
    </Grid> </ViewCell.View> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

    And in my ViewModel

    ` public DelegateCommand PeopleDetailCommand { get; private set; }

        public MainPageViewModel(INavigationService navigationService)
        {
            PeopleDetailCommand = new DelegateCommand(PeepDetails);            
        }
    
        public async void PeepDetails()
        {
            //here is where I need the parameters 
        }`
    
    Tuesday, August 9, 2016 3:06 PM
  • User116727 posted

    If you need parameters supplied to the DelegateCommand, you should be using DelegateCommand<T> where T is your data type of the parameter.

    Monday, August 15, 2016 9:41 PM
  • User219939 posted

    I think I'm not doing something right. When I change to use DelegateCommand<T> I'm still not getting the value of the parameter from the listview.

    ` public DelegateCommand '<'string> PeopleDetailCommand { get; private set; }

    public MainPageViewModel(INavigationService navigationService)
    {
        PeopleDetailCommand = new DelegateCommand<string>(PeepDetails);            
    }
    
    public async void PeepDetails(string myvalue)
    {
     //myvalue is always null
    
    }`
    
    Tuesday, August 16, 2016 3:38 PM
  • User116727 posted

    Yup, you're not doing something right. This means that whatever you are using for your CommandParameter is null. You can verify this by hardcoing the CommandParameter = Some Value and then execute the command again. It will work. Doublecheck what you are using as a parameter.

    Tuesday, August 16, 2016 4:07 PM
  • User219939 posted

    Thanks for your help! I've been watching your videos on Prism and I am actually using the template pack and Prism for this project. This is actually my very first Xamarin project.

    Yeah, I just hard coded the command parameter and it passed over to the delegatecommand correctly. I'm using the same property that I'm using in the listview "ID". It shows in the listview when I run the app but the command parameter just isn't doesn't seem to be populated. Do they not reference the same property? I've marked (**) the two lines that bind to "ID" below.

    <ListView x:Name="lstPeople" ItemsSource="{Binding peoplelist}" HasUnevenRows="True"> <b:Interaction.Behaviors> <b:BehaviorCollection> **<b:EventToCommand EventName="ItemTapped" Command="{Binding PeopleDetailCommand}" CommandParameter="{Binding ID}"/> />** </b:BehaviorCollection> </b:Interaction.Behaviors> <ListView.ItemTemplate> <DataTemplate> <ViewCell Height="100" > <ViewCell.View> <Grid Padding="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" > <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Source="{Binding ImageUrl}" Aspect="AspectFill" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" /> <Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" HorizontalOptions="Center" Font="Large" /> <Label Grid.Row="1" Grid.Column="1" Text="{Binding Description}" HorizontalOptions="Center" Font="Large" /> **<Label Grid.Row="0" Grid.Column="3" Text="{Binding ID}" HorizontalOptions="Center" /> **
    </Grid> </ViewCell.View> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

    Tuesday, August 16, 2016 4:53 PM
  • User77966 posted

    At the point you are making your Binding the BindingContext is your ViewModel, and since there is no 'ID' property in your ViewModel you would see this return nothing to your Command.

    For what you are trying to do I would use an attached behavior

     public class ItemTappedAttached
     {
            public static readonly BindableProperty CommandProperty =
            BindableProperty.CreateAttached (
                propertyName: "Command",
                returnType: typeof(ICommand),
                declaringType: typeof(ListView),
                defaultValue: null,
                defaultBindingMode: BindingMode.OneWay,
                validateValue: null,
                propertyChanged: OnItemTappedChanged);
    
        public static ICommand GetItemTapped(BindableObject bindable)
        {
            return (ICommand)bindable.GetValue (CommandProperty);
        }
    
        public static void SetItemTapped(BindableObject bindable, ICommand value)
        {
            bindable.SetValue (CommandProperty, value);
        }
    
        public static void OnItemTappedChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = bindable as ListView;
            if (control != null)
                control.ItemTapped += OnItemTapped;
        }
    
        private static void OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            var control = sender as ListView;
            var command = GetItemTapped (control);
    
            if (command != null && command.CanExecute (e.Item))
                command.Execute (e.Item);
        }
    }
    

    You can attach this to you list view like so:

    <ListView x:Name="lstPeople"  ItemsSource="{Binding peoplelist}" HasUnevenRows="True" local:ItemTappedAttached.Command="{Binding PeopleDetailCommand}>
    
    Wednesday, August 17, 2016 1:25 AM
  • User219939 posted

    @SapienDeveloper you are awesome!!!!!

    That is exactly what I needed. Thank you very much!

    Wednesday, August 17, 2016 3:40 AM
  • User77966 posted

    @Michaelc No problem. Glad to help

    Wednesday, August 17, 2016 3:48 AM
  • User112706 posted

    @SapienDeveloper thank ou too, with Prism this solved all problems.

    Friday, September 2, 2016 8:50 PM
  • User292111 posted

    Can Anyone suggest me books or online tut orial which i should learn for the basics of xamarin?

    Tuesday, January 31, 2017 5:07 AM
  • User76049 posted

    @SHAKTIMAN

    https://blogs.msdn.microsoft.com/microsoft_press/2016/03/31/free-ebook-creating-mobile-apps-with-xamarin-forms/

    There's courses on various website like pluralsight

    https://www.pluralsight.com/courses/xamarin-forms-introduction

    You can also try some of the self guided learnign courses in Xamarin university which are a great start

    https://university.xamarin.com/self-guided

    Tuesday, January 31, 2017 10:06 AM
  • User292111 posted

    Thanks NMackay

    Wednesday, February 1, 2017 4:42 AM
  • User347798 posted

    you can this code

    in view Model (say : GeneralViewModel) // my model is GeneralDataModel private GeneralDataModel _Name;

        public GeneralDataModel Name
        {
            get { return _Name; }
    
            set
            {
                _Name = value;
                OnPropertyChanged();
                Show();
            }
    
        }
    
        public async void Show()
        {
            await new MainPage().DisplayAlert("Ok", "You Select " + Name.name, "Ok");
           // note name is property in my model (say : GeneralDataModel )
        }
    

    in xaml Design ListView ItemsSource="{Binding GeneralDataModel}" SelectedItem="{Binding Name, Mode=TwoWay}" HasUnevenRows="True">

    in xaml cs public MainPage() { InitializeComponent(); BindingContext = new GeneralViewModel(); }

    Wednesday, October 18, 2017 9:23 AM
  • User365191 posted

    @SapienDeveloper said: At the point you are making your Binding the BindingContext is your ViewModel, and since there is no 'ID' property in your ViewModel you would see this return nothing to your Command.

    For what you are trying to do I would use an attached behavior

     public class ItemTappedAttached
     {
          public static readonly BindableProperty CommandProperty =
            BindableProperty.CreateAttached (
                propertyName: "Command",
                returnType: typeof(ICommand),
                declaringType: typeof(ListView),
                defaultValue: null,
                defaultBindingMode: BindingMode.OneWay,
                validateValue: null,
                propertyChanged: OnItemTappedChanged);
    
      public static ICommand GetItemTapped(BindableObject bindable)
        {
            return (ICommand)bindable.GetValue (CommandProperty);
        }
    
        public static void SetItemTapped(BindableObject bindable, ICommand value)
        {
            bindable.SetValue (CommandProperty, value);
        }
    
        public static void OnItemTappedChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = bindable as ListView;
            if (control != null)
                control.ItemTapped += OnItemTapped;
        }
    
        private static void OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            var control = sender as ListView;
            var command = GetItemTapped (control);
    
            if (command != null && command.CanExecute (e.Item))
                command.Execute (e.Item);
        }
    }
    

    You can attach this to you list view like so:

    I have created the behavior in the MVVMFramework.Behaviors.ItemTappedAttached

    and consume it in XAML by

    Wednesday, May 30, 2018 4:03 PM
  • User366431 posted

    Houssem Dellai - has a video on Behavior & BindableProperty in Xamarin.Forms Then you can bind to a ViewModel Command. Check this out

    Saturday, August 24, 2019 4:49 PM