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

如何设置ItemsControl的ItemsSource以显示对象集合及其子对象集合

让ItemsControl依次渲染Job、Shipment、Fee控件的实现方案

嘿,我明白你的需求了——你想让ItemsControl不仅渲染Jobs集合里的Job对象,还要把每个Job下面的Shipments和Fees子集合里的元素也依次渲染出来,而且已经准备好了对应DataType的DataTemplate。那我给你两种实用的方案,你可以根据自己的布局需求选:

方案一:扁平化数据源(同层级连续渲染)

这种方法是把Job、它的Shipments、Fees全部合并到一个统一的集合里,让ItemsControl直接遍历这个扁平集合,这样所有元素会按顺序连续显示,没有层级区分。

第一步:在ViewModel里创建合并后的集合

你需要在ViewModel中新增一个集合,专门用来存放所有需要渲染的对象(Job + 对应的Shipment + Fee),还要处理集合的动态更新:

using System.Collections.ObjectModel;
using System.Collections.Specialized;

// 假设你的ViewModel类里已有Invoice属性
private ObservableCollection<object> _allJobRelatedItems;
public ObservableCollection<object> AllJobRelatedItems
{
    get
    {
        if (_allJobRelatedItems == null)
        {
            _allJobRelatedItems = new ObservableCollection<object>();
            // 初始化时加载数据
            UpdateAllJobRelatedItems();
            // 监听Jobs集合的变化,自动刷新
            Invoice.Jobs.CollectionChanged += Jobs_CollectionChanged;
        }
        return _allJobRelatedItems;
    }
}

private void UpdateAllJobRelatedItems()
{
    _allJobRelatedItems.Clear();
    if (Invoice?.Jobs == null) return;
    
    foreach (var job in Invoice.Jobs)
    {
        // 先添加当前Job
        _allJobRelatedItems.Add(job);
        
        // 添加该Job下的所有Shipment
        if (job.Shipments != null)
        {
            foreach (var shipment in job.Shipments)
                _allJobRelatedItems.Add(shipment);
            // 监听Shipments的变化,实时更新
            job.Shipments.CollectionChanged += (s, e) => UpdateAllJobRelatedItems();
        }
        
        // 添加该Job下的所有Fee
        if (job.Fees != null)
        {
            foreach (var fee in job.Fees)
                _allJobRelatedItems.Add(fee);
            // 监听Fees的变化,实时更新
            job.Fees.CollectionChanged += (s, e) => UpdateAllJobRelatedItems();
        }
    }
}

private void Jobs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    UpdateAllJobRelatedItems();
}

第二步:修改XAML绑定

把ItemsControl的ItemsSource绑定到这个新的扁平集合,剩下的DataTemplate会自动根据DataType匹配对应的CustomControl:

<ItemsControl ItemsSource="{Binding AllJobRelatedItems}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:Job}">
            <local:JobCustomControl />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Shipment}">
            <local:ShipmentCustomControl />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Fee}">
            <local:FeeCustomControl />
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

方案二:嵌套ItemsControl(保持层级结构)

如果你希望每个Job控件下面紧跟着它自己的Shipments和Fees,保持清晰的层级关系,那就用嵌套ItemsControl的方式:

<ItemsControl ItemsSource="{Binding Invoice.Jobs}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:Job}">
            <StackPanel Margin="0 0 0 10" <!-- 给Job和子项之间加个间距 --> >
                <!-- 先渲染当前Job的控件 -->
                <local:JobCustomControl DataContext="{Binding}" />
                
                <!-- 渲染该Job下的所有Shipment -->
                <ItemsControl ItemsSource="{Binding Shipments}" Margin="15 0 0 0"> <!-- 缩进子项,区分层级 -->
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type local:Shipment}">
                            <local:ShipmentCustomControl />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                
                <!-- 渲染该Job下的所有Fee -->
                <ItemsControl ItemsSource="{Binding Fees}" Margin="15 0 0 0">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type local:Fee}">
                            <local:FeeCustomControl />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
        
        <!-- 全局的DataTemplate也可以保留,方便其他地方复用 -->
        <DataTemplate DataType="{x:Type local:Shipment}">
            <local:ShipmentCustomControl />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Fee}">
            <local:FeeCustomControl />
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

两种方案的小提示

  • 方案一适合需要所有元素在视觉上是同一条连续列表的场景,比如所有Job、Shipment、Fee不分层级依次排列。
  • 方案二适合需要明确区分Job和其子项层级的场景,比如每个Job作为一个“组”,下面跟着自己的Shipments和Fees。
  • 如果你的集合是动态变化的(比如新增/删除Job、Shipment、Fee),方案一要记得监听所有子集合的CollectionChanged事件来刷新扁平集合;方案二则因为直接绑定子集合,WPF会自动处理动态更新。

内容的提问来源于stack exchange,提问作者Cody W Hageman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:50:58