2

In a resource dictionary I am defining a DataTemplate with x:Key="AnimalsDataTemplate". The resource dictionary is registered under Application.Resources in App.xaml.

In my main page, I have a RefreshView containing a ListView named ListView1, and a class named Animals, and a list AnimalsList populated when new data is created from the Animals class. ListView1's ItemTemplate.DataTemplate is set to x:Name="AnimalsDataTemplate".

I am trying to assign the Animals class to the ListView1's ItemSource; I can assign the variable x like this

<x:Array Type="{x:Type local:MainPage}">
</x:Array>

but I cannot assign it to MainPage.Animals.

I have tried coding all this just in the ListView tag itself, but it will not bind to the Animals class either.

This is my App.xaml file:

<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:M_Android"
             x:Class="M_Android.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/DataTemplates.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

And here's my DataTemplates.xaml file:

<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

    <DataTemplate x:Key="AnimalsDataTemplate">
        <ViewCell>
            <Grid RowSpacing="6"
                  ColumnSpacing="6">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Label Text="{Binding Name}"
                       TextColor="Black"
                       FontAttributes="Bold" />
                <Label Grid.Column="1"
                       Text="{Binding Location}"
                       TextColor="Black" />
                <Label Grid.Column="2"
                       Text="{Binding Age}"
                       TextColor="Black"
                       HorizontalTextAlignment="End" />
            </Grid>
        </ViewCell>
    </DataTemplate>
</ResourceDictionary>

My MainPage XAML file:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:M_Android"
             x:Class="M_Android.MainPage">
    <ScrollView>
        <VerticalStackLayout
            Padding="30,0"
            Spacing="25">

            <RefreshView 
                IsRefreshing="{Binding IsRefreshing}"
                Command="{Binding RefreshCommand}"
                x:Name="Rfresh">

                <ListView x:Name = "ListView1">
                    <ListView.ItemsSource>
                        <x:Array Type="{x:Type local:MainPage}">
                        </x:Array>
                    </ListView.ItemsSource>
                    <ListView.ItemTemplate>
                        <DataTemplate x:Name="AnimalsDataTemplate">
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView> 
            </RefreshView>    
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

MainPage.cs file:

public List<Animals> AnimalsList = new List<Animals>();

public class Animals 
{
    public string Name { get; set; }
    public string Location { get; set; }
    public string Age { get; set; }

    public override string ToString()
    {
        return Name;
    }

    public Animals(string name, string location, string age) 
    {
        Name = name;
        Location = location;
        Age = age;       
    }
}

public void ButtonClick(object sender, EventArgs args)
{
    refreshListView();
}

public void refreshListView()
{
    RefreshView refreshView = new RefreshView();
    ICommand refreshCommand = new Command(() => {
        // IsRefreshing is true
        // Refresh data here
        var S = new Animals("Jon", "22", "House");
        AnimalsList.Add(S);

        var S1 = new Animals("Jane", "20", "House");
        AnimalsList.Add(S1);

        var S2 = new Animals("Bill", "300", "House");
        AnimalsList.Add(S2);

        refreshView.IsRefreshing = false;
    });

    refreshView.Command = refreshCommand;
    refreshCommand.Execute(Rfresh);
}

2 Answers 2

2

Unfortunately, there are many things that go wrong in your code. I believe, the main question is how to bind a collection of Animal objects to the ListView, that uses a DataTemplate defined as a resource. Besides that, you are wondering how the RefreshView and the Button are going to work correctly -but those are secondary questions and not really relevant to the main question. So, let's break everything down:

  1. Name your "entity" class Animal instead of Animals. This is just a good practice suggestion but will allow you to name the collection Animals or AnimalsList which is also fine.

  2. You are trying to use the DataTemplate resource incorrectly. Use the ItemTemplate property of the ListView component like this:

<ListView x:Name="ListView1" 
      ItemTemplate="{StaticResource AnimalsDataTemplate}">
  1. You are trying to bind a List<T> to the ListView. That won't work. The recommended way is to use an ObservableCollection<T> instead of List<T>. The ObservableCollection<T> object raises the appropriate notifications when the list changes. If an item is added, updated, or removed from the list, or if the whole list is recreated, any bound objects to this collection are informed and redisplay themselves using the new information in the collection. Also, you need to use the MVVM pattern, which means you need a View Model class as the binding context of your MainPage. Last but not least, both the View Model class and the Animal class need to implement the INotifyPropertyChanged interface to let the UI know of changes to objects in your code. Here's the ViewModel class, MainPageViewModel:
public class MainPageViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Animal> _AnimalsList = new();      
    public ObservableCollection<Animal> AnimalsList
    {
        get { return _AnimalsList; }
        set
        {
            _AnimalsList = value;
            RaisePropertyChanged(nameof(AnimalsList)));
        }
    }
      
    private bool _IsRefreshing;
    public bool IsRefreshing
    {
        get { return _IsRefreshing; }
        set
        {
            _IsRefreshing = value;
            RaisePropertyChanged(nameof(IsRefreshing));
        }
    }
      
    public MainPageViewModel()
    {
        ButtonCommand = new Command(() => IsRefreshing = true);
        RefreshCommand = new Command(() => Refresh());
    }
      
    public void Refresh()
    {
        IsRefreshing = true;
      
        AnimalsList = new ObservableCollection<Animal>()
        {
            new Animal("Jon", "22", "House"),
            new Animal("Jane", "20", "House"),
            new Animal("Bill", "300", "House")
        };
      
        IsRefreshing = false;
    }
      
    public event PropertyChangedEventHandler? PropertyChanged;
      
    public virtual void RaisePropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this,
            new PropertyChangedEventArgs(propertyName));
    }
      
    public ICommand? ButtonCommand { get; private set; }
    public ICommand? RefreshCommand { get; private set; }
}

The ViewModel defines AnimalsList, which will be bound to the ListView. It also defines IsRefreshing, which will be bound to the RefreshView and command object to bind to the RefreshView and Button controls. Setting IsRefreshing = true automatically triggers the RefreshCommand, just like pulling down would do. Please, note the implementation of INotifyPropertyChanged, which is essential.

And here's the Animal class. Again, please, note the implementation of INotifyPropertyChanged, which is absolutely essential:

public class Animal : INotifyPropertyChanged
{
    private string _Name { get; set; }
    private string _Location { get; set; }
    private string _Age { get; set; }
      
    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            RaisePropertyChanged(nameof(Name));
        }
    }
      
    public string Location
    {
        get { return _Location; }
        set
        {
            _Location = value;
            RaisePropertyChanged(nameof(Location));
        }
    }
      
    public string Age
    {
        get { return _Age; }
        set
        {
            _Age = value;
            RaisePropertyChanged(nameof(Age));
        }
    }
      
    public override string ToString()
    {
        return Name;
    }
      
    public Animal(string name, string location, string age)
    {
        Name = name;
        Location = location;
        Age = age;
    }
      
    public event PropertyChangedEventHandler? PropertyChanged;
      
    public virtual void RaisePropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this,
            new PropertyChangedEventArgs(propertyName));
    }
}
  1. The code-behind of MainPage sets the binding context of the view to the view model. It is best to override OnAppearing to set the binding context instead of doing so in the constructor:
public partial class MainPage : ContentPage
{
    private readonly MainPageViewModel _ViewModel;
 
    public MainPage()
    {
        InitializeComponent();
        _ViewModel = new MainPageViewModel();
    }
   
    protected override void OnAppearing()
    {
        base.OnAppearing();
        BindingContext = _ViewModel;
    }
}
  1. Here's the XAML of MainPage:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.MainPage">
   
    <ScrollView>
        <VerticalStackLayout
            Padding="30,0"
            Spacing="25">
   
            <RefreshView IsRefreshing="{Binding IsRefreshing}"
                         Command="{Binding RefreshCommand}"
                         x:Name="Refresh">
                   
                <ListView x:Name="ListView1" 
                      ItemsSource="{Binding AnimalsList}"
                      ItemTemplate="{StaticResource AnimalsDataTemplate}">
                </ListView>
                   
            </RefreshView>
            <Button Text="Refresh" 
                    Command="{Binding ButtonCommand}"></Button>
        </VerticalStackLayout>
    </ScrollView>
   
</ContentPage>

Please, note the bindings. For example, the ListView.ItemsSource binds to the AnimalsList public property of the MainPageViewModel class. Also, note that you want to define the RefreshView once, either in markup or in code (in your code you are defining it twice, both in markup and in code.)

Hope it helps.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for joining in. Some other ideas there. We achieved a working method but still might be better which brings me to a new issue. When I try to implement my function call for refreshing the view after a callback event, I receive an user handle error when trying to update the AnimalsList
Welcome to SO. Please, note that comments cannot be used for discussion purposes. Actually, SO issues a warning if too many comments appear under a post. Also, please, make sure you restrict to one question per post/thread. Reading SO's guideline(s) and How do I ask a good question? can prove very helpful.
0

set the ItemSource in the XAML

<ListView ItemsSource="{Binding AnimalsList}">

and in the code behind, set the BindingContext

public MainPage()
{
    this.BindingContext = this;
    this.AnimalsList = new List<Animals>();
}

finally, AnimalsList must be a public property

public List<Animals> AnimalsList { get; set; }

18 Comments

Ok. I was trying the list bind with no luck. I hadn't seen anything about declaring the BindingContext.
This alone did not solve it.
Did you remove the Itemssource from the XAML?
Also,use an ObservableCollection instead of a List
I changed the Code to your suggestion. I noticed the Observable Collection in posts but have not tried it. Use it same as a List?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.