Canvas.children:System.ArgumentException:“指定的索引已在使用中。请先断开指定索引处的 Visual 子级。”

3

目前我正试图为wpf中画布的一个子元素分配新值,但它抛出了上述异常。我有以下代码,我认为它是相关的:

var val = comps[0].GetComponent();
c.Children[comps[0].Index] = val;

有趣的是,当我这样做时,问题自行解决:

var val = comps[0].GetComponent();
c.Children.RemoveAt(comps[0].Index);
c.Children.Insert(comps[0].Index, val);

但我希望了解正在发生的情况,以及是否有更有效的方法来防止异常。


你使用 RemoveAt (int) 和 Insert (int, object) 的版本对于给定的问题陈述是正确的。 但是一般来说,为了解决方案,值得使用一个带有面板的 ItemsControl 用于 Canvas 项和给定的 ItemTemplate。 这可以大大简化代码并改善实现。 - EldHasp
1个回答

3
发生的情况是,System.Windows.Media.VisualCollection类会显式地检查您试图分配新值的集合中的索引是否已经被使用,如果已经被使用,则会抛出一个ArgumentException异常。请看源代码中第379行:
if (child != null)
{
    throw new System.ArgumentException(SR.Get(SRID.VisualCollection_EntryInUse));
}

因此,正如您已经做的那样,您应明确检查索引处是否存在元素并在尝试插入新项之前将其删除:

int index = 0;
if (canvas.Children.Count > index)
{
    canvas.Children.Remove(canvas.Children[index]);
}
canvas.Children.Insert(index, val);

1
@мм8,在我看来,索引加一的操作是不必要的。 想象一下如果有五个元素,你需要替换第四个元素会发生什么。 条件将不成立,插入操作将代替替换操作。 - EldHasp
而不是使用 Remove (object),最好使用 RemoveAt (int)。 - EldHasp
@EldHasp:你是对的,+1 是不必要的。我已经编辑了我的回答。不过你的第二条评论是不正确的。 - mm8
@мм8,你能解释一下你的想法吗? 我的推理如下:Remove(object)执行等效项的线性搜索。并且将删除第一个等效元素。 RemoveAt(int)不需要搜索,直接删除指定索引处的元素。 这更快。此外,如果集合中有多个等效项,则不会发生冲突。 - EldHasp
“RemoveAt” 在调用 “InternalRemove” 之前基本上会查找该项两次。虽然我没有测量哪种方法最快,但这不应该有任何影响,因为这不是性能问题。 - mm8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接