Listado recursivo en VB. NET 2017

Listar de forma recursiva todos los archivos de un directorio y de los subdirectorios que contiene, informando del tiempo requerido para la tarea, con botones para ordenar la lista de forma ascendente o descendente, en Visual Studio 2017

Damián Castroviejo tiene un ejercicio titulado “How to use a recursive function to read a directory structure and save it to XML” cuyo código consigue listar adecuadamente de forma recursiva los archivos de un directorio y de sus subdirectorios y guardar la estructura de archivos y carpetas en un archivo XML. Aplicando la función recursiva propuesta por Damián Castroviejo, en este ejercicio se rellena un control ListBox con todos los archivos existentes en una carpeta determinada.

El ejercicio tiene estas características:

  • seleccionar una carpeta y rellenar un control Listbox con todos los archivos que contiene, incluyendo los contenidos en las subcarpetas
  • informar al usuario del tiempo que se ha tardado en rellenar el control ListBox utilizando la propiedad Environment.TickCount, lo que equivale a una medición sencilla del rendimiento de esa tarea
  • ordenar la lista de archivos de forma ascendente, descendente o buscando una extensión concreta.

Inicio del programa

Para que el formulario arranque centrado en la pantalla en horizontal pero ocupando el área visible en vertical excepto un pequeño espacio de 140px se emplean este código:

Me.Left = (Screen.PrimaryScreen.WorkingArea.Width - Me.Width) \ 2
Me.Height = (Screen.PrimaryScreen.WorkingArea.Height) - 140
Me.Width = 535

Mediante un cuadro de diálogo de selección de carpeta (FolderBrowserDialog) se elige el directorio cuyos archivos (y los de las subcarpetas) se van a listar.

' PROCEDIMIENTO QUE SE ENCARGA DE ELEGIR EL DIRECTORIO
Private Sub AbrirCarpeta()
    ' Cuadro de diálogo de selección de carpeta (FolderBrowserDialog)
    If folderDlg.ShowDialog = Windows.Forms.DialogResult.OK Then
	' strPath contiene la ruta al directorio elegido y strCarpeta una matriz con sus archivos
	strPath = folderDlg.SelectedPath
    End If
    strCarpeta = System.IO.Directory.GetFiles(strPath)
    '
    Me.Text = "Dir2Files"
    ' Mostrar la ruta completa al directorio 
    lblList.Text = "Directorio seleccionado: " + strPath + ""
    '
End Sub

Rellenar el ListBox

Rellenar el control ListBox llamando a la función recursiva ListarArchivos1 a la que se le pasan 2 parámetros String, el nombre del directorio actual que cambia en cada pasada recursiva y la ruta completa al directorio actual. El ListBox se rellena sólo con nombres de archivo, sin carpetas. Al empezar la serie, ambas rutas coinciden (nombre y ruta del directorio actual).

Un bucle ForEach recorre cada carpeta del directorio para obtener sus archivos. La función ListarArchivos1 es llamada de nuevo dentro de su propio código (por ello es recursiva) para obtener los archivos de las subcarpetas que puede haber dentro de cada carpeta. Otro bucle ForEach obtiene la lista de archivos, en este caso la función no necesita ser recursiva ya que los archivos no contienen otros archivos dentro de ellos mismos.

Por otro lado se emplea Environment.TickCount que devuelve el nº de milisegundos desde que se ha iniciado Windows. Basado en ello, calculando la propiedad TickCount al comienzo y al final del procedimiento vemos su duración. Es una forma poco elaborada de comparar el rendimiento de procedimientos diferentes.

' FUNCION RECURSIVA QUE RELLENA EL CONTROL LISTBOX SÓLO CON NOMBRES DE ARCHIVOS
' A LA FUNCIÓN SE LE PASAN 2 PARÁMETROS:
'- NOMBRE DEL DIRECTORIO ACTUAL, CAMBIA EN CADA PASADA RECURSIVA
'- RUTA COMPLETA AL DIRECTORIO ACTUAL
Private Function ListarArchivos1(ByVal s As String, ByVal strPath As String) As Object
    ' Variables para contener información sobre archivos y carpetas
    Dim d As DirectoryInfo
    Dim ofs As New DirectoryInfo(strPath + "\")
    Dim oFile As FileInfo
    '
    ' Bucle For Each para las carpetas: obtener todas las carpetas contenidas en el directorio seleccionado
    For Each d In ofs.GetDirectories
	' Llamada recursiva (ListarArchivos1 es llamada desde dentro de ListarArchivos1)
	' Porque cada carpeta puede tener a su vez subcarpetas que también han de ser analizadas
	ListarArchivos1(d.Name, strPath + "\" + d.Name)
    Next
    '
    ' Bucle For Each para los archivos: obtener todos los archivos existentes en cada una de las carpetas y añadirlos al ListBox
    ' Este bucle no necesita llamadas recursivas porque un archivo no tiene subarchivos que recorrer
    For Each oFile In ofs.GetFiles
	' Distintas formas de mostrar la lista de archivos en el control ListBox
	'listBox.Items.Add((listBox.Items.Count + 1) + ". " + oFile.Name)
	'listBox.Items.Add(oFile.Name + " [" + (listBox.Items.Count + 1) + "]")
	listBox.Items.Add(oFile.Name)
    Next
End Function
Private Sub BtListBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtListBox.Click
	' Environment.TickCount devuelve el nº de milisegundos desde que se ha iniciado Windows
	' Calculando la propiedad TickCount al comienzo y al final del procedimiento vemos su duración
	' Es una forma poco elaborada de comparar el rendimiento de procedimientos diferentes
	inicio = System.Environment.TickCount
	'
	' Rellenar el control ListBox llamando a la funcion recursiva ListarArchivos1
	' a la que se le pasan 2 parámetros String:
	'- nombre del directorio actual, cambia en cada pasada recursiva
	'- ruta completa al directorio actual
	' Al empezar la serie, ambas rutas coinciden (nombre y ruta del directorio actual)
	listBox.Items.Clear()
	ListarArchivos1(strPath + "\", strPath + "\")
	'
	final = System.Environment.TickCount
	' Información del tiempo empleado en rellenar el control ListBox
	MsgBox("Tiempo utilizado en el proceso: " + CStr(((final - inicio) / 1000)) + " segundos.")
    End If
End Sub

Ordenar la lista de forma ascendente o descendente

El proceso de ordenar un ListBox es complejo por lo que se recurre a este “truco”:

  1. utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox
  2. volcamos el contenido del ListBox al ArrayList
  3. vaciamos el ListBox
  4. ordenamos el ArrayList
  5. lo volcamos al ListBox con los elementos ordenados.

Volvemos a emplear Environment.TickCount para saber el tiempo que ha tardado la tarea.

' ORDENAR EL CONTROL LISTVIEW USANDO UN ARRAYLIST
Private Sub OrdenarLista1(ByRef listBox As ListBox)
    Dim matriz As New ArrayList
    Dim indice As Integer
    '
    inicio = System.Environment.TickCount
    '
    For indice = 0 To listBox.Items.Count - 1
	'matriz.Add(CStr(listBox.Items(indice)))
	matriz.Add(listBox.Items(indice))
    Next
    matriz.Sort()
    listBox.Items.Clear()
    '
    If b = True Then
	' ordenar de manera ascendente
	For indice = matriz.Count - 1 To 0 Step -1
	    listBox.Items.Add(matriz(indice))
	Next
    Else
	' ordenar de manera descendente
	For indice = 0 To matriz.Count - 1
	    listBox.Items.Add(matriz(indice))
	Next
    End If
    '
    final = System.Environment.TickCount
    MsgBox("Tiempo utilizado en el proceso: " + CStr(((final - inicio) / 1000)) + " segundos.")
End Sub

Seleccionar los archivos por extensión

Una vez relleno el ListBox se puede elegir una extensión para que se muestren sólo los archivos que tengan esa extensión en su nombre. Primero se rellena el ListBox con la lista completa. Después utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox:

  1. pasamos al ArrayList los elementos del ListBox cuyo nombre termina en la extensión buscada
  2. vaciamos el ListBox
  3. volcamos el ArrayList al ListBox con los elementos encontrados.
' LISTAR LOS ARCHIVOS CON UNA EXTENSIÓN DETERMINADA
Private Sub BtExtension_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btExt1.Click
    ' Si el cuadro de texto txtExt1 no está vacío
    If txtExt1.Text.Length > 0 Then
	'
	inicio = System.Environment.TickCount
	'
	' rellenar el ListBox (para buscar siempre en la lista completa y no
	' en la de la extensión que se haya usado previamente)
	listBox.Items.Clear()
	ListarArchivos1(strPath + "\", strPath + "\")
	j = listBox.Items.Count
	'
	' Utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox
	' - pasamos al ArrayList los elementos del ListBox cuyo nombre termina en la extensión buscada
	' - vaciamos el ListBox
	' - volcamos el ArrayList al ListBox con los elementos encontrados
	Dim matriz As New ArrayList
	Dim indice As Integer
	'
	For indice = 0 To listBox.Items.Count - 1
	    If CStr(listBox.Items(indice)).EndsWith("." + txtExt1.Text, StringComparison.CurrentCultureIgnoreCase) Then
		matriz.Add(CStr(listBox.Items(indice)))
	    End If
	Next
	listBox.Items.Clear()
	'
	For indice = 0 To matriz.Count - 1
	    listBox.Items.Add(matriz(indice))
	Next
	'
	final = System.Environment.TickCount
	MsgBox("Tiempo utilizado en el proceso: " + CStr(((final - inicio) / 1000)) + " segundos.")
	'
    Else
	If a = True Then
	    MsgBox("Falta la extensión para la búsqueda", MsgBoxStyle.Information)
	End If
    End If
End Sub

Información en la barra del formulario

En todos los pasos del programa mostramos en la barra de título información sobre el nº de archivos mostrados:

' Información en la barra de título
 j = listBox.Items.Count
 Me.Text = "Archivos ." + txtExt1.Text + ": " + CStr(j)

Aplicación completa en Visual Studio 2017.

Nota: el ajuste automático de escala del formulario que muestra la ventana de la aplicación se puede configurar de varias maneras, lo tienes en las propiedades del formulario como AutoScaleMode, la opción con la que obtengo el mejor resultado es con Dpi.

2 respuestas a “Listado recursivo en VB. NET 2017”

  1. Veo que usas el signo + en vez de & para concatenar cadenas, sé que ambos son válidos pero ¿por qué motivo prefieres +?

  2. He usado & durante muchos años ya que era la tradición de Visual Basic pero al poner fragmentos de código en el blog he observado que WordPress formatea algunos caracteres (como hace con & por ejemplo) y da menos problemas mostrar el carácter + que & en esos fragmentos de código. Ese sería el principal motivo.

    Pero muchos programadores expertos recomiendan usar + por similitud con tantos lenguajes que así lo hacen, como C# y Java, por ejemplo.

    NOTA: si se usa + para concatenar cadenas hay que tener en cuenta que si se une una cadena y un número (por ej: “cadena” + 4) hay que hacer conversión del número para que no de error de compilación (ej: “cadena” + Cstr(4)).

Deja un comentario