Xamarin Forms ListView MVVM Binding ItemSource Example

  • by
xamarin-forms-listview-mvvm-itemsource-binding

Xamarin Forms: Binding ListView to a ItemSource using MVVM architecture

Xamarin forms advises us to follow MVVM architectural pattern to create our apps. Using MVVM we can create apps that provides us with code reusability. ListView in Xamarin forms can be integrated with custom layout to create a great looking listings in our app. In this example we will use MVVM pattern to create a Xamarin forms ListView. Its itemsource will be bound to ViewModel class. We will use a model class to create an ObservableCollection and this collection will be our final item source.xamarin-forms-listview-mvvm-itemsource-binding

DOWNLOAD SOURCE CODE

Create a new Model class in your Xamarin forms project with name Products.cs and edit it as below:

Models > Products.cs:

namespace XamSample.Models
{
public class Products
{
public string productName { get; set; }
public string productCompany { get; set; }
public string productPrice { get; set; }
public string productImage { get; set; }
public string productRating { get; set; }

}
}

We will display Product information on our ListView with a product image. The app will contain two pages one to display Product listing on listview and other to view selectedItem. We will use same ViewModel for both our Views and set SelectedItem.

ViewModels > VMProducts.cs:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using XamSample.Models;

namespace XamSample.ViewModels
{
public class VMProducts : INotifyPropertyChanged
{
private ObservableCollection<Products> _obProducts;
public ObservableCollection<Products> obProducts
{
get => _obProducts;
set { _obProducts = value; OnPropertyChanged(); }
}

private Products _selectedProduct;

public Products selectedProduct
{
get => _selectedProduct;
set {
_selectedProduct = value;
OnPropertyChanged();
}
}

public VMProducts()
{
AddProducts();

}

public void AddProducts()
{
try
{
obProducts = new ObservableCollection<Products>();
obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/813y2%2BdPUOL._AC_SL1500_.jpg",
productName = "Samsung Note 20 5G Ultra",
productPrice = "$1099",
productCompany = "Samsung",
productRating = "4.5"
});

obProducts.Add(new Products
{
productImage = "https://m.media-amazon.com/images/I/71sNNCTfMuL._FMwebp__.jpg",
productName = "Apple iPhone 12 mini",
productPrice = "$699",
productCompany = "Apple",
productRating = "4.6"
});

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/61v0enHOXpL._AC_SL1500_.jpg",
productName = "Google Pixel 2 64 GB",
productPrice = "$117",
productCompany = "Google",
productRating = "3.8"
});

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/71MHTD3uL4L._SL1500_.jpg",
productName = "iPhone 12 Pro Max",
productPrice = "$1200",
productCompany = "Apple",
productRating = "4.0"
});

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/71OYLm6srFL._SL1500_.jpg",
productName = "Samsung Galaxy S20 FE",
productPrice = "$600",
productCompany = "Samsung",
productRating = "4.1"
});

 

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/410mmBW-AYL._AC_.jpg",
productName = "OnePlus 8 Pro 5G",
productPrice = "$924",
productCompany = "OnePlus",
productRating = "4.7"
});

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/71valQo5u5L._AC_SL1500_.jpg",
productName = "Samsung Galaxy A01",
productPrice = "$121.76",
productCompany = "Samsung",
productRating = "4.1"
});

obProducts.Add(new Products
{
productImage = "https://images-na.ssl-images-amazon.com/images/I/61sLvszoETL._AC_SL1500_.jpg",
productName = "Nokia 2.4 Android 10",
productPrice = "$1099",
productCompany = "Nokia",
productRating = "3.5"
});
}
catch (Exception ex)
{

}
}

#region INotifyChangedProperties
public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyChangedProperties

}
}

The ViewModel contains an ObservableCollection to type Product model class and we are adding items to it. These items can be fetched from a Web API/ Database. Here, I’m adding static data for creating this example. This collection will be the ItemSource for our ListView control. The selectedProduct will be set from View once the use selected any item.

Create two Views like below:

Views > ViewProducts.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamSample.Views.ViewProducts"
Title="XamarinForms MVVM ListView"
>
<ContentPage.Content>

<StackLayout HorizontalOptions="Center" VerticalOptions="FillAndExpand" Padding="10">

<ListView x:Name="lstProducts" HorizontalOptions="StartAndExpand" VerticalOptions="FillAndExpand"
SeparatorColor="LightGray" SeparatorVisibility="Default" HasUnevenRows="True"
ItemsSource="{Binding obProducts}" CachingStrategy="RecycleElement"
ItemSelected="LstProducts_ItemSelected" SelectedItem="{Binding selectedProduct,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="5" VerticalOptions="FillAndExpand"
MinimumHeightRequest="100">
<Image WidthRequest="70" HeightRequest="70">
<Image.Source>
<UriImageSource Uri="{Binding productImage}"
CacheValidity="2"
CachingEnabled="True"/>
</Image.Source>
</Image>
<StackLayout Orientation="Vertical" Padding="2" VerticalOptions="Center" Spacing="2">

<Label Text="{Binding productName}" FontSize="18" TextColor="Black" FontAttributes="Bold"/>

<StackLayout Orientation="Horizontal" HorizontalOptions="Start">
<Label Text="by" FontSize="12" TextColor="Gray"/>
<Label Text="{Binding productCompany}" FontSize="12" TextColor="Gray"/>

</StackLayout>

<Label Text="{Binding productPrice}" FontSize="16" TextColor="#eb3443"/>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding productRating}" FontSize="12" TextColor="#3483eb"/>
<Label Text="/5" FontSize="12" TextColor="#3483eb"/>
</StackLayout>

</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

</StackLayout>

</ContentPage.Content>
</ContentPage>

This is our Listing page. We are display all our items on ListView control. The ItemSource is being set to obProducts and SelectedItem to SelectedProduct variable. We will set its ViewModel from code-behind. On any selecting item, it will navigate to next view.

Views > ViewProductDetails.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamSample.Views.ViewProductDetails"
Title="{Binding selectedProduct.productName}">
<ContentPage.Content>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="5"
Spacing="5">

<Image x:Name="imgProduct" WidthRequest="200" HeightRequest="200">
<Image.Source>
<UriImageSource Uri="{Binding selectedProduct.productImage}"
CacheValidity="2"
CachingEnabled="True"></UriImageSource>
</Image.Source>
</Image>
<StackLayout Orientation="Vertical" Padding="15">
<Label x:Name="lblProductName" Text="{Binding selectedProduct.productName}" FontSize="20" FontAttributes="Bold"/>

<StackLayout Orientation="Horizontal" HorizontalOptions="Start">
<Label Text="by" TextColor="Gray"/>
<Label x:Name="lblCompany" Text="{Binding selectedProduct.productCompany}" TextColor="Gray"/>
</StackLayout>

<StackLayout Orientation="Horizontal" HorizontalOptions="Start">
<Label x:Name="lblRatings" Text="{Binding selectedProduct.productRating}" TextColor="#3483eb"/>
<Label Text="/5" TextColor="#3483eb"/>
</StackLayout>
<Label x:Name="lblPrice" Text="{Binding selectedProduct.productPrice}" TextColor="#eb3443"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>

This is again bound to the same selectedProduct variable from the ViewModel class. Edit the code-behind of both these Views like below:

Views > ViewProducts.xaml.cs:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XamSample.Models;
using XamSample.ViewModels;

namespace XamSample.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ViewProducts : ContentPage
{
VMProducts vm;
public ViewProducts()
{
InitializeComponent();
vm = new VMProducts();
this.BindingContext = vm;
}

protected override void OnAppearing()
{
if (lstProducts.SelectedItem != null)
lstProducts.SelectedItem = null;
}
private async void LstProducts_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
try
{
if (lstProducts.SelectedItem != null)
{
Products products = (Products)e.SelectedItem;
if (products != null && !string.IsNullOrWhiteSpace(products.productName))
{
await Navigation.PushAsync(new ViewProductDetails(vm));
}

}
}
catch (Exception ex)
{

}
}
}
}

Here we have to catch and first see if the selected item from our ListView is null or not. After this validation check we can move to the next view. Also, OnAppearing() method set listView selectedItem to null if anything is selected from first to avoid confusions for our users.

Views > ViewProductDetails.xaml.cs:

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XamSample.ViewModels;

namespace XamSample.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ViewProductDetails : ContentPage
{

public ViewProductDetails(VMProducts products)
{
InitializeComponent();

if (products != null && products.selectedProduct != null)
{
this.BindingContext = products;
}
}
}
}

Now run your Xamarin app.

Xamarin Forms: Binding ListView to a ItemSource using MVVM – Output:

xamarin-forms-listview-mvvm-itemsource-binding

DOWNLOAD SOURCE CODE

Also see:

Xamarin forms Creating Action Sheets

Xamarin forms MVVM Binding CommandParameter to Command

Xamarin forms SQLite database – Performing CRUD operations

Xamarin forms creating and using SQLite database

Xamarin forms using Google Maps

Xamarin forms using Camera – saving Images to Gallery

Xamarin Forms Creating a Simple ListView