Search code examples
xamarin.formsdata-bindingdatatemplate

ListView show only the last item in a list when using DataTemplate


I'm binding a customer list to a Xamarin Listview. I want to use a DataTemplate with a ViewCell in it.

As per documentation, we can use one of 2 constructors:

  1. new DataTemplate(typeof(CustomerViewCell));

  2. new DataTemplate(=>new CustomerViewCell);

However, they lead to different results. The first one correctly displays the list, while the second one shows repeated the last item of the list.

Do I don't know something basic here?

Here's my customer model:

 public class Customers : List<Customer>
    {

        public Customers()
        {
            Add(new Customer { No = 1, Name = "Microsoft" });
            Add(new Customer { No = 2, Name = "Google" });
            Add(new Customer { No = 3, Name = "Facebook" });
        }

    }

    public class Customer {

       public int No { get; set; }
        public string Name { get; set; }

    }

And here is the CustomerViewCell.xaml, which is inherited from ViewCell

<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="LVDataTemplate.View.CustomerViewCell">
  <ViewCell.View>
      <StackLayout Orientation="Horizontal">
          <Label Text="{Binding No}" />
          <Label Text="{Binding Name}" />
        </StackLayout>
  </ViewCell.View>
</ViewCell>

The CustomerListView, which shows the Customers:


    public partial class CustomerListView :ContentPage
    {

        public CustomerListView()
        {
            InitializeComponent();

            MyListView.ItemsSource = new Customers();

            //1. Work correctly 
            // MyListView.ItemTemplate = new DataTemplate(typeof(CustomerViewCell));

            //2. Work in-correctly. only the last item repeated through out the list
            var theCell = new CustomerViewCell();
            MyListView.ItemTemplate = new DataTemplate(()=>theCell);

        }


    }

The first code shows the result:

  1. Microsoft.
  2. Google
  3. Facebook

The second shows

  1. Facebook
  2. Facebook
  3. Facebook

Solution

  • Please read the document here: data-templates/creating

    The method public DataTemplate(Func<object> loadTemplate); is use to create a datatemplate as a resource, so the correct usage of the second constructor is:

    var personDataTemplate = new DataTemplate(() => {
    
            return new ViewCell1();
        });
    
    
        Resources = new ResourceDictionary();
        Resources.Add("personTemplate", personDataTemplate);
    
        testListView.ItemTemplate = (DataTemplate)Resources["personTemplate"];
    

    Or you can customize the ViewCell like this:

    var personDataTemplate = new DataTemplate (() => {
      var grid = new Grid ();
      ...
      return new ViewCell { View = grid };
    });
    

    Also, you can use personDataTemplate directly without define it as a resource:

     testListView.ItemTemplate = personDataTemplate;
    

    The problem of your code is you should create theCell inside the block, you can use:

        var personDataTemplate = new DataTemplate(() => {
    
            return new ViewCell1();
        });
    

    Or

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();       
            testListView.ItemTemplate = new DataTemplate(() => test());
        }
    
        public ViewCell1 test() {
    
            //other actions
            return new ViewCell1();
        }
    }
    

    The first constructor MyListView.ItemTemplate = new DataTemplate(typeof(CustomerViewCell)); is creating-a-datatemplate-with-a-type, that is correct.