65.9K
CodeProject is changing. Read more.
Home

Updating the Items Displayed of a listView in Xamarin.Forms

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.06/5 (6 votes)

Oct 13, 2018

CPOL

2 min read

viewsIcon

25468

How to update the Items displayed of a ListView in Xamarin.Forms

Introduction

I have a Xamarin.Forms application displaying a list of items. This is a read only list that gets notified of updates from the server. Visual Studio has a template for the Cross Platforms application, with Xamarin.Forms to have a Master Detail project. This template contains a listView that is updated from the interface itself. But I needed the items in the list to get updated without any user interaction.

Background

The template generated by Visual Studio is a good starting point, but it didn't work as expected. There are lots of comments in Stackoverflow and others about the topic, many of them point to different directions.

Using the Code

First, you have your list in your .xaml file:

            <ListView x:Name="ItemsListView" 
                ItemsSource="{Binding Bases}"
                VerticalOptions="FillAndExpand"
                 HasUnevenRows="true"
                 RefreshCommand="{Binding LoadItemsCommand}"
                 IsPullToRefreshEnabled="true"
                 IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                 CachingStrategy="RecycleElement"
                 ItemSelected="OnBaseSelected">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Padding="10" Orientation="Vertical">
                                <Grid>
                                   <Grid.RowDefinitions>
                                       <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <ContentPage.Resources>
                                        <ResourceDictionary>
                                            <local:BooleanToColorConverter 
                                            x:Key="BooleanToColorConverter" /> 
                                            <!-- red or green -->
                                        </ResourceDictionary>
                                    </ContentPage.Resources>
                                    <Label Text="{Binding Name}" 
                                    LineBreakMode="NoWrap" 
                                    Style="{DynamicResource ListItemTextStyle}" 
                                    FontSize="16"  Grid.Row="0" Grid.Column="0"/>
                                    <Label Text="Connection" 
                                    FontSize="15" TextColor="{Binding IsConnected, 
                                    Converter={StaticResource BooleanToColorConverter}}" 
                                    VerticalOptions="Center" FontAttributes ="Bold" 
                                       HorizontalOptions="End"  
                                       Grid.Row="0" Grid.Column="1" >                                        
                                    </Label>
                                    <Label Text="Unplugged" FontSize="25" 
                                    FontAttributes="Bold" TextColor="Black" 
                                    VerticalOptions="Center" HorizontalOptions="Center" 
                                    Grid.Row="0" Grid.Column="2" 
                                    IsVisible="{Binding IsNotPlugged}">
                                    </Label>                                    
                                </Grid>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

In the code behind, Bases is an observable collection of items. It's assigned to the ItemsSource attribute of the listView (ItemsSource="{Binding Bases}").

public ObservableCollection<Base> Bases { get; set; }

In the constructor:

Bases = new ObservableCollection<Base>();

This makes the list to be updated when an item is added or removed:

Base b = item as Base;
Bases.Add(b);

var found = Bases.FirstOrDefault(x => x.BaseNumber == item);
Bases.Remove(found);

But whenever an item itself is updated, the observable collection will not trigger any notification to the interface. In order to fix that, the item itself needs to implement the INotifyPropertyChanged interface. This will add an event of type PropertyChangedEventHandler, that needs to be called in the setter of the properties that should be updated.

public class Base: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };

private string name;
public string Name
{
    get { return name; }
    set
     {
      name = value;
      PropertyChanged(this, new PropertyChangedEventArgs("Name"));
     }
}

private bool isConnected;
public bool IsConnected {
    get { return isConnected; }
    set 
      {
      isConnected = value;
      PropertyChanged(this, new PropertyChangedEventArgs("IsConnected"));
      }
}

private bool isPlugged;
public bool IsPlugged
{
    get { return isPlugged; }
    set
      {
        isPlugged = value;
        PropertyChanged(this, new PropertyChangedEventArgs("IsNotPlugged"));
      }
}
}

If you have a read only property, you need to add it in the set of the property that affects it.

Additionally, if you need to reset the items of the observable collection, never ever create a new one but use clear.

Bases.Clear();

foreach (Base b in items as List<Base>)
{
Bases.Add(b);
}

If you create a new observable collection, the code behind will correctly point to the new one but the listView will continue bind to the old one.

Finally, to make it work with Android 7, following this link, you need to add a reference to System.ObjectModel in the Android project - not in the library project.

History

  • 13th October, 2018: Initial version