Search code examples
c#.netmaui

.NET MAUI ListView not rendering data


I am trying to show the medicineList data in the ListPage.xaml using ListView. I have checked the documentation but I don't find anything noticeable. I have looked at this doc and tried to follow it. But it is not working.

This is ListPage.xaml.cs

using MedicineAlert.Data;
using System.Text.Json;

namespace MedicineAlert.Pages;

public partial class ListPage : ContentPage
{
    public List<MedicineData> MedicineList { get; set; }
    public ListPage()
    {
        InitializeComponent();
        // Retrieve the app's local data directory path
        string localDataDirPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        // Combine the local data directory path with your desired subdirectory
        string dbFolderPath = Path.Combine(localDataDirPath, "DB");
        string filePath = Path.Combine(dbFolderPath, "data.json");

        if(File.Exists(filePath))
        {
            string json = File.ReadAllText(filePath);
            MedicineList = JsonSerializer.Deserialize<List<MedicineData>>(json);
        }
    }
}

This is ListPage.xaml

<?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="MedicineAlert.Pages.ListPage"
             Title="ListPage"
             x:Name="MedicinesPage">
    <StackLayout>
        <ListView 
            ItemsSource="{Binding MedicineList, Source={x:Reference MedicinesPage}}"
        >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell
                        Text="{Binding MedicineName}"
                    />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

And this is AppShell.xaml

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MedicineAlert.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:views="clr-namespace:MedicineAlert.Pages"
>
    <TabBar>
        <Tab
            Title="Add"
            Icon="add.png"
        >
            <ShellContent
                Title="Home"
                ContentTemplate="{DataTemplate views:AddPage}"
                Route="AddPage" 
            />
        </Tab>

        <Tab 
            Title="List"
            Icon="list.png"
        >
            <ShellContent
                Title="List"
                ContentTemplate="{DataTemplate views:ListPage}"
                Route="ListPage" 
            />
        </Tab>
    </TabBar>
</Shell>

The ListView in ListPage is not rendering. What could be the problem?


Solution

  • The reason the ListView is not rendering is two-fold:

    1. The internal List<MedicineData> medicineList is a field, but it should be a property as only properties can be used in binding expressions
    2. Your ItemsSource binding is not pointing to the code-behind

    First, change the field into a property:

    public List<MedicineData> MedicineList { get; }
    

    after that, move the InitializeComponent(); call to the end of the constructor:

    public ListPage()
    {        
        // Retrieve the app's local data directory path
        string localDataDirPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        // Combine the local data directory path with your desired subdirectory
        string dbFolderPath = Path.Combine(localDataDirPath, "DB");
        string filePath = Path.Combine(dbFolderPath, "data.json");
    
        if(File.Exists(filePath))
        {
            string json = File.ReadAllText(filePath);
            MedicineList = JsonSerializer.Deserialize<List<MedicineData>>(json);
        }
    
        InitializeComponent();
    }
    

    This is required, because in your case the binding will only get evaluated once and that happens during the InitializeComponent(); call.

    Finally, update the XAML as follows using a binding Source which points to the name of the page:

    <?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="MedicineAlert.Pages.ListPage"
                 Title="ListPage"
                 x:Name="MedicinesPage">
        <StackLayout>
            <ListView 
                ItemsSource="{Binding MedicineList, Source={x:Reference MedicinesPage}}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell
                            Text="{Binding MedicineName}"
                        />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage>
    

    Binding a page to its own code behind is also an option by setting BindingContext = this;, but that's not necessary with this approach.

    Note: It's not recommended to load data inside a constructor. You may want to rethink your code design here. If you need to update the List frequently, you should replace it with an ObservableCollection and make the property itself also observable. This is a common use case for the MVVM pattern.