• TreeView的几个使用小技


    最近的项目里要用到TreeView控件,而它的数据来源是从数据库里读取,而不是磁盘。我试过从从磁盘读取目录结构,感觉还不错,曾经在5秒内返回了我的C盘所有数据。

    然而从数据库里读取数据是一个很头痛的事情,因为不得不用递归来处理数据,而这样是很浪费时间的,特别是数据库的读取上,基本上90%的时间都消费在数据库的读取上。下面是这样的一个例子:

       private void CreateTreeNode(TreeNode i_node, long i_rootFolderID)
      {
       Folder m_tempFolder   = new Folder();
       DataTable m_table   = new DataTable();
       WaveFolderManager.GetSubFolderData(m_table,i_rootFolderID);
       foreach(DataRow m_row in m_table.Rows)
       {
        TreeNode m_subFolder   = new TreeNode();
        m_subFolder.ExpandedImageUrl = m_openFolderPic;
        m_subFolder.ImageUrl   = m_closeFolderPic;
        m_subFolder.Text    = m_row["f_folderName"].ToString();
        m_subFolder.NodeData   += m_row["f_id"].ToString()+"|";
        m_subFolder.NodeData   += m_row["f_parentID"].ToString()+"|";
        m_subFolder.NodeData   += m_row["f_clientID"].ToString()+"|Folder";
        CreateTreeNode(m_subFolder,Convert.ToInt64(m_row["f_id"]));
        i_node.Nodes.Add(m_subFolder);
       }
       m_table.Clear();
       WaveFolderManager.GetFiles(m_table,i_rootFolderID);
       foreach(DataRow m_row in m_table.Rows)
       {
        TreeNode m_files  = new TreeNode();
        m_files.CheckBox  = true;
        m_files.Text   = m_row["v_fileName"].ToString();
        m_files.NodeData  += m_row["v_id"].ToString()+"|";
        m_files.NodeData  += m_row["v_folderID"].ToString()+"|";
        m_files.NodeData  += m_row["v_clientID"].ToString()+"|File";
        i_node.Nodes.Add(m_files);
       }
      }
    上面的算法在数据库数据不大,而且数据库处理速度很快的时候可以临时用一下吧,但真的太不理想了。10级以内的调用都要花5秒左右的时间,而且总记录数不到100,真是郁闷死了。

    后来改用每一级展开,也就是让TreeView的AutoPostBack为真,并处理OnExpend事件,这样就可以每展开一级的时候来载入数据。这是一个很不错的选择,同样的算法,只是只读取到下一及,并用Loading来代替子树。
      private void CreateSubTreeNode(TreeNode i_node,long i_rootFolderID)
      {
       Folder m_tempFolder   = new Folder();
       DataTable m_table   = new DataTable();
       WaveFolderManager.GetSubFolderData(m_table,i_rootFolderID);
       foreach(DataRow m_row in m_table.Rows)
       {
        TreeNode m_subFolder   = new TreeNode();
        m_subFolder.ExpandedImageUrl = m_openFolderPic;
        m_subFolder.ImageUrl   = m_closeFolderPic;
        m_subFolder.Text    = ""+m_row["f_folderName"].ToString()+"";
        m_subFolder.NodeData   += m_row["f_id"].ToString()+"|";
        m_subFolder.NodeData   += m_row["f_parentID"].ToString()+"|";
        m_subFolder.NodeData   += m_row["f_clientID"].ToString()+"|Folder";
       // CreateTreeNode(m_subFolder,Convert.ToInt64(m_row["f_id"]));
        long m_subID = Convert.ToInt64(m_row["f_id"]);
        if(WaveFolderManager.GetSubFolderNumber(m_subID)>0||WaveFolderManager.GetSubFilesNumber(m_subID)>0)
        {
         TreeNode m_subFolders  = new TreeNode();
         m_subFolders.Text   = "Click 'Load SubFolder' to Load data...";
         m_subFolder.Nodes.Add(m_subFolders);
        }
        i_node.Nodes.Add(m_subFolder);
       }
       m_table.Clear();
       WaveFolderManager.GetFiles(m_table,i_rootFolderID);
       foreach(DataRow m_row in m_table.Rows)
       {
        TreeNode m_files  = new TreeNode();
        m_files.CheckBox  = true;
        m_files.Text   = m_row["v_fileName"].ToString();
        m_files.NodeData  += m_row["v_id"].ToString()+"|";
        m_files.NodeData  += m_row["v_folderID"].ToString()+"|";
        m_files.NodeData  += m_row["v_clientID"].ToString()+"|File";
        i_node.Nodes.Add(m_files);
       }
      }
    这样确实不错,每次读取数据库的时间都只有0.2秒左右。而这样最不好的就是每选择一个节点都会有一次的载入,而且TreeView控件的事件是一起的,不能独立出展开事件及点击事件,而树载入后,其它的主要操作就是选择节点,而每次都载入一次让人很受不了。虽然基本上不花什么时间,但如果在INTERT上,大量的时间可能要消费在来回的路费上。所以该方法也放弃了。。。。。。。

    最可行的方法可能就是在页面内存里进行数据处理,也就是先一口气从数据库里取出所有的记录,然后在页面逻辑里生成树,这样方法很有效,而且页面还可以缓存数据。这是它的算法:

      public void ReloadFolderAndFileData(DataSet i_dataSet)
      {
       DataTable m_table1 = new DataTable("m_folders");
       DataTable m_table2 = new DataTable("m_files");
       WaveFolderManager.GetAllFolders(m_table1);
       WaveFileManager.GetAllFiles(m_table2);
       i_dataSet.Tables.Add(m_table1);
       i_dataSet.Tables.Add(m_table2);
      }

      private void CreateTreeNodeWithCache(TreeNode i_node, long i_rootFolderID,DataSet i_dataset)
      {
       foreach(DataRow m_row in i_dataset.Tables["m_folders"].Rows)
       {
        if(Convert.ToInt64(m_row["f_parentID"])==i_rootFolderID)
        {
         TreeNode m_subFolder   = new TreeNode();
         m_subFolder.ExpandedImageUrl = m_openFolderPic;
         m_subFolder.ImageUrl   = m_closeFolderPic;
         m_subFolder.Text    = m_row["f_folderName"].ToString();
         m_subFolder.NodeData   += m_row["f_id"].ToString()+"|";
         m_subFolder.NodeData   += m_row["f_parentID"].ToString()+"|";
         m_subFolder.NodeData   += m_row["f_clientID"].ToString()+"|Folder";
         CreateTreeNodeWithCache(m_subFolder,Convert.ToInt64(m_row["f_id"]),i_dataset);
         i_node.Nodes.Add(m_subFolder);
        }
       }
       foreach(DataRow m_row in i_dataset.Tables["m_files"].Rows)
       {
        if(Convert.ToInt64(m_row["v_folderID"])==i_rootFolderID)
        {
         TreeNode m_files  = new TreeNode();
         m_files.CheckBox  = true;
         m_files.Text   = m_row["v_fileName"].ToString();
         m_files.NodeData  += m_row["v_id"].ToString()+"|";
         m_files.NodeData  += m_row["v_folderID"].ToString()+"|";
         m_files.NodeData  += m_row["v_clientID"].ToString()+"|File";
         i_node.Nodes.Add(m_files);
        }
       }
      }

    然而这给树的操作带来了很多的不便。首先,当用户删除一个结点的时候,数据库是一定要更新的。而这个时候还得更新内存里的DataSet并且还要更新TreeView结构,这样一来,三者同时更新的算法可不好受。我没有继续下去,只是做一个更改节点名字的小函数,而删除,转移和复制就都放弃了,因为实在是不好处理好三者的同步(而且学不知道这样做的速度到底怎样)。最头痛的还是TreeView的Node不能在循环里更改,这应该是.net的属性,也就是当用foreach在一个集合里循环的时候,是不能修改这个集合的,所以在TreeNodes里循环处理数据的时候,不能添加和删除节点,这是很头痛的,这不得不再用一个集合记录下哪些结点要添加,哪些结点要删除。而这样又添加了一个表在内存里(小做了一下,没完成)。所以,最后也放弃了这个方案。

    最后,得用TreeView自己的数据结构来处理数据。因为从数据库里读取的记录,最后要的是树型的数据结构,而不是记录本身,而TreeView已经在第一次载入的时候形成了树,所以就没有必要再载入了。而TreeView有视图缓存在客户端,这样本身就保存了数据,利用这样一个特点,可以考虑这样的方案:只处理数据库与TreeView本身,而且充分利用视图缓存。

    以下是几个主要的函数:

       if(!IsPostBack)
       {//只在第一次访问的时候读取数据库。
        this.LoadTreeNodeData();
       }

    //还是从内存的DataSet里生成树,但DataSet并不缓存在页面里。
      private void CreateTreeNodeWithCache(TreeNode i_node, long i_rootFolderID,DataSet i_dataset)
      {
       foreach(DataRow m_row in i_dataset.Tables["m_folders"].Rows)
       {
        if(Convert.ToInt64(m_row["f_parentID"])==i_rootFolderID)
        {
         TreeNode m_subFolder   = new TreeNode();
         m_subFolder.ExpandedImageUrl = m_openFolderPic;
         m_subFolder.ImageUrl   = m_closeFolderPic;
         m_subFolder.Text    = m_row["f_folderName"].ToString();
         m_subFolder.NodeData   += m_row["f_id"].ToString()+"|";
         m_subFolder.NodeData   += m_row["f_parentID"].ToString()+"|";
         m_subFolder.NodeData   += m_row["f_clientID"].ToString()+"|Folder";
         CreateTreeNodeWithCache(m_subFolder,Convert.ToInt64(m_row["f_id"]),i_dataset);
         i_node.Nodes.Add(m_subFolder);
        }
       }
       foreach(DataRow m_row in i_dataset.Tables["m_files"].Rows)
       {
        if(Convert.ToInt64(m_row["v_folderID"])==i_rootFolderID)
        {
         TreeNode m_files  = new TreeNode();
         m_files.CheckBox  = true;
         m_files.Text   = m_row["v_fileName"].ToString();
         m_files.NodeData  += m_row["v_id"].ToString()+"|";
         m_files.NodeData  += m_row["v_folderID"].ToString()+"|";
         m_files.NodeData  += m_row["v_clientID"].ToString()+"|File";
         i_node.Nodes.Add(m_files);
        }
       }
      }

    这里是更新树的时候最要注意的:
      private void Toolbar1_ButtonClick(object sender, System.EventArgs e)
      {
       ToolbarButton m_button  = sender as ToolbarButton;
       string m_commandID   = m_button.ID;
       switch(m_commandID)
       {
        case "c_command1"://Rename a file
         RenameFile();
         break;
        case "c_command2"://Delete a file
         ShowCaution();
         break;
        case "c_command3"://Move files
         string[] m_nodeData1 = GetNodeData();
         if(m_nodeData1.Length<4||m_nodeData1[3].ToLower()!="folder")return;

         DataTable m_table = new DataTable();
         m_table.Columns.Add(new DataColumn("v_id"));
         m_table.Columns.Add(new DataColumn("v_folderID"));
         m_table.Columns.Add(new DataColumn("v_clientID"));
         m_table.Columns.Add(new DataColumn("v_fileName"));
         m_table.Columns.Add(new DataColumn("v_nodeIndex"));
         MoveFiles(TreeView1.GetNodeFromIndex("0"),GetSelectNodeID(),m_table);
         TreeNode m_node = TreeView1.GetNodeFromIndex(TreeView1.SelectedNodeIndex);
         TreeNode[] m_tempNode = new TreeNode[m_table.Rows.Count];
         for(int i=0;i     {//取得要移动的节点,并记录下,注意,这里不能直接Remove,否则取不到下一个节点。
          m_tempNode[i] = TreeView1.GetNodeFromIndex(m_table.Rows[i]["v_nodeIndex"].ToString());
         }
         foreach(TreeNode i_node in m_tempNode)
         {//一口气删除
          i_node.Remove();
         }
         foreach(DataRow m_row in m_table.Rows)
         {
          TreeNode m_subFolder = new TreeNode();
          m_subFolder.CheckBox = true;
          m_subFolder.Text  = m_row["v_fileName"].ToString();
          m_subFolder.NodeData += m_row["v_id"].ToString()+"|";
          m_subFolder.NodeData += m_row["v_folderID"].ToString()+"|";
          m_subFolder.NodeData += m_row["v_clientID"].ToString()+"|file";
          m_node.Nodes.Add(m_subFolder); 
         }
    //     this.TreeView1.Nodes.Clear();原来要重新载入树,而修改后,不用再载入了,而是直接修改视图里的树
    //     LoadTreeNodeData();
         break;
        case "c_command4"://Copy files
         string[] m_nodeData2 = GetNodeData();
         if(m_nodeData2.Length<4||m_nodeData2[3].ToLower()!="folder")return;
         DataTable m_table2 = new DataTable();
         m_table2.Columns.Add(new DataColumn("v_id"));
         m_table2.Columns.Add(new DataColumn("v_folderID"));
         m_table2.Columns.Add(new DataColumn("v_clientID"));
         m_table2.Columns.Add(new DataColumn("v_fileName"));
         CopyFiles(TreeView1.GetNodeFromIndex("0"),GetSelectNodeID(),m_table2);
         TreeNode m_node2 = TreeView1.GetNodeFromIndex(TreeView1.SelectedNodeIndex);
         foreach(DataRow m_row in m_table2.Rows)
         {
          TreeNode m_subFolder = new TreeNode();
          m_subFolder.CheckBox = true;
          m_subFolder.Text  = m_row["v_fileName"].ToString();
          m_subFolder.NodeData += m_row["v_id"].ToString()+"|";
          m_subFolder.NodeData += m_row["v_folderID"].ToString()+"|";
          m_subFolder.NodeData += m_row["v_clientID"].ToString()+"|file";
          m_node2.Nodes.Add(m_subFolder);
         }
         break;
        case "c_command5":
         ShowFileInfo();
         break;
        case "c_command6":
         DownloadFile();
         break;
        default:
         break;
       }
      }
    ///下面是一个MoveFile的函数:
      ///


      ///
      ///

      ///
      ///
      private void MoveFiles(TreeNode i_node,long i_folderID,DataTable i_table)
      {
       foreach(TreeNode m_node in i_node.Nodes)
       {
        if(m_node.Checked)
        {
         MoveFile(m_node,i_folderID);
         string[] m_nodeData = m_node.NodeData.Split(new char[]{'|'});
         DataRow m_row  = i_table.NewRow();
         m_row["v_id"]  = m_nodeData[0];
         m_row["v_folderID"] = i_folderID;
         m_row["v_clientID"] = m_nodeData[2];
         m_row["v_fileName"] = m_node.Text;
         m_row["v_nodeIndex"]= m_node.GetNodeIndex();
         i_table.Rows.Add(m_row);
        }
        MoveFiles(m_node,i_folderID,i_table);
       }
      }
      private void MoveFile(TreeNode i_node,long i_folderID)
      {
       string[] m_nodeData = i_node.NodeData.Split(new char[]{'|'});
       long m_fileID  = Convert.ToInt64(m_nodeData[0]);
       WaveFile m_file  = new WaveFile();
       m_file.LoadFileData(m_fileID);
       m_file.FolderID = i_folderID;
       m_file.UpdateData(m_fileID);
       m_file.Dispose();
      }

    ================================
      /\_/\                        
     (=^o^=)  Wu.Country@侠缘      
     (~)@(~)  一辈子,用心做一件事!
    --------------------------------
      学而不思则罔,思而不学则怠!  
    ================================
  • 相关阅读:
    hdu-1162 Eddy's picture---浮点数的MST
    hdu-3371 Connect the Cities---kruskal
    hdu-1879 继续畅通工程---确定部分边的MST
    hdu-1875 畅通工程再续---MST
    hdu1863 畅通工程---MST&连通
    hdu-1233 还是畅通工程---MST模板
    hdu-1232 畅通工程---并查集
    BZOJ3940: [Usaco2015 Feb]Censoring
    BZOJ2434: [Noi2011]阿狸的打字机
    BZOJ2938: [Poi2000]病毒
  • 原文地址:https://www.cnblogs.com/WuCountry/p/300151.html
Copyright © 2020-2023  润新知