1

我的 TabControl 中有多个 TabItem;tabItem1, tabItem2, tabItem3...这些是

CloseableTabItem。

如果我在 tabItem1 中添加一个节点并按下按钮为该节点创建一个 subGraph 模型,则

相同的节点应该出现在 tabItem2 中并带有一个按钮;以便

tabItem2-Header = nodeName 和 nodeName = tabItem1-Header。

如果我从 tabitem2 中的节点按下按钮,tabitem1 应该被聚焦。如果我关闭

tabItem1 并按下相同的按钮 tabItem1 应该再次加载(这发生在

SubGraphButton_Click)。

你看到这段代码有问题吗?

  private void ChildNode_Click(object sender, RoutedEventArgs args)
  {
        System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
        Node node = Part.FindAncestor<Node>(button);
        MyNodeData nodeData = node.Data as MyNodeData;
        foreach (TabItem item in tabControl.Items)
        {
            if (nodeData.Text == item.Header.ToString())
            {
                item.Focus();
            }
            else if (nodeData.Text != item.Header.ToString())
            {
                SubGraphButton_Click(sender, args);
            }
        }
 }
 private void SubGraphButton_Click(object sender, RoutedEventArgs args)
 {
        string activeDirectory = @"X:\SubGraph\";
        string[] files = Directory.GetFiles(activeDirectory);
        foreach (string fileName in files)
        {
            FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
            Node node = Part.FindAncestor<Node>(button);
            MyNodeData nodeData = node.Data as MyNodeData;
            if (node != null)
            {
                if (nodeData.Text + ".epk" == fileName.Substring(12, fileName.Length - 12) && !tabControl.Items.Contains(tabItem1))
                {
                    tabControl.Items.Add(tabItem1);
                    tabItem1.Focus();
                    var model = new MyGraphLinksModel();
                    model.Modifiable = true;
                    model.HasUndoManager = true;
                    activateDiagram(myDiagram1);
                    activeDiagram.Model = model;
                    model.Name = fileName.Substring(12, fileName.Length - 12);
                    model.Name = model.Name.Substring(0, model.Name.Length - 4);
                    tabItem1.Header = model.Name;
                    activeDiagram.PartManager.UpdatesRouteDataPoints = false;
                    StreamReader reader = new StreamReader(file);
                    string contents = reader.ReadToEnd();
                    XElement root = XElement.Parse(contents);
                    activeDiagram.LayoutCompleted += LoadLinkRoutes;
                    model.Load<MyNodeData, MyLinkData>(root, "MyNodeData",   "MyLinkData");
                }
           }
  }
4

6 回答 6

11

当您在修改集合的过程中修改集合时,很可能会导致错误。错误的类型及其可能性往往会根据底层集合的实际情况而有所不同。在迭代时修改 aList很可能会给你一个错误(或者如果你修改它很多)和可能超出范围的错误。修改 aLinkedList可能会导致空指针异常、无限循环、访问不存在的项目等,但可能性要小得多。

因为在一般情况下,出现问题的可能性相当高,这些问题的影响也相当高,并且很难诊断实际出了什么问题(以及在哪里)C# 选择在尝试迭代时抛出异常在迭代期间修改的集合。这样你就不会遇到奇怪的、意想不到的问题,这些问题要等到更远的地方才会出现,然后再出现根本原因。

有几种不同的策略可用于避免此问题:

  1. 迭代与您真正想要修改的集合不同的集合。在某些情况下,这可以简单地通过ToList在序列上添加调用来完成,以便将其移动到新集合中;这样做时,被迭代的集合与被修改的集合是分开的,因此没有错误。

  2. 您可以避免修改foreach循环内的实际集合。这方面的常见示例是创建一个List或其他“要进行的更改”集合,无论它是itemsToAdditemsToRemove。然后您可以在循环之后为所有这些项目添加/删除/任何内容。(如果您只修改集合大小的一小部分,这很有效。)

  3. 某些类型的集合可以在不实际使用传统迭代器(即foreach循环)的情况下“迭代”。例如,您可以List使用常规for循环来迭代 a,并在添加或删除项目时简单地修改(递增/递减)循环变量。如果做得正确,这往往是一个有效的选择,但很容易犯错误和出错,所以虽然其他选项(略微)效率较低,但它们对于非性能密集型代码来说是非常好的选择,因为他们的简单。

于 2012-07-19T14:49:01.233 回答
2

你不能修改你正在迭代的集合。

您可以用简单的“for”循环替换“foreach”循环,但请注意在从集合中添加/删除项目时正在运行的索引。

像这样:

        for (int i = 0; i < tabControl.Items.Count; i++)
        {
            TabItem item = tabControl.Items[i];
            ... // your logic here
        }

另一个可能方便的选项是将项目添加到选项卡控件中,而不是将项目添加到选项卡控件中。项目集合将其作为返回值,将它们保存在列表中,在完成所有项目的迭代后,插入所有选项卡创建到 items 集合中,因此您在运行集合时不会修改集合。

于 2012-07-19T14:48:18.113 回答
1

tabControl.Items在枚举集合(在循环中进行)时,不允许修改集合(在这种情况下),foreach因为它会使枚举器无效。

导致错误的特定代码行可能是

// In SubGraphButton_Click
// This line of code is called inside an enumeration of tabControl.Items
// This is not permitted!
tabControl.Items.Add(tabItem1);

从概念上讲,您的代码如下所示:

private void ChildNode_Click(object sender, RoutedEventArgs args)
{
    System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
    Node node = Part.FindAncestor<Node>(button);
    MyNodeData nodeData = node.Data as MyNodeData;
    foreach (TabItem item in tabControl.Items)
    {
        if (nodeData.Text == item.Header.ToString())
        {
            item.Focus();
        }
        else if (nodeData.Text != item.Header.ToString())
        {
            // This line will throw an exception 
            DoSomethingThatModifiesTabControlItemsCollection()
        }
    }
 }
于 2012-07-19T14:48:09.107 回答
1

在 foreach 循环中,您调用 SubGraphButton_Click 依次添加一个新节点tabControl.Items.Add(tabItem1); 这是不允许的。您可以改用 for 循环。

于 2012-07-19T14:48:26.213 回答
1

是的,这条线

tabControl.Items.Add(tabItem1); 

更改您在 NodeClick 中枚举的集合,这
在枚举世界中是不可以的

尝试使用标准 for 循环,但顺序相反......

    for( int x = tabControl.Items.Count - 1; x>= 0; x--)
    {  
        TabItem item = tabControl.Items[x];
        if (nodeData.Text == item.Header.ToString())  
        {  
            item.Focus();  
        }  
        else if (nodeData.Text != item.Header.ToString())  
        {  
            SubGraphButton_Click(sender, args);  
        }  
    }  

以相反的顺序循环避免检查在 SubGraphButton 中添加的新项目。
我不知道这是否是预期的效果。

于 2012-07-19T14:52:07.740 回答
0

当您对 tabControl 中的 TabItems 进行 ForEach 时,您无法在 ForEach 内执行任何会导致 tabControl 的项目集合更改的任何操作。

这是框架的限制。这是因为您当前正在迭代 TabItems。

所以在您的ChildNode_Click函数内部,

在你的 ForEach 内部

foreach (TabItem item in tabControl.Items)

你打电话给

SubGraphButton_Click(sender, args);

在该函数内部,您调用

tabControl.Items.Add(tabItem1);

您不能在 ForEach 中操作 Items 集合。

于 2012-07-19T14:49:01.173 回答