Listview selectedItem using MVVM and XAML

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!

Posts

  • arquitecto_arquitecto_ USMember

    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; }

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭

    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.

  • YasahbiYasahbi FRMember ✭✭
    edited August 2016

    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 :)

  • MichaelcMichaelc USMember

    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 
        }`
    
  • BrianLagunasBrianLagunas USInsider ✭✭✭

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

  • MichaelcMichaelc USMember

    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
    
    }`
    
  • BrianLagunasBrianLagunas USInsider ✭✭✭

    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.

  • MichaelcMichaelc USMember

    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>

  • SapienDeveloperSapienDeveloper USMember ✭✭

    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}>
    
  • MichaelcMichaelc USMember

    @SapienDeveloper you are awesome!!!!!

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

  • SapienDeveloperSapienDeveloper USMember ✭✭

    @Michaelc No problem. Glad to help

  • GuiWaltrickeGuiWaltricke BRMember ✭✭

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

  • SHAKTIMANSHAKTIMAN INMember ✭✭

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

  • NMackayNMackay GBInsider, University ✭✭✭✭✭

    @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

  • SHAKTIMANSHAKTIMAN INMember ✭✭

    Thanks NMackay

  • AhmedAdel.2002AhmedAdel.2002 USMember
    edited October 18

    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();
    }

Sign In or Register to comment.