8

再会!

我想听听你对这个问题的建议。

我正在办公室制作一个小工具,以将“产品层次结构数据”显示为树形视图。我们的应用程序只能以表格方式显示它,如果层次结构中存在任何不正确的数据,则很难追踪。

我能够制定一些逻辑并以正确的层次结构显示数据。

但是我这里的主要问题是我正在处理 100K-200K+ 记录,并且创建/分配每个节点并将其添加到树中肯定需要时间。根据我的测试,平均每分钟可以创建8000个节点。我还注意到这个应用程序的内存使用量随着它的运行而逐渐增加。

我将包含数据结构的屏幕截图以及应用程序的外观,以便为您提供一些想法:

在此处输入图像描述

在此处输入图像描述

请在下面考虑我的代码,我很高兴知道您对如何优化它的想法。我知道这里有很多需要改进的地方。

对不起,很长的帖子......

顺便说一句,我使用的是 C# 和 .net2.0。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Data.OleDb;
using System.Data;
using System.IO;
using System.Threading;

namespace WinformAppTest
{
    public partial class MainForm : Form
    {
        private DataSet _ds = new DataSet();

        public MainForm()
        {
            InitializeComponent();
        }

        void MainFormLoad(object sender, EventArgs e)
        {
            PopulateDataSet();

            lblTotalRows.Text = _ds.Tables[0].Rows.Count.ToString();
        }

        void PopulateDataSet()
        {
            string query = @"select * from " + "test.csv";
            try
            {
                OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\\;Extended Properties=\"Text;HDR=Yes;FMT=Delimited\"");
                OleDbDataAdapter da = new OleDbDataAdapter();

                OleDbCommand cmd = new OleDbCommand(query, conn);
                conn.Open();
                da.SelectCommand = cmd;
                _ds.Clear();
                da.Fill(_ds, "CSV");

                conn.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.GetBaseException().ToString());
            }
        }

        void BtnRunClick(object sender, EventArgs e)
        {
            Thread newThread1 = new Thread(MainForm.DoWork);
            newThread1.Start(this);
        }

        public static void DoWork(object data)
        {
            MainForm form = (MainForm)data;
            int counter = 0;
            TreeNode[] nd;

            foreach(DataRow row in ((MainForm)data)._ds.Tables[0].Rows)
            {
                TreeNode node = new TreeNode();
                if(row["Level Number"].ToString() == "1")
                {           
                    node.Name = row["ECC Hierarchy Code"].ToString();
                    node.Text = row["ECC Hierarchy Code"].ToString() + ", " + row["Name"].ToString();

                    form.Invoke((MethodInvoker)delegate { ((MainForm)data).treeView1.Nodes.Add(node); });       
                }
                else
                {                   
                    nd = ((MainForm)data).treeView1.Nodes.Find(row["Parent Code"].ToString(), true);
                    node.Name = (string)row["ECC Hierarchy Code"];
                    node.Text = row["ECC Hierarchy Code"].ToString() + ", " + row["Name"].ToString();

                    form.Invoke((MethodInvoker)delegate { nd[0].Nodes.Add(node); });
                }

                counter++;
                form.Invoke((MethodInvoker)delegate { ((MainForm)data).lblLoded.Text = counter.ToString(); });
            }       
        }
    }
}
4

3 回答 3

9

这其中的关键可能如下:

  1. 调用 TreeView.BeginUpdate
  2. 向 TreeView 添加/删除多个节点
  3. 调用 TreeView.EndUpdate

这会停止在添加/删除节点时发生的任何绘制。一旦调用了 EndUpdate,整个 TreeView 就会被绘制一次。

http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.beginupdate.aspx

于 2012-06-03T23:04:16.190 回答
3

一些想法...

  1. 您正在使用线程来获取数据,然后使用 Invoke 将每个数据项添加到树中。这是非常缓慢的。一个更好的主意是使用线程来获取您需要的所有数据,然后使用 treeView1.Nodes.AddRange() 一次将所有节点添加到树中。

  2. 您可以通过重新设计应用程序来进行更多优化,以便它获取第一级节点的所有数据,然后仅在用户展开树节点时获取较低级别节点的数据。

于 2012-06-01T20:02:30.873 回答
2

在处理大型数据集时,内置的 WinForms 控件会受到限制。据我记得,内置的 TreeView 将尝试为每个节点分配它自己的内存(这意味着你有一个数据副本,加上额外的元数据,对于你的 100K+ 记录中的每一个,即使它们中的大多数不会在给定的会话中查看。

我使用提供虚拟节点的开源 TreeView 替代方案取得了很大的成功(在它们变得可见之前您不会渲染)

http://www.codeproject.com/Articles/20552/Virtual-Treeview-Implementation

于 2012-06-01T04:04:56.933 回答