You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
    {
        
    }
}

问题分析与解决方案

核心问题点

  1. GetDataDB未实现属性通知:数据更新时UI无法感知到变化,导致ListView不刷新。
  2. 数据源实例不共享:在ListView_ItemSelected中新建的GetDataDB实例和DataDisplayDetail的BindingContext不是同一个对象,数据根本没传到ListView绑定的实例上。
  3. 错误使用静态列表:代码中直接调用GetDataDB.GetDataFromDB.Add,操作的是静态属性,但ListView绑定的是实例属性,两者无关。
  4. 异步调用阻塞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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 13:08:09