Search code examples
xamlmvvmdata-bindingmauicommunity-toolkit-mvvm

Binding ListView to ObservableCollection with MVVM


I'm developing a Maui app. I use a MVVM structure and the MVVM Community toolkit for an easier life. I now ran into a problem when displaying a ListView which is bound to a ObservableCollection in My Viewmodel.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="WaiterEval.Views.UserList"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:vm="clr-namespace:WaiterEval.ViewModels"
             x:DataType="vm:UserList_ViewModel">
    
    <VerticalStackLayout>
        <ListView x:Name="userList" ItemsSource="{Binding Users}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid Padding="10">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.5*" />
                                <ColumnDefinition Width="0.25*" />
                                <ColumnDefinition Width="0.25*" />
                            </Grid.ColumnDefinitions>
                            <Label Grid.Row="0" Grid.Column="0" Text="{Binding }" />
                            <Label
                        Grid.Row="0"
                        Grid.Column="1"
                        Text="{Binding Test}" />
                            <Label
                        Grid.Row="0"
                        Grid.Column="2"
                        Text="Edited" />


                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>

        </ListView>
    </VerticalStackLayout>
</ContentPage>
namespace WaiterEval.ViewModels
{
    public partial class UserList_ViewModel : ObservableObject
    {
        [ObservableProperty]
        public ObservableCollection<User> users = new ObservableCollection<User>();

        private readonly MyDbContext _dbContext;
        public UserList_ViewModel(MyDbContext dbContext)
        {
            _dbContext = dbContext;
            addUsers();
        }

        public async void addUsers()
        {
            try
            {
                Users = new ObservableCollection<User>(await _dbContext.Users.OfType<User>().ToListAsync());
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }
}

The Collection is populated with Data after exiting the constructor.

namespace WaiterEval.Views;

public partial class UserList : ContentPage
{
    public UserList(UserList_ViewModel vm)
    {
        InitializeComponent();
        BindingContext = vm;

    }
}

When trying to Bind something in the ListCell the Datacontext is the ViewModel and not the List like it is supposed to be. I can't access any properties of User. Where am I wrong here?


Solution

  • The x:DataType="vm:UserList_ViewModel" in the <ContentPage> will make the ContentPage and the children view bind to type of UserList_ViewModel.

    You can declare the datatype for the ListView's item DataTemplate to make the item bind to type of User:

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="WaiterEval.Views.UserList"
                 xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
                 xmlns:vm="clr-namespace:WaiterEval.ViewModels"
                 xmlns:models="clr-namespace:WaiterEval.Models" 
                 x:DataType="vm:UserList_ViewModel">
        
        <VerticalStackLayout>
            <ListView x:Name="userList" ItemsSource="{Binding Users}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="models:User">
                        ....
                    </DataTemplate>
                </ListView.ItemTemplate>
    
            </ListView>
        </VerticalStackLayout>
    </ContentPage>