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

C# UWP 如何获取N级可变深度嵌套ListView中元素的各级父集合索引

解决方案

1. 统一数据模型约定

首先给嵌套条目类增加父子关联属性,后续所有索引查找逻辑都基于该模型实现:

// 标记条目所属的父集合类型
public enum ParentCollectionType
{
    Root,
    Children,
    Friends
}

public class NestItem : INotifyPropertyChanged
{
    // 你提前分配的唯一ID
    public Guid Id { get; set; }
    // 父级条目ID,根节点设为null
    public Guid? ParentId { get; set; }
    // 当前条目属于父级的哪个集合
    public ParentCollectionType ParentCollection { get; set; }
    // 业务所需的子集合
    public ObservableCollection<NestItem> Children { get; set; } = new();
    public ObservableCollection<NestItem> Friends { get; set; } = new();
    // 其他自定义业务属性
}

ListView的ItemSource对应根集合:ObservableCollection<NestItem> RootItems { get; set; }

2. 拖拽事件中获取基础信息

WPF/MAUI/WinUI等主流框架的拖拽事件都可以拿到放置目标的绑定上下文,以及插入位置信息:

private void OnDrop(object sender, DragEventArgs e)
{
    if (!e.Data.GetDataPresent(typeof(NestItem))) return;
    
    var draggedItem = e.Data.GetData(typeof(NestItem)) as NestItem;
    var targetUiElement = sender as FrameworkElement;
    // 拿到要放入的父级条目实例
    var targetParentItem = targetUiElement.DataContext as NestItem;
    // 标记要放入父级的Children还是Friends集合,可通过UI元素的Tag/附加属性区分
    var targetCollection = targetUiElement.Tag.ToString() == "Children" 
        ? ParentCollectionType.Children 
        : ParentCollectionType.Friends;
    // 获取插入位置索引,ListView可直接用ItemContainerGenerator.IndexFromContainer拿到
    int insertIndex = GetInsertIndex(e.GetPosition(targetUiElement), targetUiElement);
    
    // 生成完整索引路径
    var indexPath = GetFullIndexPath(targetParentItem, targetCollection, RootItems);
    // 按路径插入元素
    InsertItemByPath(RootItems, indexPath, draggedItem, insertIndex);
}

3. 生成全层级索引路径

用迭代方式从目标父级往上遍历,自动收集所有上层索引,无需提前知道嵌套深度:

// 索引路径节点,记录每一层的所属集合和对应索引
public class IndexPathNode
{
    public ParentCollectionType CollectionType { get; set; }
    public int Index { get; set; }
}

/// <summary>
/// 生成从根节点到目标集合的完整索引路径
/// </summary>
public List<IndexPathNode> GetFullIndexPath(NestItem targetParentItem, ParentCollectionType targetCollection, ObservableCollection<NestItem> rootItems)
{
    var path = new List<IndexPathNode>();
    // 先添加目标集合节点
    path.Add(new IndexPathNode { CollectionType = targetCollection });
    
    var currentItem = targetParentItem;
    while (currentItem != null)
    {
        int currentIndex = -1;
        if (currentItem.ParentId == null)
        {
            // 已遍历到根层级
            currentIndex = rootItems.IndexOf(currentItem);
            path.Insert(0, new IndexPathNode { CollectionType = ParentCollectionType.Root, Index = currentIndex });
            break;
        }
        // 查找父级条目(数据量大的话建议全局维护Dictionary<Guid, NestItem>缓存,无需递归遍历)
        var parentItem = FindItemById(rootItems, currentItem.ParentId.Value);
        // 查找当前条目在父集合中的索引
        currentIndex = currentItem.ParentCollection switch
        {
            ParentCollectionType.Children => parentItem.Children.IndexOf(currentItem),
            ParentCollectionType.Friends => parentItem.Friends.IndexOf(currentItem),
            _ => -1
        };
        // 插入到路径头部
        path.Insert(0, new IndexPathNode { CollectionType = currentItem.ParentCollection, Index = currentIndex });
        currentItem = parentItem;
    }
    return path;
}

// 辅助方法:按ID递归查找条目
public NestItem FindItemById(ObservableCollection<NestItem> items, Guid id)
{
    foreach (var item in items)
    {
        if (item.Id == id) return item;
        var childRes = FindItemById(item.Children, id);
        if (childRes != null) return childRes;
        var friendRes = FindItemById(item.Friends, id);
        if (friendRes != null) return friendRes;
    }
    return null;
}

4. 按索引路径插入元素

遍历路径定位到目标集合,完成插入操作,同时更新拖拽元素的父级关联属性:

/// <summary>
/// 按索引路径将拖拽元素插入到正确位置
/// </summary>
public void InsertItemByPath(ObservableCollection<NestItem> rootItems, List<IndexPathNode> path, NestItem draggedItem, int insertIndex)
{
    // 先移除元素原来的位置
    RemoveItemFromOriginalPosition(rootItems, draggedItem);
    
    // 插入到根集合的特殊情况
    if (path.Count == 1 && path[0].CollectionType == ParentCollectionType.Root)
    {
        rootItems.Insert(insertIndex, draggedItem);
        draggedItem.ParentId = null;
        draggedItem.ParentCollection = ParentCollectionType.Root;
        return;
    }

    object currentCollection = rootItems;
    // 遍历到倒数第二个节点,最后一个节点为要插入的目标集合
    for (int i = 0; i < path.Count - 1; i++)
    {
        var node = path[i];
        var currentItem = ((ObservableCollection<NestItem>)currentCollection)[node.Index];
        
        if (i == path.Count - 2)
        {
            // 到达目标集合的父级,执行插入
            var targetCollection = path[i + 1].CollectionType;
            if (targetCollection == ParentCollectionType.Children)
            {
                currentItem.Children.Insert(insertIndex, draggedItem);
                draggedItem.ParentId = currentItem.Id;
                draggedItem.ParentCollection = ParentCollectionType.Children;
            }
            else
            {
                currentItem.Friends.Insert(insertIndex, draggedItem);
                draggedItem.ParentId = currentItem.Id;
                draggedItem.ParentCollection = ParentCollectionType.Friends;
            }
            break;
        }
        // 切换到下一级集合继续遍历
        currentCollection = path[i + 1].CollectionType switch
        {
            ParentCollectionType.Children => currentItem.Children,
            ParentCollectionType.Friends => currentItem.Friends,
            _ => currentCollection
        };
    }
}

// 辅助方法:移除元素的原有引用
private void RemoveItemFromOriginalPosition(ObservableCollection<NestItem> rootItems, NestItem item)
{
    if (item.ParentId == null)
    {
        rootItems.Remove(item);
        return;
    }
    var parent = FindItemById(rootItems, item.ParentId.Value);
    if (item.ParentCollection == ParentCollectionType.Children)
        parent.Children.Remove(item);
    else
        parent.Friends.Remove(item);
}

内容的提问来源于stack exchange,提问作者Qaiser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 10:48:00