Xamarin中FlyoutMenu点击后ListView无法显示数据库数据求助
Xamarin密码管理APP:ListView数据不显示问题解决及页面结构疑问
问题描述
- 使用C# Xamarin开发个人密码管理移动端应用,用于存储社交网络、邮箱、游戏、网购等各类账号密码。
- 当前遇到数据库数据无法显示在ListView的问题,ListView始终为空;调试时点击FlyoutPage菜单项能进入对应的
ListView_ItemSelected方法,但数据仍无法正常显示。 - 疑问:是否可以在
FlyoutPageDetail中完成所有操作,还是需要新建页面来处理相关逻辑?
相关代码
DataDisplay C#
public partial class DataDisplay : FlyoutPage { public DataDisplay() { InitializeComponent(); FlyoutPage.ListView.ItemSelected += ListView_ItemSelected; } private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e) { var selectedCategory = e.SelectedItem as DataDisplayFlyoutMenuItem; if (selectedCategory == null) { return; } DataBase dbConnexion = new DataBase(); switch (selectedCategory.Title) { case "Administratif": var getDataAdmin = dbConnexion.GetAdministratifData(); var result = getDataAdmin.Result; GetDataDB getDataDB = new GetDataDB(); getDataDB.GetDataFromDB = new List<GetDataDB>(); foreach (var data in result) { GetDataDB.GetDataFromDB.Add(new GetDataDB { Titre = data.Titre, Login = data.Login, Password = data.Password, Url = data.Url }); } break; case "Ecommerce": break; case "Jeux": break; case "Mail": break; case "Reseauxsociaux": break; } IsPresented = false; FlyoutPage.ListView.SelectedItem = null; } }
DataDisplay XAML
<?xml version="1.0" encoding="utf-8" ?> <FlyoutPage xmlns="schemas.microsoft.com/dotnet/2021/maui" xmlns:x="schemas.microsoft.com/winfx/2009/xaml" x:Class="MemoDePasse.DataDisplay" xmlns:pages="clr-namespace:MemoDePasse"> <FlyoutPage.Flyout> <pages:DataDisplayFlyout x:Name="FlyoutPage" /> </FlyoutPage.Flyout> <FlyoutPage.Detail> <NavigationPage> <x:Arguments> <pages:DataDisplayDetail /> </x:Arguments> </NavigationPage> </FlyoutPage.Detail> </FlyoutPage>
DataDisplayDetail C#
public partial class DataDisplayDetail : ContentPage { public DataDisplayDetail() { InitializeComponent(); BindingContext = new GetDataDB(); } public void BtnClickGoAddingDataPage(object sender, EventArgs e) { Navigation.PushModalAsync(new AddingDataPage()); } }
DataDisplayDetail XAML
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="schemas.microsoft.com/dotnet/2021/maui" xmlns:x="schemas.microsoft.com/winfx/2009/xaml" x:Class="MemoDePasse.DataDisplayDetail" xmlns:local="clr-namespace:MemoDePasse" x:DataType="local:GetDataDB" Title="Catégories"> <StackLayout> <ListView x:Name="ListViewData" SeparatorVisibility="Default" SeparatorColor="Black" ItemsSource="{Binding GetDataFromDB}"> <ListView.Header> <Grid BackgroundColor="LightGray" IsEnabled="True" IsVisible="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Label Grid.Column="0" Text="Titre" FontAttributes="Bold" FontSize="Default" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> <Label Grid.Column="1" Text="Login" FontAttributes="Bold" FontSize="Default" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> <Label Grid.Column="2" Text="Mot de passe" FontAttributes="Bold" FontSize="Default" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> <Label Grid.Column="3" Text="URL" FontAttributes="Bold" FontSize="Default" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> </Grid> </ListView.Header> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Label Grid.Column="0" Text="{Binding Titre}"/> <Label Grid.Column="1" Text="{Binding Login}"/> <Label Grid.Column="2" Text="{Binding Password}"/> <Label Grid.Column="3" Text="{Binding Url}"/> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <Grid VerticalOptions="End"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Button x:Name="Add" Grid.Column="0" Text="Ajouter" BackgroundColor="Green" Clicked="BtnClickGoAddingDataPage"/> <Button x:Name="Update" Grid.Column="1" Text="Modifier" BackgroundColor="BlueViolet"/> <Button x:Name="Delete" Grid.Column="2" Text="Supprimer" BackgroundColor="Red"/> </Grid> </StackLayout> </ContentPage>
DataDisplayFlyout C#
public partial class DataDisplayFlyout : ContentPage { public ListView ListView; public DataDisplayFlyout() { InitializeComponent(); BindingContext = new FlyoutPage1FlyoutViewModel(); ListView = MenuItemsListView; } class FlyoutPage1FlyoutViewModel : INotifyPropertyChanged { public ObservableCollection<DataDisplayFlyoutMenuItem> MenuItems { get; set; } public FlyoutPage1FlyoutViewModel() { MenuItems = new ObservableCollection<DataDisplayFlyoutMenuItem>(new[] { new DataDisplayFlyoutMenuItem { Id = 0, Title = "Administratif" }, new DataDisplayFlyoutMenuItem { Id = 1, Title = "Ecommerce" }, new DataDisplayFlyoutMenuItem { Id = 2, Title = "Jeux" }, new DataDisplayFlyoutMenuItem { Id = 3, Title = "Mail" }, new DataDisplayFlyoutMenuItem { Id = 4, Title = "Reseauxsociaux" }, }); } #region INotifyPropertyChanged Implementation public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged([CallerMemberName] string propertyName = "") { if (PropertyChanged == null) return; PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion } }
DataDisplayFlyout XAML
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="schemas.microsoft.com/dotnet/2021/maui" xmlns:x="schemas.microsoft.com/winfx/2009/xaml" x:Class="MemoDePasse.DataDisplayFlyout" Title="Flyout"> <StackLayout> <ListView x:Name="MenuItemsListView" SeparatorVisibility="None" HasUnevenRows="true" ItemsSource="{Binding MenuItems}"> <ListView.Header> <Grid BackgroundColor="#03A9F4"> <Grid.ColumnDefinitions> <ColumnDefinition Width="10"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="10"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="80"/> <RowDefinition Height="Auto"/> <RowDefinition Height="10"/> </Grid.RowDefinitions> <Label Grid.Column="1" Grid.Row="1" Text="Catégories" Style="{DynamicResource SubtitleStyle}"/> </Grid> </ListView.Header> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Padding="15,10" HorizontalOptions="FillAndExpand"> <Label VerticalOptions="FillAndExpand" VerticalTextAlignment="Center" Text="{Binding Title}" FontSize="15"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>
GetDataDB C#(ListView绑定数据源)
public class GetDataDB { public string Titre { get; set; } public string Login { get; set; } public string Password { get; set; } public string Url { get; set; } public IList<GetDataDB> GetDataFromDB { get; set; } public GetDataDB() { } }
问题分析与解决方案
核心问题点
- GetDataDB未实现属性通知:数据更新时UI无法感知到变化,导致ListView不刷新。
- 数据源实例不共享:在
ListView_ItemSelected中新建的GetDataDB实例和DataDisplayDetail的BindingContext不是同一个对象,数据根本没传到ListView绑定的实例上。 - 错误使用静态列表:代码中直接调用
GetDataDB.GetDataFromDB.Add,操作的是静态属性,但ListView绑定的是实例属性,两者无关。 - 异步调用阻塞UI:用
.Result获取异步数据库结果会阻塞主线程,可能导致数据加载异常。
修复步骤
1. 重构GetDataDB类,实现INotifyPropertyChanged并改用ObservableCollection
ObservableCollection会自动通知UI数据变化,适合作为ListView的数据源:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; public class GetDataDB : INotifyPropertyChanged { public string Titre { get; set; } public string Login { get; set; } public string Password { get; set; } public string Url { get; set; } private ObservableCollection<GetDataDB> _getDataFromDB; public ObservableCollection<GetDataDB> GetDataFromDB { get => _getDataFromDB; set { _getDataFromDB = value; OnPropertyChanged(); } } public GetDataDB() { GetDataFromDB = new ObservableCollection<GetDataDB>(); } public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 修改DataDisplay的ItemSelected方法,共享ViewModel实例并正确异步加载数据
public partial class DataDisplay : FlyoutPage { private GetDataDB _sharedViewModel; public DataDisplay() { InitializeComponent(); FlyoutPage.ListView.ItemSelected += ListView_ItemSelected; // 获取Detail页面的ViewModel实例,后续操作都用这个实例 var navigationPage = (NavigationPage)Detail; var detailPage = (DataDisplayDetail)navigationPage.RootPage; _sharedViewModel = (GetDataDB)detailPage.BindingContext; } private async void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e) { var selectedCategory = e.SelectedItem as DataDisplayFlyoutMenuItem; if (selectedCategory == null) return; DataBase dbConnexion = new DataBase(); _sharedViewModel.GetDataFromDB.Clear(); // 清空旧分类数据 switch (selectedCategory.Title) { case "Administratif": var adminData = await dbConnexion.GetAdministratifData(); foreach (var data in adminData) { _sharedViewModel.GetDataFromDB.Add(new GetDataDB { Titre = data.Titre, Login = data.Login, Password = data.Password, Url = data.Url }); } break; case "Ecommerce": // 补充电商分类的数据获取逻辑,示例: // var ecommerceData = await dbConnexion.GetEcommerceData(); // foreach(var data in ecommerceData) { ... } break; case "Jeux": // 补充游戏分类的数据获取逻辑 break; case "Mail": // 补充邮箱分类的数据获取逻辑 break; case "Reseauxsociaux": // 补充社交网络分类的数据获取逻辑 break; } IsPresented = false; FlyoutPage.ListView.SelectedItem = null; } }
3. 确认DataDisplayDetail的ViewModel初始化
public partial class DataDisplayDetail : ContentPage { public GetDataDB ViewModel { get; private set; } public DataDisplayDetail() { InitializeComponent(); ViewModel = new GetDataDB(); BindingContext = ViewModel; } public void BtnClickGoAddingDataPage(object sender, EventArgs e) { Navigation.PushModalAsync(new AddingDataPage()); } }
页面结构疑问解答
完全可以在FlyoutPageDetail(即DataDisplayDetail)中完成所有操作(查看、添加、修改、删除),不需要新建额外页面。这种单页管理的结构更简洁,用户操作流程更顺畅。如果后续功能复杂度提升(比如编辑表单过于复杂),再考虑拆分出独立的编辑页面即可。
内容的提问来源于stack exchange,提问作者JRMY
相关产品推荐
相关产品推荐

