Xamarin Forms嵌套对象列表展示与绑定实现求助(含多值表头)
解决方案
1. 调整模型,实现属性变更通知
你的现有模型缺少INotifyPropertyChanged实现,这是Xamarin.Forms数据绑定响应UI更新的核心。必须给模型添加这个接口,同时为订单添加计算属性判断所有子项是否已装车。
修改后的ShippingItem模型
using System.ComponentModel; using System.Runtime.CompilerServices; public class ShippingItem : INotifyPropertyChanged { public int Id { get; set; } public int ShippingOrderId { get; set; } public string ItemDescription { get; set; } private bool _isLoaded; public bool IsLoaded { get => _isLoaded; set { if (_isLoaded != value) { _isLoaded = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
修改后的ShippingOrder模型
添加IsAllItemsLoaded计算属性,并监听子项状态变化:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; public class ShippingOrder : INotifyPropertyChanged { public int Id { get; set; } public string OrderRef { get; set; } public string CustName { get; set; } public DateTime DeliveryDate { get; set; } private ObservableCollection<ShippingItem> _shippingItems; public ObservableCollection<ShippingItem> ShippingItems { get => _shippingItems; set { if (_shippingItems != value) { if (_shippingItems != null) { UnsubscribeFromItems(_shippingItems); _shippingItems.CollectionChanged -= ShippingItems_CollectionChanged; } _shippingItems = value; if (_shippingItems != null) { SubscribeToItems(_shippingItems); _shippingItems.CollectionChanged += ShippingItems_CollectionChanged; } OnPropertyChanged(); UpdateIsAllItemsLoaded(); } } } private bool _isAllItemsLoaded; public bool IsAllItemsLoaded { get => _isAllItemsLoaded; set { if (_isAllItemsLoaded != value) { _isAllItemsLoaded = value; OnPropertyChanged(); } } } private void ShippingItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) SubscribeToItems(e.NewItems.Cast<ShippingItem>()); if (e.OldItems != null) UnsubscribeFromItems(e.OldItems.Cast<ShippingItem>()); UpdateIsAllItemsLoaded(); } private void SubscribeToItems(IEnumerable<ShippingItem> items) { foreach (var item in items) item.PropertyChanged += Item_PropertyChanged; } private void UnsubscribeFromItems(IEnumerable<ShippingItem> items) { foreach (var item in items) item.PropertyChanged -= Item_PropertyChanged; } private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ShippingItem.IsLoaded)) UpdateIsAllItemsLoaded(); } private void UpdateIsAllItemsLoaded() { IsAllItemsLoaded = ShippingItems?.All(item => item.IsLoaded) ?? false; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
Shipment模型可保持原有结构,初始化时使用修改后的模型类即可。
2. 视图层级展示(无需复杂分组)
你的模型本身就是ShippingOrder包含ShippingItems的层级结构,无需额外分组操作,直接用嵌套CollectionView实现UI原型效果:
<CollectionView ItemsSource="{Binding Shipment.ShippingOrders}"> <CollectionView.ItemTemplate> <DataTemplate> <Frame Margin="5" Padding="0" CornerRadius="4"> <StackLayout> <!-- 订单头部 --> <StackLayout Orientation="Horizontal" Padding="10" BackgroundColor="{Binding IsAllItemsLoaded, Converter={StaticResource BoolToColorConverter}, ConverterParameter='Order'}"> <Label Text="{Binding OrderRef}" FontAttributes="Bold" FontSize="Medium" /> <Label Text="{Binding CustName}" Margin="10,0,0,0" FontSize="Medium" /> <Label Text="{Binding DeliveryDate, StringFormat='{0:dd/MM/yyyy}'}" Margin="10,0,0,0" FontSize="Medium" /> </StackLayout> <!-- 订单下的物品列表 --> <CollectionView ItemsSource="{Binding ShippingItems}" Margin="10,5,10,10"> <CollectionView.ItemTemplate> <DataTemplate> <Frame Margin="0,2" Padding="8" CornerRadius="3" BackgroundColor="{Binding IsLoaded, Converter={StaticResource BoolToColorConverter}, ConverterParameter='Item'}"> <Label Text="{Binding ItemDescription}" /> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
3. 实现背景色转换Converter
创建BoolToColorConverter,根据状态返回对应背景色:
using System; using System.Globalization; using Xamarin.Forms; public class BoolToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool isCompleted) { if (parameter?.ToString() == "Order") return isCompleted ? Color.LightGreen : Color.White; else if (parameter?.ToString() == "Item") return isCompleted ? Color.LightBlue : Color.White; } return Color.White; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在XAML资源中注册Converter:
<ContentPage.Resources> <local:BoolToColorConverter x:Key="BoolToColorConverter" /> </ContentPage.Resources>
4. 扫码逻辑处理
扫码后定位目标ShippingItem并更新状态即可:
public void OnScanCompleted(int scannedItemId) { var targetItem = Shipment.ShippingOrders .SelectMany(order => order.ShippingItems) .FirstOrDefault(item => item.Id == scannedItemId); if (targetItem != null) targetItem.IsLoaded = true; }
关键说明
- 你的模型结构完全适配层级展示,无需强行使用ListView分组(分组多用于平级数据按字段归类)
- 通过监听集合和属性变化,确保订单
IsAllItemsLoaded实时更新,自动触发UI背景色变更 - 所有绑定均为响应式,无需手动刷新UI
内容的提问来源于stack exchange,提问作者allyouneedisdave
相关产品推荐
相关产品推荐

