I am new to .NET MAUI/MVVM/SQLite, and C# for that matter. I am trying to utilize my ViewModel to bind data from my model class. I can use the model namespace as my binding property just fine, and it shows the data in my listview. As soon as I try to use my ViewModel, however, it is no longer displaying.
CRUDView XAML Working
<Frame Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3" BackgroundColor="#B1C0C9" BorderColor="#7989A3">
<ScrollView Orientation="Vertical">
<ListView x:Name="listView"
BackgroundColor="Gray"
HasUnevenRows="True"
SeparatorVisibility="Default"
ItemTapped="listView_ItemTapped"
FlexLayout.Grow="1"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<VerticalStackLayout Padding="5">
<Label Text="{Binding Question}"
FontSize="17"
FontFamily="OCRA"
TextColor="Black"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Id}"
FontSize="10"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="0"
/>
<Label Text="{Binding Answer}"
FontSize="15"
FontFamily="OCRA"
TextColor="White"
Grid.Column="1"
Padding="10"/>
<Label Text="{Binding Difficulty}"
FontSize="15"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="3"
Padding="10"/>
</Grid>
</VerticalStackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollView>
</Frame>
When I revert back to using the ViewModel for data binding:
CRUDView XAML
<Frame Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3" BackgroundColor="#B1C0C9" BorderColor="#7989A3">
<ScrollView Orientation="Vertical">
<ListView x:Name="listView"
BackgroundColor="Gray"
HasUnevenRows="True"
SeparatorVisibility="Default"
ItemTapped="listView_ItemTapped"
FlexLayout.Grow="1"
ItemsSource="{Binding Flashcards}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:CRUDViewModel">
<ViewCell>
<VerticalStackLayout Padding="5">
<Label Text="{Binding Flashcard.Question}"
FontSize="17"
FontFamily="OCRA"
TextColor="Black"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Flashcard.Id}"
FontSize="10"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="0"
/>
<Label Text="{Binding Flashcard.Answer}"
FontSize="15"
FontFamily="OCRA"
TextColor="White"
Grid.Column="1"
Padding="10"/>
<Label Text="{Binding Flashcard.Difficulty}"
FontSize="15"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="3"
Padding="10"/>
</Grid>
</VerticalStackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollView>
</Frame>
ViewModel
namespace cadflash.ViewModels
{
public partial class CRUDViewModel : ObservableObject
{
[ObservableProperty]
public ObservableCollection<Flashcard> flashcards = new();
[ObservableProperty]
public Flashcard flashcard = new();
public CRUDViewModel()
{
Flashcard = new Flashcard();
Flashcards = [];
}
MauiProgram
MauiProgram
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
fonts.AddFont("OCRAEXT.ttf", "OCRA");
});
builder.Services.AddSingleton<LocalDbService>();
builder.Services.AddTransient<HomeView>();
builder.Services.AddTransient<DifficultyView>();
builder.Services.AddTransient<CRUDView>();
builder.Services.AddTransient<CRUDViewModel>();
builder.Services.AddTransient<DifficultyViewModel>();
builder.Services.AddTransient<HomeViewModel>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
}
CRUDView.cs
CRUDView.xaml.cs
public partial class CRUDView : ContentPage
{
CRUDViewModel viewModel;
private readonly LocalDbService localDbService; // This is the service that is used to interact with the local database
private int _editFlashcardId; // This is the ID of the flashcard that is being edited
public CRUDView(LocalDbService localDbService)
{
InitializeComponent();
BindingContext = new CRUDViewModel();
this.localDbService = localDbService;
Task.Run(async () => await localDbService.GetFlashcardsAsync());
}
protected override async void OnAppearing()
{
base.OnAppearing();
listView.ItemsSource = await localDbService.GetFlashcardsAsync();
}
It has been a week of agonizing pain trying to get the data showing in listview. Today I figured out how to just bind the data directly from the model class. Ideally, I would like to use the ViewModel binding...
There are some problems with the usages in your code.
First, since you tried to use MVVM, you need to add the relative code about localDbService
to your MVVM model. For example, you can add both the localDbService
and loading data method to MVVM model.
//private readonly LocalDbService localDbService; // This is the service that is used to interact with the local database
public CRUDViewModel()
{
//initialize localDbService and load data
//this.localDbService = localDbService;
}
Second,since you set ItemsSource="{Binding Flashcards}"
for your ListView
, the data type of the DataTemplate
of ListView should be Flashcard
not <DataTemplate x:DataType="vm:CRUDViewModel">
.
Third, please remove the prefix Flashcard
while binding(e.g. <Label Text="{Binding Flashcard.Id}"
).
I have achieved this function with some fake data.
You can refer to the following code:
CRUDViewModel.cs
public partial class CRUDViewModel:ObservableObject
{
[ObservableProperty]
public ObservableCollection<Flashcard> flashcards = new();
[ObservableProperty]
public Flashcard flashcard = new();
//private readonly LocalDbService localDbService; // This is the service that is used to interact with the local database
public CRUDViewModel()
{
Flashcard = new Flashcard();
Flashcards = [];
//initialize localDbService and load data
//this.localDbService = localDbService;
GetData();
}
public async void GetData() {
//List<Flashcard> datas = await localDbService.GetFlashcardsAsync());
// add the data to Flashcards
//foreach (Flashcard flashcard in datas) {
// Flashcards.Add(flashcard);
//}
//add some fake data to variable Flashcards
Flashcards.Add(new Flashcard { Id = 01, Question = "1+ 1 = ", Answer = "2", Difficulty = "easy" });
Flashcards.Add(new Flashcard { Id = 02, Question = "1+ 3 = ", Answer = "4", Difficulty = "easy" });
Flashcards.Add(new Flashcard { Id = 03, Question = "123+163 = ", Answer = "286", Difficulty = "difficult" });
}
}
MainPage.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"
xmlns:models="clr-namespace:MauiMvvmListApp0407.Models"
x:Class="MauiMvvmListApp0407.MainPage">
<Frame Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3" BackgroundColor="#B1C0C9" BorderColor="#7989A3">
<ListView x:Name="listView"
BackgroundColor="Gray"
HasUnevenRows="True"
SeparatorVisibility="Default"
ItemsSource="{Binding Flashcards}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Flashcard">
<ViewCell>
<VerticalStackLayout Padding="5">
<Label Text="{Binding Question}"
FontSize="17"
FontFamily="OCRA"
TextColor="Black"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Id}"
FontSize="10"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="0"
/>
<Label Text="{Binding Answer}"
FontSize="15"
FontFamily="OCRA"
TextColor="White"
Grid.Column="1"
Padding="10"/>
<Label Text="{Binding Difficulty}"
FontSize="15"
FontFamily="OCRA"
TextColor="Black"
Grid.Column="3"
Padding="10"/>
</Grid>
</VerticalStackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
</ContentPage>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
CRUDViewModel viewModel;
public MainPage()
{
InitializeComponent();
viewModel = new CRUDViewModel();
this.BindingContext = viewModel;
}
}