ObservableCollection双向绑定问题:第二页ListView显示空白
问题描述
我的项目包含3个页面、2个ViewModel和2个模型。第一页可正常将未结订单填充至ListView,但点击第一页ListView项触发Orders_ItemTapped事件后,本该将对应订单条目填充到第二页的ListView中,却出现ListView空白的情况。
调试过程中无报错,点击事件能调用所有方法但无效。经初步排查,问题可能源于双向数据绑定、ViewModel的使用方式或点击事件中ViewModel的实例化逻辑。
为进一步测试,我在第二个ViewModel的构造函数中硬编码调用GetOrderItemsById()来填充列表,此时第二页可正常显示所有订单条目。我不熟悉依赖注入,推测问题与ViewModel中ObservableCollection的属性/构造函数使用或点击事件中ViewModel的实例化有关。
相关代码
页面1 "OpenOrdersPage" 代码后台
using ApexHQ_Mobile.Models; using ApexHQ_Mobile.ViewModels; namespace ApexHQ_Mobile.Pages; public partial class OpenOrdersPage : ContentPage { private OpenWOrdersVM vm; public OpenOrdersPage() { InitializeComponent(); vm = new OpenWOrdersVM(); this.BindingContext = this.vm; } private void Orders_ItemTapped(object sender, ItemTappedEventArgs e) { try { var order = (WarehouseOrder)e.Item; var vm = new WOrderItemsVM { OrderId = order.Id }; vm.GetOrderItems(); var itemsPage = new WOrderItemsPage(); itemsPage.BindingContext = vm; Shell.Current.GoToAsync(nameof(itemsPage)); } catch (Exception ex) { DisplayAlert("Error", ex.Message, "OK"); throw; } } }
页面2 "WOrderItemsPage" 代码后台
using ApexHQ_Mobile.ViewModels; namespace ApexHQ_Mobile.Pages; public partial class WOrderItemsPage : ContentPage { private WOrderItemsVM vm; public WOrderItemsPage() { InitializeComponent(); vm = new WOrderItemsVM(); this.BindingContext = this.vm; } private void Order_ItemTapped(object sender, ItemTappedEventArgs e) { // Future use to populate OrderItemDetail Page } }
ViewModel 1 "OpenWOrdersVM"
using ApexHQ_Mobile.Models; using ApexHQ_Mobile.Services.Database; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using System.Collections.ObjectModel; namespace ApexHQ_Mobile.ViewModels; internal partial class OpenWOrdersVM : ObservableObject { public WarehouseOrder order { get; set; } [ObservableProperty] private ObservableCollection<WarehouseOrder> orders = new(); //Initialize View Model in the constructor public OpenWOrdersVM() { orders.Clear(); orders = new ObservableCollection<WarehouseOrder>(WarehoueOrderService.GetWarehoueOrders()); } [RelayCommand] public void GoBack() { Shell.Current.GoToAsync(".."); } [RelayCommand] public void ShowOrderList() { Shell.Current.GoToAsync(".."); } }
ViewModel 2 "WOrderItemsVM"
using ApexHQ_Mobile.Models; using ApexHQ_Mobile.Services.Database; using CommunityToolkit.Mvvm.ComponentModel; using System.Collections.ObjectModel; namespace ApexHQ_Mobile.ViewModels { public partial class WOrderItemsVM: ObservableObject { private WOItem item { get; set; } private ObservableCollection<WOItem> items = new(); private long orderId; public long OrderId { get => orderId; set => SetProperty(ref orderId, value); } public ObservableCollection<WOItem> Items { get => items; set => SetProperty(ref items, value); } //public ObservableCollection<WOItem> Items { get { return items; } } public void GetOrderItems() { try { Items.Clear(); var items = new ObservableCollection<WOItem>(WarehoueOrderService.GetOrderItemsById(orderId)); if (items != null) { foreach (var item in items) { Items.Add(item); } } } catch (Exception ex) { throw; } } //public WOrderItemsVM() //{ // GetOrderItems(); //} } }
页面路由 AppShell.xaml.cs
using ApexHQ_Mobile.Pages; namespace ApexHQ_Mobile; public partial class AppShell : Shell { public AppShell() { InitializeComponent(); //Page Route Routing.RegisterRoute(nameof(MainPage), typeof(MainPage)); Routing.RegisterRoute(nameof(OpenOrdersPage), typeof(OpenOrdersPage)); Routing.RegisterRoute(nameof("itemsPage"), typeof(WOrderItemsPage)); } }
问题根源与修复方案
核心问题
1. Shell导航的页面实例覆盖
你在Orders_ItemTapped中手动创建WOrderItemsPage并设置ViewModel,但调用Shell.Current.GoToAsync时,Shell会重新实例化WOrderItemsPage,导致你之前设置的ViewModel被页面构造函数里新创建的空ViewModel覆盖,最终ListView无数据。
2. 路由注册语法错误
AppShell.xaml.cs中使用nameof("itemsPage")是错误写法,nameof用于获取类型/变量的名称字符串,此处应该直接使用页面类型的nameof(WOrderItemsPage)或字符串"WOrderItemsPage"。
修复步骤
步骤1:修正Shell路由注册
// 替换原错误路由 Routing.RegisterRoute(nameof(WOrderItemsPage), typeof(WOrderItemsPage));
步骤2:修改导航逻辑,传递参数而非手动创建页面
private async void Orders_ItemTapped(object sender, ItemTappedEventArgs e) { try { var order = (WarehouseOrder)e.Item; // 通过导航参数传递OrderId await Shell.Current.GoToAsync($"{nameof(WOrderItemsPage)}?OrderId={order.Id}"); } catch (Exception ex) { await DisplayAlert("Error", ex.Message, "OK"); throw; } }
步骤3:修改WOrderItemsPage接收参数并初始化ViewModel
using ApexHQ_Mobile.ViewModels; namespace ApexHQ_Mobile.Pages; public partial class WOrderItemsPage : ContentPage { private WOrderItemsVM vm; // 添加构造参数接收OrderId public WOrderItemsPage(long orderId) { InitializeComponent(); vm = new WOrderItemsVM { OrderId = orderId }; vm.GetOrderItems(); this.BindingContext = vm; } }
可选优化:简化WOrderItemsVM的集合更新逻辑
public void GetOrderItems() { try { var fetchedItems = WarehoueOrderService.GetOrderItemsById(orderId); Items = fetchedItems != null ? new ObservableCollection<WOItem>(fetchedItems) : new ObservableCollection<WOItem>(); } catch (Exception ex) { throw; } }
内容的提问来源于stack exchange,提问作者Mike Yim
相关产品推荐
相关产品推荐

