如何消除C# MAUI ListView的BindingDiagnostics属性未找到警告?
.NET MAUI ListView绑定警告问题排查
问题描述
我是.NET MAUI新手,项目在Windows和Android上能正常运行,可正确显示SQLite数据库中的选民列表,但填充ListView时调试日志会出现绑定警告,希望解决这些问题。
代码与警告信息
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" x:Class="SOTest.MainPage"> <ScrollView> <VerticalStackLayout> <Button Text="Search" Clicked="OnButtonClicked" /> <ListView x:Name="MyListView" ItemsSource="{Binding DataToDisplay}"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <VerticalStackLayout Padding="5"> <Label Text="{Binding first_name}" /> <Label Text="{Binding last_name}" /> </VerticalStackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </VerticalStackLayout> </ScrollView> </ContentPage>
MainPage.xaml.cs
using SQLite; using System.Diagnostics; using System.Collections.ObjectModel; namespace SOTest; public partial class MainPage : ContentPage { //--- Data Model public class MyDataModel { [PrimaryKey, AutoIncrement] public int voter_id { get; set; } public string first_name { get; set; } public string last_name { get; set; } public string street_name { get; set; } } //--- Database Constants public static class DatabaseConstants { public const string DatabaseFilename = "TestDatabase.db3"; public const SQLite.SQLiteOpenFlags Flags = // open the database in read/write mode SQLite.SQLiteOpenFlags.ReadWrite | // create the database if it doesn't exist SQLite.SQLiteOpenFlags.Create | // enable multi-threaded database access SQLite.SQLiteOpenFlags.SharedCache; public static string DatabasePath => Path.Combine(FileSystem.Current.AppDataDirectory, DatabaseFilename); } //--- Database Operations SQLiteAsyncConnection Database; public async Task SetUpDb() { if (Database is not null) return; Database = new SQLiteAsyncConnection(DatabaseConstants.DatabasePath, DatabaseConstants.Flags); await Database.CreateTableAsync<MyDataModel>(); } public async Task AddRecord(MyDataModel myRecord) { await SetUpDb(); await Database.InsertAsync(myRecord); Debug.WriteLine($"Inserted {myRecord.first_name} {myRecord.last_name}"); } public async Task<List<MyDataModel>> GetSomeRecords(string LastNameFilter) { await SetUpDb(); return await Database.QueryAsync<MyDataModel>("SELECT * FROM [MyDataModel] WHERE [last_name] = '" + LastNameFilter + "' COLLATE NOCASE"); } public async Task<List<MyDataModel>> GetAllRecords() { await SetUpDb(); return await Database.Table<MyDataModel>().ToListAsync(); } //--- Observable Collection public ObservableCollection<MyDataModel> DataToDisplay { get; set; } //--- Main Page and Event Handlers public MainPage() { InitializeComponent(); DataToDisplay = new ObservableCollection<MyDataModel>() { }; BindingContext = this; } protected override async void OnAppearing() { base.OnAppearing(); await SetUpDb(); MyDataModel Record1 = new MyDataModel(); Record1.first_name = "Albert"; Record1.last_name = "Albertson"; await AddRecord(Record1); MyDataModel Record2 = new MyDataModel(); Record2.first_name = "Betty"; Record2.last_name = "Boop"; await AddRecord(Record2); MyDataModel Record3 = new MyDataModel(); Record3.first_name = "Chris"; Record3.last_name = "Kringle"; await AddRecord(Record3); } //--- Button Click Initiates the Query and ListView Population private async void OnButtonClicked(object sender, EventArgs e) { DataToDisplay.Clear(); var MyList = await GetAllRecords(); if (MyList.Count > 0) { var i = 0; foreach (var item in MyList) { Debug.WriteLine($"Adding {item.first_name}"); DataToDisplay.Add(item); i++; } } else { Debug.WriteLine("No Records"); } } }
运行效果
点击Windows上的“Search”按钮后效果如下:
警告信息
填充ListView时收到以下警告:
Adding Albert Adding Betty Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'first_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text' Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'last_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text' Adding Chris Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'first_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text' Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'last_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text' Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'first_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text' Microsoft.Maui.Controls.Xaml.Diagnostics.BindingDiagnostics: Warning: 'last_name' property not found on 'SOTest.MainPage', target property: 'Microsoft.Maui.Controls.Label.Text'
解决方案
问题根源
警告的核心原因是:DataTemplate初始化时,默认会继承外部的BindingContext(也就是MainPage实例),此时ListView还没有加载数据,绑定引擎会尝试在MainPage上查找first_name和last_name属性,自然找不到,从而抛出警告。
解决步骤
有两种简单的方式可以消除这些警告:
方式1:给DataTemplate指定x:DataType
通过明确指定DataTemplate的数据类型,让绑定引擎提前知道要绑定的是MyDataModel对象,而不是外部的MainPage。
修改MainPage.xaml中的DataTemplate部分:
<ListView.ItemTemplate> <DataTemplate x:DataType="local:MyDataModel"> <ViewCell> <VerticalStackLayout Padding="5"> <Label Text="{Binding first_name}" /> <Label Text="{Binding last_name}" /> </VerticalStackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate>
同时需要在ContentPage的命名空间中添加local前缀的引用:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:SOTest" x:Class="SOTest.MainPage">
方式2:在DataTemplate内部显式设置BindingContext
另一种方式是在ViewCell内部手动设置BindingContext为当前列表项:
<ListView.ItemTemplate> <DataTemplate> <ViewCell BindingContext="{Binding .}"> <VerticalStackLayout Padding="5"> <Label Text="{Binding first_name}" /> <Label Text="{Binding last_name}" /> </VerticalStackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate>
这种方式通过{Binding .}明确告诉绑定引擎,当前ViewCell的上下文是列表中的单个MyDataModel对象。
额外优化建议
- 建议将
MyDataModel类从MainPage内部移到单独的文件中,符合代码组织规范,也让x:DataType的引用更清晰。 - 数据库查询中的
GetSomeRecords方法存在SQL注入风险,建议改用参数化查询:
public async Task<List<MyDataModel>> GetSomeRecords(string LastNameFilter) { await SetUpDb(); return await Database.QueryAsync<MyDataModel>("SELECT * FROM [MyDataModel] WHERE [last_name] = ? COLLATE NOCASE", LastNameFilter); }
内容的提问来源于stack exchange,提问作者SqueezeOJ
相关产品推荐
相关产品推荐

