.NET MAUI中ListView无法显示ViewModel的ObservableProperty绑定数据
.NET MAUI MVVM绑定ListView无数据问题
我是.NET MAUI、MVVM、SQLite及C#的新手,尝试通过ViewModel绑定模型类的数据。直接绑定模型命名空间时,ListView能正常显示数据,但切换为ViewModel的ObservableProperty绑定后,数据就不再显示。
可正常工作的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"> <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>
(原效果:显示数据的ListView)
改用ViewModel绑定后的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>
(原效果:无数据的ListView)
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代码
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代码
public partial class CRUDView : ContentPage { CRUDViewModel viewModel; private readonly LocalDbService localDbService; private int _editFlashcardId; 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(); } }
问题分析与修复方案
问题出在几个关键节点:
- DataTemplate的x:DataType错误:ListView的ItemTemplate上下文是
Flashcards集合中的单个Flashcard对象,而非CRUDViewModel,所以x:DataType应指向Flashcard模型。 - 绑定路径错误:ItemTemplate的上下文是单个
Flashcard,不需要写Flashcard.Question,直接绑定Question即可。 - 数据未流入ViewModel:当前
OnAppearing直接给控件赋值,绕过了ViewModel的Flashcards集合,应把数据库数据填充到ViewModel中。 - 未利用依赖注入:已注册ViewModel,应通过构造函数注入而非手动实例化。
修复步骤:
1. 修改CRUDView的XAML中ItemTemplate部分
<!-- 先在XAML顶部添加Flashcard模型的命名空间,比如 xmlns:model="clr-namespace:cadflash.Models" --> <ListView.ItemTemplate> <DataTemplate x:DataType="model: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>
2. 修改CRUDView.cs,注入ViewModel并更新数据
public partial class CRUDView : ContentPage { private readonly CRUDViewModel viewModel; private readonly LocalDbService localDbService; private int _editFlashcardId; public CRUDView(CRUDViewModel viewModel, LocalDbService localDbService) { InitializeComponent(); this.viewModel = viewModel; BindingContext = viewModel; this.localDbService = localDbService; } protected override async void OnAppearing() { base.OnAppearing(); var flashcards = await localDbService.GetFlashcardsAsync(); viewModel.Flashcards.Clear(); foreach (var card in flashcards) { viewModel.Flashcards.Add(card); } } }
3. 优化ViewModel构造函数(可选)
public partial class CRUDViewModel : ObservableObject { [ObservableProperty] public ObservableCollection<Flashcard> flashcards = new(); [ObservableProperty] public Flashcard flashcard = new(); public CRUDViewModel() { // ObservableProperty已自动初始化,无需重复赋值 } }
修改完成后,ListView即可通过ViewModel正常绑定并显示数据。
内容的提问来源于stack exchange,提问作者Brett Simonds
相关产品推荐
相关产品推荐

