ToolStripItemCollection.AddRange方法中的缺陷?

时间:2014-05-29 18:50:27

标签: c# .net winforms

我坚信在实施ToolStripItemCollection.AddRange时是错误的:

我使用两个菜单制作了Windows窗体应用程序,每个菜单包含两个项目。

first menu

second menu

(对不起,但我没有足够的声誉来发布图片)。

接下来,我实现按钮点击处理程序:

private void button1_Click(object sender, EventArgs e)
{
    menu1.DropDownItems.AddRange(menu2.DropDownItems);
}
抛出了

和System.ArgumentOutOfRangeException。

我非常好奇为什么会这样,我用ILSpy反编译了ToolStripItemCollection程序集。这就是我所看到的:

public void AddRange(ToolStripItemCollection toolStripItems)
{
    if (toolStripItems == null)
    {
        throw new ArgumentNullException("toolStripItems");
    }
    if (this.IsReadOnly)
    {
        throw new NotSupportedException(SR.GetString("ToolStripItemCollectionIsReadOnly"));
    }
    using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items))
    {
        int count = toolStripItems.Count;
        for (int i = 0; i < count; i++)
        {
            this.Add(toolStripItems[i]);
        }
    }
}

没有什么可以打扰的。我们来看看ToolStripItemCollection.Add方法:

public int Add(ToolStripItem value)
{
    this.CheckCanAddOrInsertItem(value);
    this.SetOwner(value);
    int result = base.InnerList.Add(value);
    if (this.itemsCollection && this.owner != null)
    {
        this.owner.OnItemAdded(new ToolStripItemEventArgs(value));
    }
    return result;
}

并最终进入ToolStripItemCollection.SetOwner:

private void SetOwner(ToolStripItem item)
{
    if (this.itemsCollection && item != null)
    {
        if (item.Owner != null)
        {
            item.Owner.Items.Remove(item);
        }
        item.SetOwner(this.owner);
        if (item.Renderer != null)
        {
            item.Renderer.InitializeItem(item);
        }
    }
}

我们可以清楚地看到,在每一步中, toolStripItems 中的循环删除项目。 MSDN对ToolStripItemCollection实现的IList接口发表了评论: 在连续元素(如列表)的集合中,删除元素后面的元素向上移动以占据空出的位置。如果集合已编制索引,则还会更新已移动元素的索引。。 结果,我们最终在错误的索引上访问toolStripItems中的项目(第二个项目转移到位置0)。我是对的吗?

1 个答案:

答案 0 :(得分:1)

不是100%确定问题是什么,但您可以通过将集合转换为数组(需要强制转换)来解决此问题:

menu1.DropDownItems.AddRange(menu2.DropDownItems.Cast<ToolStripItem>().ToArray());

你的版本引发错误是因为当第一个菜单被添加到DropDown集合时,它会从迭代的集合中删除,因此OutOfRangeException。