分享
 
 
 

微软风格的Explore

王朝厨房·作者佚名  2007-01-04
窄屏简体版  字體: |||超大  

尽管Visual Basic标准控件DriveListBox、DirListBox和FileListBox在一些环境中能很好使用,但若想显示类似Windows Explorer的文件夹结构就显得力不从心了。这时就必须使用TreeView控件,因为它为文件、文件夹和驱动器显示提供了完善的解决方案,并且系统的FileSystemObject对象能有效地获得主机的目录信息。

本文将介绍如何使用FileSystemObject将指定的目录或全部的驱动器与TreeView控件相结合,以及如何消除当驱动器全部文件列表填充TreeView时所遇到的缺陷。作为技巧,文中还讨论如何用SendMessage()的API函数来对TreeView内容的快速清除。事实上,这是一种最快捷的方法,它也可用于其他控件中。

关于FileSystemObject对象库

众所周知,FileSystemObject对象库是在Visual Basic Scripting Edition 2.

中发布的,它包括一系列用于驱动器、文件夹和文件的操作。为了能在Visual Basic中使用FileSystemObject,必须引用SCRRUN.DLL文件以便能使用它的属性、方法和事件。在VB工程中添加SCRRUN.DLL的引用,是通过选择"Project"(工程)->"References"(引用)命令,然后选定"Microsoft Scripting Runtime"项来进行的。一旦引用后,就可用FileSystemObject方法获得文件夹名称以及每个文件夹下的文件名。

FileSystemObject允许用户在当前磁盘中进行文件操作,如复制、删除以及其他文件夹、文件操作等。本文着重讨论与文件夹结构相关的方法,即如何获得文件夹和文件名称。

创建示例

示例创建的过程是这样的,首先在默认的表单中添加必要的控件;然后添加代码,用来将目录内容添加在TreeView中;最后,利用SendMessageLong() API函数添加一个过程,用来清除TreeView中的内容。

创建用于文件和文件夹的表单

首先,创建一个新的Visual Basic工程;然后,将默认的表单标题设置为"Exploring the Windows' directory",再在表单上添置一个TreeView控件和一个命令按钮控件,保留默认名称。最后,将命令按钮的标题设置为"Fill TreeView"。

声明两个重要的变量

在这一步中,我们需要为表单添加一些代码。首先,需要安装SCRRUN.DLL的引用,从菜单栏上选择"Project"(工程)->"References"(引用)命令,在弹出的对话框中,选中"Microsoft Scripting Runtime"项。单击[OK]按钮关闭对话框。然后,在表单中右击鼠标,从弹出的快捷菜单中选择"View Code"命令,在声明部分添加下列语句:

Const DEFAULT_DRIVE = "C:\"

Dim FSO As New Scripting.FileSystemObject

这里为简单起见,我们用一个常量来指定建立的目录树的默认驱动器。当然,最好能让用户对这个值作出选择。

用户或许想知道为什么在表单层中声明一个FSO变量,而不是在实际读取文件信息的过程中声明。这完全是一次偶然机会的心得,因为在许多好的程序中,通常允许用户用多种方式处理选择的文件。在表单层范围内定义一个FSO变量,其目的是可以处理所有其他FileSystemObject成员,而不必每次都有定义一个对象变量。

基于这种考虑,在表单被调用时应对FSO变量进行初始化,而当表单关闭后,该变量应被清除。这项工作的最终完成是通过下面的代码:

Private Sub Form_Load()

Set FSO = New FileSystemObject

End Sub

Private Sub Form_Unload(Cancel As Integer)

Set FSO = Nothing

End Sub

添加命令按钮Click()的事件

下面来添加代码用于TreeView控件的填充,这个过程是通过命令按钮的Click()事件来处理的,具体的代码如Listing A所示。代码中,一开始是定义几个变量,然后判断TreeView控件是否已经包含默认驱动器下的节点内容。若是,则不需要重新填充TreeView控件。若不是,则根据已知的节点创建新的节点变量,并连同TreeView控件一起调用PopulateTreeView()过程。显然,这个定制的子程序需要两个参数,一个是要填充的TreeView对象,另一个是要创建的目录树的父节点。PopulateTreeView()过程将在后面进行详细讨论。

Listing A:

Private Sub Command1_Click()

Dim strRootDir As String

Dim ndRoot As Node

strRootDir = UCase(DEFAULT_DRIVE)

On Error Resume Next

Set ndRoot = TreeView1.Nodes(strRootDir)

On Error GoTo

If ndRoot Is Nothing Then

Set ndRoot = TreeView1.Nodes.Add(, , strRootDir, _

strRootDir)

ndRoot.Sorted = True

PopulateTreeview TreeView1, ndRoot

End If

Set ndRoot = Nothing

End Sub

消除长时间等待的缺陷

在这一步中,我们需要考虑到定制的过程必须能从默认的目录中读取完整的文件信息并能填充到TreeView控件中。在磁盘大小越来越大的今天,这种方式却是非常不明智的。为了能得到目录的文件列表,Visual Basic必须在给定的文件夹下不断循环搜索所有的文件和子文件夹,当发现一个另外的子文件夹时,又要开始同样的循环,直到最后一个文件被找到为止。用这种方式读取全部的目录信息必然使程序挂起许多分钟。如果假设读取的磁盘,不仅仅是主机上的,而且还有网络上的,那么Visual Basic可能要耗上几个小时的时间。

作为技巧,我们在获取文件信息采用"即需即用"的原则。也就是说,只将在用户选定的文件夹的目录树的信息填充到TreeView控件中。是啊,一个从来就没有被选定的内容又怎么能去填充呢?

也就是说,在TreeView控件中,只有当用户展开一个文件夹项时,代码才填充该文件夹下的目录信息。基于这种思想,在TreeView控件的Expand()事件中必须调用定制的PopulateTreeView()过程,如下面的代码:

Private Sub TreeView1_Expand(ByVal Node _As MSComctlLib.Node)

PopulateTreeview TreeView1, Node

End Sub

这样,当Visual Basic调用该过程时,它处理被选中的节点,理论上包含能建立其他文件结构的目录。现在我们在这里提供相应的代码,用来添加到PopulateTreeView()过程中。

增加PopulateTreeView()程序

这一步,我们准备添加定制的PopulateTreeView()过程。前面已提及,我们的这个子程序是通过其中一个父节点参数创建相应的目录结构。但是,我们每次向TreeView添加的目录应该包含多少层呢?如果代码在所有的嵌套子文件夹中不断循环,这必然有潜在的缺陷,因为获取所有的硬盘文件结构信息必然需要大量的时间。另一方面,我们的代码也不能简单地在一个过程查找当前子文件夹下的文件信息。

为了能让TreeView控件显示子目录节点旁的"+"、"-"号,我们的代码必须填充其子项内容。但实际上,尽管用户可能仅仅选择显示C驱动器的目录结构,但我们的子程序还必须能添加所有可见的子目录内容。幸运的是,这第二层的内容仍然可以用代码去填充到TreeView控件中去。Listing B 是定制的PopulateTreeView()过程的全部代码:

Listing B:

Sub PopulateTreeview(trvw As TreeView, ndParent As Node)

Dim fldrParent As Folder

Dim fldrChildren As Folders

Dim FoundFile As Files

Dim FoundDir As Folder

Dim FoundFiles As Files

Dim iDir As Integer, iFile As Integer

Dim ndChild As Node

Dim blnHasChildren As Boolean

Set fldrParent = FSO.GetFolder(ndParent.Key)

Set fldrChildren = fldrParent.SubFolders

Set FoundFiles = fldrParent.Files

blnHasChildren = CBool(ndParent.Children)

With trvw

For Each FoundDir In fldrChildren

If blnHasChildren Then

Set ndChild = .Nodes(FoundDir.Path)

Else

Set ndChild = .Nodes.Add(ndParent.Key, _ tvwChild, FoundDir.Path, FoundDir.Name)

ndChild.Sorted = True

End If

If ndParent.Expanded Then

PopulateTreeview trvw, ndChild

End If

Next FoundDir

For Each FoundFile In FoundFiles

On Error Resume Next

Set ndChild = .Nodes(FoundFile.Path)

On Error GoTo

If ndChild Is Nothing Then

.Nodes.Add FoundFile.ParentFolder.Path, _tvwChild, FoundFile.Path, FoundFile.Name

End If

Next FoundFile

End With

Set ndChild = Nothing

Set fldrParent = Nothing

Set fldrChildren = Nothing

Set FoundFiles = Nothing

End Sub

在不工作中填充TreeView的子项

要查看这个定制过程的结果,按[F5]来运行这个工程。当Visual Basic显示默认的表单时,单击[Fill TreeView]按钮。此时,产生Click()事件,并运行PopulateTreeView()过程,处理TreeView1和ndRoot节点(此时是C:)。然后,该过程得到一个基于ndParent参数的文件夹对象以及该文件夹下的子文件夹。这时,代码循环所有的子文件夹。如果代码查找到父节点存在相应的子项内容,则代码为该父节点添加子文件夹,然后该过程为TreeView项目添加一个子节点。如果当前父节点在TreeView中没有子项,但是与父节点相关的父文件夹包含子文件夹,则代码向TreeView添加新的文件夹名。

然后,该过程判断父节点是否展开。若是,则代码添加相应的子文件夹内容,这样在TreeView层次上出现"+"、"-"号。为了达到这一点,后面调用的PopulateTreeView()就是用子节点来处理的。在第二次调用这个过程时,由于子节点没有展开,因此PopulateTreeView()过程不会再被调用。最后,该过程在该文件夹下循环所有的文件,并将其填充到TreeView中。

快速清除TreeView控件中的内容

在这个示例中,用户可以通过单击命令按钮在TreeView控件中添加文件夹和文件列表。在实际的应用程序中,我们应该可以让用户通过相应的控件选择不同的开始目录。因此,在用新项目填充TreeView控件之前,还必须删除该控件中的原有内容。通常,我们可以使用递归的方式来遍历TreeView控件中的所有节点,然后删除相应的项目,但是却要花费不少时间,尤其是当TreeView控件含有更多的项目和嵌套时。一个最简单快捷的方法,是使用SendMessage() API函数,在一次循环后就可将TreeView控件的内容删除。

为了说明它的工作原理,我们返回到默认表单的设计视图中,再在表单中添加一个命令按钮,打开表单的代码窗口,在声明部分添加如Listing C所示的API声明。

Listing C:

Const TV_FIRST As Long = &H11

Const TVM_GETNEXTITEM As Long = (TV_FIRST + 1)

Const TVM_DELETEITEM As Long = (TV_FIRST + 1)

Const TVGN_ROOT As Long = &H

Const WM_SETREDRAW As Long = &HB

Private Declare Function SendMessageLong Lib "user32" _

Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg _

As Long, ByVal wParam As Long, ByVal lParam As Long) _

As Long

然后,添加Command2的Click()事件,并增加下列代码:

Private Sub Command2_Click()

'Remove all items from the Treeview

ClearTreeView TreeView1.hWnd

End Sub

最后,建立一个ClearTreeView()子程序,如Listing D所示。

Listing D:

Private Sub ClearTreeView(ByVal tvHwnd As Long)

Dim lNodeHandle As Long

'Turn off redrawing first

SendMessageLong tvHwnd, WM_SETREDRAW, False,

'Remove each node in the treeview

Do

lNodeHandle = SendMessageLong(tvHwnd, _

TVM_GETNEXTITEM, TVGN_ROOT,

)

If lNodeHandle Then

SendMessageLong tvHwnd, TVM_DELETEITEM,, _lNodeHandle

Else

Exit Do

End If

Loop

SendMessageLong tvHwnd, WM_SETREDRAW, True,

End Sub

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有