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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:15:41