To update automatically the ItemsSource property of Xamarin.Forms ListView, I try to use ObservableCollection object in the code behind file. Then I assign that ObservableCollection object to the ListView.ItemsSource property . But I just do the assignment just one in the constructor of the code behind file.
Example 1 with a timer, updating the ObservableCollection object automatically changes the view in Xaml file. Example 1 works as I expect with ObservableCollection.
Example 2 does not work when the button named "Set 2" is pressed. The view in the xaml file does not change with new data event handler of that button updates in the code behind. Please explain the reason why Example 2 does not work even though I use the same concept of ObservableCollection as in Example 1.
The following are codes of Example 1 that work as I expect.
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace ObservableLogger
{
public partial class ObservableLoggerPage : ContentPage
{
public ObservableLoggerPage()
{
InitializeComponent();
ObservableCollection<DateTime> list = new ObservableCollection<DateTime>(); // ObservableCollection<DateTime> object is used
listView.ItemsSource = list; // ********** bind
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
list.Add(DateTime.Now); // *** changing the ObservableCollection<DateTime> object automatically makes ListView's ItemsSource is rebound and ListView's UI changed.
return true;
});
}
}
}
<?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="ObservableLogger.ObservableLoggerPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="10, 20, 10, 0"
Android="10, 0"
WinPhone="10, 0" />
</ContentPage.Padding>
<ListView x:Name="listView" />
</ContentPage>
The following are codes of Example 2 that do not work as I expect. When button named "Set 2" is hit, its event handler changes ObservableCollection people (a private field), but the ListView is not updated in UI.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Mvvm1
{
public partial class Mvvm1Page : ContentPage
{
private ObservableCollection<Person> _people; // private field
public Mvvm1Page()
{
InitializeComponent();
_people = getPeople(SetOption.Set1);
listViewPeople.ItemsSource = _people; // Only one time of assigning
}
public void ButtonSet1OnClicked(object sender, EventArgs e)
{
_people = getPeople(SetOption.Set1); // *** do change the ObservableCollection<Person> but ListView is not updated in UI.
}
public void ButtonSet2OnClicked(object sender, EventArgs e)
{
_people = getPeople(SetOption.Set2); // *** do change the ObservableCollection<Person> but ListView is not updated in UI.
}
private ObservableCollection<Person> getPeople(SetOption op)
{
var list = new ObservableCollection<Person>();
var p1 = new Person { Id = 1, Age = 19, FirstName = "Anna", LastName = "Larson" };
var p2 = new Person { Id = 2, Age = 23, FirstName = "Beri", LastName = "Slovik" };
var p3 = new Person { Id = 3, Age = 65, FirstName = "Ron", LastName = "Prelosi" };
var p4 = new Person { Id = 4, Age = 32, FirstName = "William", LastName = "Maxel" };
var p5 = new Person { Id = 5, Age = 71, FirstName = "Fred", LastName = "Lipez" };
var p6 = new Person { Id = 6, Age = 44, FirstName = "Dave", LastName = "Vanoviz" };
switch (op)
{
case SetOption.Set1:
list.Add(p1);
list.Add(p2);
list.Add(p3);
list.Add(p4);
break;
case SetOption.Set2:
list.Add(p5);
list.Add(p6);
break;
}
return list;
}
}
}
<?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="Mvvm1.Mvvm1Page">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0" />
</ContentPage.Padding>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="0"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackLayout VerticalOptions="StartAndExpand" Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
<Button Text="Set 1" Clicked="ButtonSet1OnClicked"></Button>
<Button Text="Set 2" Clicked="ButtonSet2OnClicked"></Button>
</StackLayout>
<ListView x:Name="listViewPeople" HasUnevenRows="True" Grid.Column="0" Grid.Row="1" Header="People">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="5">
<Frame OutlineColor="Accent" Padding="10">
<StackLayout Orientation="Horizontal">
<StackLayout>
<Label Text="{Binding Id}"></Label>
<Label Text="{Binding FirstName}"></Label>
<Label Text="{Binding LastName}"></Label>
<Label Text="{Binding Age}"></Label>
</StackLayout>
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
using System.Text;
using System.Threading.Tasks;
namespace Mvvm1
{
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public enum SetOption
{
Set1 = 1,
Set2 = 2
}
}
Calling
_people = getPeople(SetOption.Set1);
in a Button Click handler does not modify the existing collection instance, but instead creates a new one, and does hence not change items in listViewPeople.ItemsSource
.
You'll have to set the ItemsSource
property each time you create a new collection. Then you wouldn't need the local _people
variable at all:
public Mvvm1Page()
{
InitializeComponent();
listViewPeople.ItemsSource = getPeople(SetOption.Set1);
}
public void ButtonSet1OnClicked(object sender, EventArgs e)
{
listViewPeople.ItemsSource = getPeople(SetOption.Set1);
}
public void ButtonSet2OnClicked(object sender, EventArgs e)
{
listViewPeople.ItemsSource = getPeople(SetOption.Set2);
}
Alternatively, you can assign the ItemsSource once and subsequently add and remove items to/from the existing collection, like e.g. _people.Add(new Person { ... });
Besides that, listView.ItemsSource = list;
is not a Binding, but just a regular assignment.