I'm starting a process in a thread that is used to built up a treeview with a recursive method. I'm getting the following error

"Action being performed on this control is being called from the wrong thread. marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action."

Here is where i start my thread: It's crashing on
"inTreeNode.Nodes.Add( tNode2 );" as if this was a reference directly to the original treeview, which isn't the case.

private void Init( XmlDocument xdoc )
                {
                        treeView.Nodes.Clear();
                        treeView.Nodes.Add( new TreeNode( xdoc.DocumentElement.Name ) );

                        TreeNode tNode = new TreeNode();
                        tNode = treeView.Nodes[ 0 ];

                        Worker worker = new Worker( xdoc.DocumentElement , tNode );
                        worker.TaskComplete += new Worker.WorkerEventHandler( worker_TaskComplete );

                        Thread thread = new Thread( worker.Start );

                        thread.Start();

                        //AddNode( xdoc.DocumentElement , tNode );
                        treeView.Nodes[ 0 ].Expand();
                }

Here is my worker definition.

class Worker
        {
                private bool _stop;
                private int _count = 0;

                private XmlNode _inXmlNode;
                private TreeNode _inTreeNode;

                public delegate void WorkerEventHandler( object sender , WorkerEventArgs e );

                public event WorkerEventHandler TaskComplete;
                public event WorkerEventHandler WorkComplete;

                public void Start()
                {
                        AddNode( _inXmlNode , _inTreeNode );
                }

                public Worker( XmlNode inXmlNode , TreeNode inTreeNode )
                {
                        _inXmlNode = inXmlNode;
                        _inTreeNode = inTreeNode;
                }

                public void AddNode(  XmlNode inXmlNode , TreeNode inTreeNode )
                {
                        /*TaskComplete( this , new WorkerEventArgs( _count ) );
                        _count++;*/
                        try
                        {
                                XmlNode xNode;
                                XmlNodeList nodeList;
                                XmlElement xElement = (XmlElement)inXmlNode;

                                TreeNode tNode;
                                TreeNode tNode2;

                                //Serialization kit
                                Child objPage;
                                StringReader sr;
                                XmlSerializer serializer = new XmlSerializer( typeof( Child ) );

                                if( inXmlNode.HasChildNodes )
                                {
                                        nodeList = inXmlNode.ChildNodes;
                                        for( int i = 0 ; i < nodeList.Count - 1 ; i++ )
                                        {
                                                xNode = inXmlNode.ChildNodes[ i ];
                                                xElement = (XmlElement)xNode;
                                                tNode2 = new TreeNode( xElement.GetAttribute( "name" ) ); // will be the name of the node

                                                //Serialize to later on save on the fly
                                                sr = new StringReader( xNode.OuterXml );
                                                objPage = (Child)serializer.Deserialize( sr );
                                                tNode2.Tag = objPage;

                                                inTreeNode.Nodes.Add( tNode2 );
                                                tNode = inTreeNode.Nodes[ i ];
                                                AddNode( xNode , tNode );
                                        }
                                        if( xElement.GetAttribute( "adopted" ) == "true" )
                                                inTreeNode.ImageIndex = (int)Image.AdoptedFolder;
                                        else
                                                inTreeNode.ImageIndex = (int)Image.Folder;
                                }
                                else
                                {
                                        inTreeNode.Text = xElement.GetAttribute( "name" ).Trim();
                                        if( xElement.GetAttribute( "adopted" ) == "true" )
                                                inTreeNode.ImageIndex = (int)Image.AdoptedPage;
                                        else
                                                inTreeNode.ImageIndex = (int)Image.Page;
                                }
                        }
                        catch( Exception ex )
                        {
                                MessageBox.Show( ex.Message , "Error" , MessageBoxButtons.OK , MessageBoxIcon.Error );
                        }
                }

                /// <summary>
                /// Tells this worker to stop it's processing cycle
                /// </summary>
                public void Stop()
                {
                        _stop = true;
                }

        }

Thanks for your help :) There might be some wrong stuff related to threading, I'm really new to that, feel free to comment.

You will have to use Invoke() method when calling something outside the thread.

Use this:

this.Invoke((MethodInvoker)delegate()
{
inTreeNode.Nodes.Add( tNode2 );
});

Thanks

You will have to use Invoke() method when calling something outside the thread.

Use this:

this.Invoke((MethodInvoker)delegate()
{
inTreeNode.Nodes.Add( tNode2 );
});

Thanks

Thanks for the reply, however, this didn't work. My worker is not a form so it doesnt contain the Invoke definition. I tried to pass as a parameter the treeview of the form, but same result.

Any ideas?

Sorry, didn't notice that.

Try this:

if (inTreeNode.InvokeRequired)
{
       inTreeNode.Invoke((MethodInvoker)delegate()
      {
             inTreeNode.Nodes.Add(tNode2);
      });
}

Sorry, didn't notice that.

Try this:

if (inTreeNode.InvokeRequired)
{
       inTreeNode.Invoke((MethodInvoker)delegate()
      {
             inTreeNode.Nodes.Add(tNode2);
      });
}

This leads to the same problem, inTreeNode is a treenode and not a treeview, weirdly, treeview contains invoke, but not treenode. Pretty sure that's because treeview is defined as a control and treenode is not.

Oh yea. TreeNode is not a control.

How about this:

Add a new member to Worker class:

private TreeView treeView;

Edit the Constructor:

public Worker( XmlNode inXmlNode , TreeNode inTreeNode, TreeView tView )
{
      _inXmlNode = inXmlNode;
      _inTreeNode = inTreeNode;
      treeView = tView;
}

Use this for the inTreeNode.Add():

if (treeView.InvokeRequired)
{
       treeView.Invoke((MethodInvoker)delegate()
      {
             inTreeNode.Nodes.Add(tNode2);
      });
}

And the worker variable:

Worker worker = new Worker( xdoc.DocumentElement , tNode , treeView );

Thanks

Going a step further !

Now I'm getting
"Cannot add or insert the item in more than one place. you must first remove it from its current location or clone it."

So I thought of cloning it, but unfortunetaly, treeview doesnt implement the interface ICloneable.

Alright fixed it, forgot to remove some code, and needed to add more code in the delegate ;)

Thanks man you are awesome !

Hmm, it's working, but it shouldnt work...

Normally, what should be done is that at the end of the recursive function an event should be called like "WorkComplete" and my form should have a method catching it and the event should return the treeview and than I could assign it to my form's treeview. But at the moment, it's simply being added by it self and it should not. I believe, I should not even need the Invoke because it should be doing anything in the main thread.

The whole key of this was to Clone the TreeNode I was passing. Because in c#, any objects that is passed in parameter (except string if im not mistaking) are passed as a reference and not an instance or a copy of it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.