如何快速加载程序中的XML?

时间:2013-01-15 15:36:21

标签: c# .net winforms

我正在创建一个小工具,此工具将包含XML树视图。处理文件较小的XML文件没有问题,但是当我尝试加载大型XML文件(大小为21MB)时,我的应用程序变得没有响应,并且需要花费太多时间来加载XML,而且大部分时间都是如此。它根本不加载XML的时间。是否有任何修复或调整可以使下面的代码更快?

public TreeView(string filename)
        {
            InitializeComponent();

            this.Text = filename;

            XmlDataDocument xmldoc = new XmlDataDocument();
            XmlNode xmlnode;
            FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            xmldoc.Load(fs);
            xmlnode = xmldoc.ChildNodes[1];
            treeView1.Nodes.Clear();
            treeView1.Nodes.Add(new TreeNode(xmldoc.DocumentElement.Name));
            TreeNode tNode;
            tNode = treeView1.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;
                for (i = 0; i <= nodeList.Count - 1; i++)
                {
                    xNode = inXmlNode.ChildNodes[i];
                    inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
                    tNode = inTreeNode.Nodes[i];
                    AddNode(xNode, tNode);
                }
            }
            else
            {
                inTreeNode.Text = inXmlNode.InnerText.ToString();
            }
        }

6 个答案:

答案 0 :(得分:2)

您必须将加载部分与构建TreeView分开。

然后可以在后台线程(BackgroundWorker)上进行加载。

与TreeView相关的所有内容都必须在主线程上发生,但您可以使用SuspendLayout等来加快速度。

答案 1 :(得分:1)

对于无响应的部分,您可以异步地在BackgroundWorker中加载xml,也许构建树视图结构的整个过程可以这样完成,并在完成时加载到控件本身。

这可能无法提高速度,但它会保持用户界面的响应速度。请记住,您无法直接从BackgroundWorker中运行的代码访问UI,您必须通过Dispatcher调用它,如下所示:

this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
     // Execute code that works with UI controls
}));

答案 2 :(得分:1)

您可以尝试将Parallel.ForEach用于子节点以跨核心分割内容。尽管如此,这将是棘手的。

http://msdn.microsoft.com/en-us/library/dd991486.aspx

答案 3 :(得分:0)

两个小建议:不是添加一个节点然后查找它,而是在temp变量中创建它然后从那里使用它。您可以使用foreach循环而不是for循环。

替换

nodeList = inXmlNode.ChildNodes;
for (i = 0; i <= nodeList.Count - 1; i++)
{
    xNode = inXmlNode.ChildNodes[i];
    inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
    tNode = inTreeNode.Nodes[i];
    AddNode(xNode, tNode);
}

foreach (var node in inXmlNode.ChildNodes)
{
    tNode = new TreeNode(node.Name);
    inTreeNode.Nodes.Add(tNode);
    AddNode(node, tNode);
}

我怀疑它会产生多大影响,但它可能会有所帮助。

答案 4 :(得分:0)

不使用XmlDataDocument,而是将代码更改为使用XmlReader,因为此类在处理XML文件时提供最高性能。 XmlReader是一个抽象类,可以从文件或任何其他数据流中读取。从文件中读取时,不会立即加载整个文档。

此处提供更多信息:shanselman的Quick overview 或者Performance: LINQ to XML vs XmlDocument vs XmlReader

答案 5 :(得分:0)

首先,你应该在处理大文件时切换到类似SAX的方法。有关一种可能的解决方案,请参阅What is the best way to parse (big) XML in C# Code?

现在你有一个可靠的方法来读取不会导致系统崩溃的文件,无论文件有多大。

下一步是填写你的树视图,正如你所说的那样,你需要在一个单独的线程中执行此操作,我建议使用backgroundworker,因为它们非常容易使用。

至于更改发生时更新用户界面,我不熟悉winforms,但如果支持ObservableCollection,我建议使用thous。