[讨论]TreeView控件:加载大量数据时,运行速度的问题!

liangfengxx 2007-04-20 01:24:41
做一个类似Windows资源管理器左边的树型菜单,需要遍历硬盘中所有文件夹,然后用TreeView控件绑定每一个文件夹及子文件夹以及文件夹中的文件(每一个节点绑定一个文件夹名或文件名,树节点的层次结构和文件夹的层次一样),我使用递归遍历硬盘中的文件夹,然后绑定到节点。

如图:
---------------------------------------------------
-我的电脑
-C:[WinXp]
+Documents and Settings
-Program Files
+Common Files
+ComPlus Applications
+Internet Explorer
+Microsoft ActiveSync
+Movie Maker
+Windows
-D:[软件]
+Microsoft SQL Server
-Winamp5.0.1
+.dll
+Theme
-Skins
+Skin1
+Skin2
+Skin3
+Adobe
+Acrobat 7.0
+Photoshop 7.0
+Flash8.0
+Microsoft Office
+Microsoft Visual Studio 8
-E:[娱乐]
+Mp3
+流行经典
+外国歌曲
+摇滚
+爵士
+乡村音乐
+港台歌手
+刘德华
+张学友
+谭咏麟
+周杰伦
+邓丽君
+梁咏琪
+大陆歌手
-电影
+<李连杰>专辑
+<周星驰>专辑
+<成龙>专辑
+A计划
+红番区
+龙少爷
+尖峰时刻
+宝贝计划
+游戏
+魔兽争霸
+帝国时代
+CS1.6
+小游戏
+搞怪碰碰球
+经典街机游戏
+连连看
+拼图游戏
------------------------------------------------
程序做是做出来了,但是程序启动时很慢,我估计了一下,绑定TreeView控件大概需要20s左右的时间(我的电脑配置:主频2.66G,512M内存,120G硬盘),那太夸张了吧...

对于这个问题,我的另一个想法是,在TreeView控件加载数据时只夹在第一层的数据,即最初只加载几个驱动器盘符,然后点击驱动器节点时,再加载该驱动器下面包含的文件和文件夹,以后各层文件夹使用同样的方法来加载子文件和子文件夹,但是程序实现起来要比我之前的程序要复杂。(主要是要判断某一个文件夹是否包含有文件以及子文件夹,以确定该文件夹前面的符号是否是"+"号)

想请教各位,还有什么更好的解决方案没有?欢迎拍砖~~~~~~~~~~~~
...全文
1335 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
cjc1021 2008-04-15
  • 打赏
  • 举报
回复
学习了...
dick78 2007-05-16
  • 打赏
  • 举报
回复
我是这样做的:第一次加载两级,每点击一个节点,再根据他的下一级(子节点)的数据,加载一级.用的是版NetAdvantage控件.这样每次只提取很少数据,速度不慢.
neil_cn 2007-04-27
  • 打赏
  • 举报
回复
很少写这么详细的注释,希望这样对你理解我的代码能有帮助
neil_cn 2007-04-27
  • 打赏
  • 举报
回复
注释版

Imports System.IO

Module Module1
'声明一个类 TestCtrl 的对象
Private testList As TestCtrl

Private myFrm As Form
Private myTree As TreeView

Private Sub TreeClicked(ByVal sender As Object, ByVal e As System.EventArgs)
'获取事件发生的源(sender),转换为 TreeView 对象以便进一步处理
Dim tmpTree As TreeView = sender
'获取事件参数,转换为 MouseEventArgs 类型以便处理
Dim ex As MouseEventArgs = e
'获取当前被点击的节点对象(是一个 TreeNode)
Dim actNode As TreeNode = tmpTree.GetNodeAt(ex.X, ex.Y)

'判断当前事件是否发生在 TreeNode 上
If (actNode IsNot Nothing) Then
'判断当前点击的节点在单击前是否处于收起状态
If (actNode.IsExpanded()) Then
If (actNode.Tag() = "Loaded") Then
'当前单击的节点的子节点已经加载过,简单展开即可
actNode.Expand()
Else
'当前单击的节点的子节点未被加载过,加载子节点

'获取当前节点对应的目录的名字
Dim strParentPath As String = actNode.Text()
'获取当前节点的父节点
Dim tmpNode As TreeNode = actNode.Parent()

'循环一直找到根节点,由此组成一个从根到当前节点的绝对路径,用来进一步检索这个路径下的一层子目录
While (tmpNode IsNot Nothing)
'对于型如"D:\"这样的需要特殊处理,处理目录树的常规方法而已
'同时将节点对应的目录名称拼接到字符串 strParentPath 中
If (tmpNode.Text().EndsWith("\")) Then
strParentPath = tmpNode.Text() + strParentPath
Else
strParentPath = tmpNode.Text() + "\" + strParentPath
End If

'向上一个节点继续尝试
tmpNode = tmpNode.Parent()
End While
'循环结束后,字符串 strParentPath 储存的即是当前单击的节点对应的文件系统中的绝对路径

'与上同,常规处理
If (Not strParentPath.EndsWith("\")) Then
strParentPath = strParentPath + "\"
End If

Dim i As Integer = 0
'如果当前单击的节点前面有加号,那么当前节点(actNode)一定有至少一个子节点
'循环用其子节点对应的目录的名称去尝试列举这个子节点对应的目录是否有子目录
'若有则将这些子目录添加到这个子节点上(相对于actNode其实是孙节点)
'循环结束时,actNode对应的目录的子目录全部都找了一遍,所有actNode的孙目录已经添加为孙节点
'此时 actNode 展开了,同时actNode的有子节点的子节点前面的加号将显示并可以点击展开了
While (i < actNode.Nodes.Count())
'设置当前路径为actNode的子节点对应的目录的绝对路径
testList.SetCurrentRoot(strParentPath + actNode.Nodes.Item(i).Text() + "\")
'检索指定目录的子目录,并将其添加为actNode的孙节点(actNode的子节点的子节点)
testList.BrowserCurrentRoot(myTree, actNode.Nodes.Item(i))

'设置下一个子节点的索引(超过总数就结束循环)
i = i + 1
End While

'为当前节点做上已加载过子节点的标记
actNode.Tag = "Loaded"
End If
End If
End If
End Sub

Public Sub Main()
'构造并初始化类 TestCtrl 的对象 testList,将 testList.strRoot 初始化为 "D:\"
testList = New TestCtrl("D:\")

'构造一个 Form 类的对象 myFrm
myFrm = New Form()

'构造一个 TreeView 类的对象 myTree
myTree = New TreeView()

'初始化 myFrm 和 myTree 的大小及相对位置
myFrm.Width = 400
myFrm.Height = 600

myTree.Left = 0
myTree.Top = 0
myTree.Width = 396
myTree.Height = 576

' 注册 myTree 鼠标单击事件的处理过程
AddHandler myTree.Click, AddressOf TreeClicked

'初始化 myTree 的根层的子节点(初始化结束后,根节点不展开,若有子节点,则根节点的前面的加号将显示)
testList.BrowserCurrentRoot(myTree, Nothing)

'将 myTree 添加为 myFrm 的控件
myFrm.Controls.Add(myTree)

'显示 myFrm
myFrm.ShowDialog()
End Sub

Public Class TestCtrl
Inherits System.Windows.Forms.Form

Private strRoot As String = ""

'构造过程,同时初始化类成员 strRoot
Public Sub New(ByVal strRootDir As String)
Me.strRoot = strRootDir
End Sub

'修改类成员 strRoot 的值的接口方法
Public Sub SetCurrentRoot(ByVal newRoot As String)
Me.strRoot = newRoot
End Sub

'检索指定节点对应目录下的第一层子目录,若存在,则将它们加入树的指定节点
'当传入的参数 tmpNode 为 Nothing 时,将其置为根节点:tmpNode = myTree.Nodes.Item(0)
Public Sub BrowserCurrentRoot(ByVal myTree As TreeView, ByVal tmpNode As TreeNode)
'简单的边界检测
If (Me.strRoot.Length() = 0) Then
Exit Sub
End If

'不做复杂检测了,全部扔到 try ... catch ... 块里去处理了
Try
'取得指定目录的 DirectoryInfo 对象
Dim myDir As New DirectoryInfo(Me.strRoot)
'从指定目录的 DirectoryInfo 对象获取所有子目录
Dim subDirs As DirectoryInfo() = myDir.GetDirectories()

If (subDirs.Length() > 0) Then
'如果还没有添加节点进树,把指定目录作为根节点添加进树
'(这点是必然的,因为在显示 myFrm 前已经调用了一次 testList.BrowserCurrentRoot(myTree, Nothing) )
If (myTree.Nodes.Count() = 0) Then
myTree.Nodes.Add(Me.strRoot)
End If

'当传入的参数 tmpNode 为 Nothing 时,将其置为根节点:tmpNode = myTree.Nodes.Item(0)
If (tmpNode Is Nothing) Then
tmpNode = myTree.Nodes.Item(0)
End If

Dim i As Integer = 0
'循环将当前目录的子目录添加为当前目录对应节点的子节点
While (i < subDirs.Length())
tmpNode.Nodes.Add(subDirs(i).FullName().Replace(Me.strRoot, ""))
i = i + 1
End While
Else
'如果还没有添加节点进树,把指定目录作为根节点添加进树
'(这点是必然的,因为在显示 myFrm 前已经调用了一次 testList.BrowserCurrentRoot(myTree, Nothing) )
If (myTree.Nodes.Count() = 0) Then
myTree.Nodes.Add(Me.strRoot)
End If
End If
Catch ex As System.Exception
'异常处理,偷懒了,不处理了
End Try
End Sub
End Class
End Module
红衣老大 2007-04-26
  • 打赏
  • 举报
回复
一个建议
你遍历到本层的时候 通过System.IO.Directory.Exists(filepath)判断是不是文件夹
如果是的话 就在他下边增加一个东西 但是要标记
这样前边就有+了

当你点机这个东西时候 你在 手搜索下边的东西 添加上去 在把你天的那个拿掉
liangfengxx 2007-04-26
  • 打赏
  • 举报
回复
to:xiaobingking(红衣)

你的方法是对的,我曾经也考虑过这个方案,谢谢你。

但我发现这样很不科学,我试过 neil_cn(Neil) 的方法,很好,我现在正在研究他的代码。

马上结帖~
liangfengxx 2007-04-23
  • 打赏
  • 举报
回复
"如果有子目录就打上+号"
-------------------------------------------
这个+号咋打?
我试过这个办法,不容易实现!

循环一层估计不行,如果用分批加载这个办法的话,要判断所展开的节点的子节点是否还有子目录,至少要循环两层;而且,如果有子目录,则要把子目录添加到节点中,这个+号才能显示吧!这个说起容易做起难啊~~~~~

能给点代码提示吗?
yangaiyuan 2007-04-23
  • 打赏
  • 举报
回复
学习 帮顶
neil_cn 2007-04-23
  • 打赏
  • 举报
回复
我的一个分区的目录数在10w-20w个,几乎感觉不到延时,这个数量级应该不会比你那边少吧?

Imports System.IO

Module Module1
Private testList As TestCtrl

Private myFrm As Form
Private myTree As TreeView

Private Sub TreeClicked(ByVal sender As Object, ByVal e As System.EventArgs)
Dim tmpTree As TreeView = sender
Dim ex As MouseEventArgs = e
Dim actNode As TreeNode = tmpTree.GetNodeAt(ex.X, ex.Y)

If (actNode IsNot Nothing) Then
If (actNode.IsExpanded()) Then
If (actNode.Tag() = "Loaded") Then
actNode.Expand()
Else
Dim i As Integer = 1
Dim strParentPath As String = actNode.Text()
Dim tmpNode As TreeNode = actNode.Parent()

While (tmpNode IsNot Nothing)
If (tmpNode.Text().EndsWith("\")) Then
strParentPath = tmpNode.Text() + strParentPath
Else
strParentPath = tmpNode.Text() + "\" + strParentPath
End If

tmpNode = tmpNode.Parent()
End While

If (Not strParentPath.EndsWith("\")) Then
strParentPath = strParentPath + "\"
End If

i = 0
While (i < actNode.Nodes.Count())
testList.SetCurrentRoot(strParentPath + actNode.Nodes.Item(i).Text() + "\")
testList.BrowserCurrentRoot(myTree, actNode.Nodes.Item(i))
i = i + 1
End While

actNode.Tag = "Loaded"
End If
End If
End If
End Sub

Public Sub Main()
testList = New TestCtrl("D:\")

myFrm = New Form()
myTree = New TreeView()

myFrm.Width = 400
myFrm.Height = 600

myTree.Left = 0
myTree.Top = 0
myTree.Width = 396
myTree.Height = 576

AddHandler myTree.Click, AddressOf TreeClicked

testList.BrowserCurrentRoot(myTree, Nothing)

myFrm.Controls.Add(myTree)

myFrm.ShowDialog()
End Sub

Public Class TestCtrl
Inherits System.Windows.Forms.Form

Private strRoot As String = ""

Public Sub New(ByVal strRootDir As String)
Me.strRoot = strRootDir
End Sub

Public Sub SetCurrentRoot(ByVal newRoot As String)
Me.strRoot = newRoot
End Sub

Public Sub BrowserCurrentRoot(ByVal myTree As TreeView, ByVal tmpNode As TreeNode)
If (Me.strRoot.Length() = 0) Then
Exit Sub
End If

Try
Dim myDir As New DirectoryInfo(Me.strRoot)
Dim subDirs As DirectoryInfo() = myDir.GetDirectories()

If (subDirs.Length() > 0) Then
If (myTree.Nodes.Count() = 0) Then
myTree.Nodes.Add(Me.strRoot)
End If

If (tmpNode Is Nothing) Then
tmpNode = myTree.Nodes.Item(0)
End If

Dim i As Integer = 0
While (i < subDirs.Length())
tmpNode.Nodes.Add(subDirs(i).FullName().Replace(Me.strRoot, ""))
i = i + 1
End While
Else
If (myTree.Nodes.Count() = 0) Then
myTree.Nodes.Add(Me.strRoot)
End If
End If
Catch ex As System.Exception

End Try
End Sub
End Class
End Module
leixueqiyi 2007-04-23
  • 打赏
  • 举报
回复
对呀,你可以不全部展开
blackhero 2007-04-23
  • 打赏
  • 举报
回复
像 csdn是的,动态加载呀
liangfengxx 2007-04-23
  • 打赏
  • 举报
回复
要下班了,顶上去!
neil_cn 2007-04-20
  • 打赏
  • 举报
回复
分批加载(点击节点再加载),浏览当前层节点(当前目录)是否有子目录(不用递归,就一层,找到就结束寻找),如果有子目录就打上+号,这个逻辑不复杂
XELEMENT 2007-04-20
  • 打赏
  • 举报
回复
“System Volume Information”文件夹,中文名称可以翻译为“系统卷标信息”。这个文件夹里就存储着系统还原的备份信息。
gui0605 2007-04-20
  • 打赏
  • 举报
回复
这个不太清楚了、、、
liangfengxx 2007-04-20
  • 打赏
  • 举报
回复
To:hertcloud(·£孙子兵法£·)
我写的是winForm程序。

我也考虑过像CSDN那样,点击一个节点,再加载其下面的节点数据,但是在winForm里面不像在网页里加载数据可以用ajax或其他脚本语句。我是想判断某一个文件夹下面是否有文件子文件夹,然后决定该文件夹前面的符号是否是"+"号(即是否可以展开),但一直没有找到一个好的方法。

另外,当我循环遍历硬盘中的文件的时候,出现了UnauthorizedAccessException(声明:我是以管理员身份登录计算机的),不知道大家遇到过这种情况没有?还有一点就是,每个盘符下都有system volumn information文件夹,但是打开盘符并没找到该文件夹(隐藏文件夹里也没有),我想问一下,这个system volumn information是什么东东?从字面上看应该是包含系统卷标的信息,还请达人指点!
hertcloud 2007-04-20
  • 打赏
  • 举报
回复
一次性 将所有的 数据 导入到页面 肯定会慢
楼主可以使用 ajax部分加载 就像CSDN论坛的树一样 点击 一个节点 再加载其下的节点数据
ajax和c#.net相结合的树型菜单
http://blog.csdn.net/zdyguilong/archive/2007/02/07/1504549.aspx
liangfengxx 2007-04-20
  • 打赏
  • 举报
回复
没有全部展开,但TreeView本来就加载了所有文件和文件夹,不关展不展开的事.
hbyelang 2007-04-20
  • 打赏
  • 举报
回复
学习
egyqy 2007-04-20
  • 打赏
  • 举报
回复
刚开始不要全部展开啊
加载更多回复(1)

16,554

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧