Click here to Skip to main content
15,881,380 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello Friends,

I need to display a large XML file (>21MB) in a tree view control in a C# Windows Form application. I have written the code which is working for small XML files but when i am trying to open a BIG XML file (>1 MB), its taking too much of time.
Can anyone suggest how i can optimise this and suggest me any changes or alternatives to achieve this.

Below is the code snippet:
C#
private void CreateTreeViewFromATXML(string strSrcFileName)
{
    XmlDataDocument xmldoc = new XmlDataDocument();
    XmlNode xmlnode ;
    FileStream fs = new FileStream(strSrcFileName, FileMode.Open, FileAccess.Read);
    xmldoc.Load(fs);
    xmlnode = xmldoc.ChildNodes[1];
    XMLTreeView.Nodes.Clear();
    XMLTreeView.Nodes.Add(new TreeNode(xmldoc.DocumentElement.Name));
    TreeNode tNode ;
    tNode = XMLTreeView.Nodes[0];
    AddNode(xmlnode, tNode);
}

private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
    //XmlNode xNode ;
    TreeNode tNode ;
    XmlNodeList nodeList ;
    int i = 0;
    if (inXmlNode.HasChildNodes)
    {
        nodeList = inXmlNode.ChildNodes;

        foreach (XmlNode XNode in inXmlNode.ChildNodes)
        {
            tNode = new TreeNode(XNode.Name);
            inTreeNode.Nodes.Add(tNode);
            AddNode(XNode, tNode);
        }
    }
    else
    {
        inTreeNode.Text = inXmlNode.InnerText.ToString();
    }
}


Thanks,
Dinesh
Posted
Comments
Sinisa Hajnal 14-May-15 6:30am    
Split XMLDocument into XML fragments, set separate threads to work on it and on thread completion merge the resultant tree.

Depending on your processor architecture you could do it in small fragment of the original time.
[no name] 14-May-15 6:33am    
Appreciate your quick response.
The suggested solution is good but i beleive that way it will become a little complex and tough. I am looking for some kind of inbuilt Dotnet APIs or any simple work arounds that will be efficient and easy to implement and handle. Thanks anyways :)
Sinisa Hajnal 14-May-15 6:45am    
BackgroundWorker threads are really simple to implement. Check this out:
https://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx

As for built-in functionality, you're already using it. In fact, your example is nearly line-by-line howto on MSDN.
[no name] 14-May-15 7:30am    
Thanks again. Will try background worker and see if it reduces the time.
Andy Lanng 14-May-15 6:45am    
You already have a recursive treebuilder. Just throw the AddNode recursive call at the ThreadPool. Put a manualresetevent on each thread so the parent thread continues once all the child recursions are finished.

If I can explain it in a short paragraph then it won't be very complex

1 solution

Here is a threaded version of your code:

C#
private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
    //XmlNode xNode ;
    TreeNode tNode ;
    XmlNodeList nodeList ;
    int i = 0;
    if (inXmlNode.HasChildNodes)
    {
        nodeList = inXmlNode.ChildNodes;

        //set of reset events so this method doesn't return until all child recursions are complete
        List<manualresetevent> resetEvents = new List<manualresetevent>();

        foreach (XmlNode XNode in nodeList)
        {
            tNode = new TreeNode(XNode.Name);
            inTreeNode.Nodes.Add(tNode);

            ManualResetEvent resetEvent = new ManualResetEvent(false);

            resetEvents.Add(resetEvent);

            //ThreadPool.  Let it manage your thread limitations
            ThreadPool.QueueUserWorkItem(state =>
            {
                AddNode(XNode, tNode);
                resetEvent.Set();
            });
            //I mean, Threadpool just does everything.  And it's that easy to implement!  How awesome if that? (Threadpool is not a silver bullet)

        }
        //Wait for the child threads to complete
        WaitHandle.WaitAll(resetEvents.ToArray());
    }
    else
    {
        inTreeNode.Text = inXmlNode.InnerText.ToString();
    }
}


EDIT: OP has implemented the code and hit the Thread Invoke Member error:

Update:

Well, here we get into a big ol' chat about Threads and how they work. This is a great subject to research because it is so useful and often used.

Basically, you have your code-behind running in one thread and your form running in a child thread (bet you didn't know that, did you ^_^).

The parent thread (code behind) can see it's own children, but the children can't see each other. Your inTreeNode does not exist in the recursive thread tree.

There are a couple of ways around this. The advice the error is giving you is to set up a method on the parent thread (code behind) that calls (invokes) the child thread running the form. You can call the parent thread from the child so the thread delegate can be used to access the form control.

With me so far? Perhaps not. If you use this method then you have to worry about "thread safety" which is preventing two threads trying to access the same object at the same time. It's the 'best' method but probably the most confusing.

Instead, the easier option is to pass the control as a parameter down to the recursive threads.

Change each AddNode to CreateNode that returns a TreeNode. Have each CreateNode method return the tNode object after it has called CreateNode and added the created nodes to tNode. After all recursions are complete you will be left with a brand new TreeNode in your parent thread. Then just add it to the TreeView ^_^


I will include the following example as is. I won't test it and I'm sure it won't work well. I'll leave that for you to fix as it is you who will be maintaining the code.

C#
private TreeNode CreateNode(XmlNode xmlNode)
{
    TreeNode treeNode = new TreeNode();
    if (xmlNode.HasChildNodes)
    {
        List<ManualResetEvent> resetEvents = new List<ManualResetEvent>();
        foreach (XmlNode xNode in xmlNode.ChildNodes)
        {
            ManualResetEvent resetEvent = new ManualResetEvent(false);
            resetEvents.Add(resetEvent);

            var node = xNode;
            ThreadPool.QueueUserWorkItem(state =>
            {
                treeNode.Nodes.Add(CreateNode(node));
                resetEvent.Set();
            });

        }
        WaitHandle.WaitAll(resetEvents.ToArray());
    }
    else
    {
        treeNode.Text = xmlNode.InnerText;
    }

    return treeNode;
}
 
Share this answer
 
v3
Comments
[no name] 14-May-15 7:51am    
@Andy: I tried your code, but its throwing some error:

Error:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll

Additional information: 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.

Could you please help.
Andy Lanng 14-May-15 8:53am    
Please review the solution updates

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900