Introducción:
Este es el código de Visual Basic para el formulario principal de
la
utilidad gsBuscarTexto.
'------------------------------------------------------------------------------
' gsBuscarTexto (07/Dic/07)
' Buscar ficheros según la especificación indicada y buscar texto en esos ficheros
'
' Basado en gsBuscar
'
' ©Guillermo 'guille' Som, 2007
'------------------------------------------------------------------------------
Option Strict On
Option Infer Off
Imports Microsoft.VisualBasic
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.IO
Imports System.Diagnostics
Imports System.Collections.Generic
Public Class Form1
Private cancelar As Boolean = False
Private fechaAsignada As String
Private dirExaminados As New List(Of String)
''' <summary>
''' Asignar a los datos de configuración
''' los contenidos de los controles y demás preferencias.
''' </summary>
''' <remarks></remarks>
Private Sub guardarConfig()
With My.Settings
' Los elementos de los combos se guardan como una cadena
' en la que se separa cada elemento de los combos con una barra vertical,
' esas cadenas las devuelven las propiedades que tiene el control de usuario.
.ultimosDirectorios = OpcionesBuscar1.Directorios
.ultimosFiltros = OpcionesBuscar1.Filtros
.ultimosBuscar1 = OpcionesBuscar1.Buscar1
.ultimosBuscar2 = OpcionesBuscar1.Buscar2
.ultimosPoner1 = OpcionesBuscar1.Poner1
.ultimosPoner2 = OpcionesBuscar1.Poner2
' Los valores individuales de cada opción
.Directorio = OpcionesBuscar1.cboDir.Text
.Filtro = OpcionesBuscar1.cboFiltro.Text
.conSubDir = OpcionesBuscar1.chkConSubDir.IsChecked.Value
.IgnorarError = OpcionesBuscar1.chkIgnorarErrores.IsChecked.Value
.textoBuscar1 = OpcionesBuscar1.cboBuscar1.Text
.textoBuscar2 = OpcionesBuscar1.cboBuscar2.Text
.textoPoner1 = OpcionesBuscar1.cboPoner1.Text
.textoPoner2 = OpcionesBuscar1.cboPoner2.Text
.opcionBuscar = OpcionesBuscar1.cboTipoBuca.Text
.chkFecha = OpcionesBuscar1.chkFecha.IsChecked.Value
.chkBuscarTexto = OpcionesBuscar1.chkBuscar.IsChecked.Value
.chkPoner = OpcionesBuscar1.chkPoner.IsChecked.Value
' Solo asignar la fecha si se ha cambiado desde que se asignó al iniciar
' (cuando está vacio el valor, se usa la fecha actual)
If String.IsNullOrEmpty(OpcionesBuscar1.txtFecha.Text) = False _
AndAlso fechaAsignada.CompareTo(OpcionesBuscar1.txtFecha.Text) <> 0 Then
.Fecha = OpcionesBuscar1.txtFecha.Text
End If
' Guardar la visibilidad de los Expander
.expanderBuscarExpanded = OpcionesBuscar1.expanderBuscar.IsExpanded
.expanderFicheroExpanded = OpcionesBuscar1.expanderFichero.IsExpanded
' Guardar el tamaño y posición del formulario,
' si está maximizado se guarda el valor de RestoreBounds
If Me.WindowState = FormWindowState.Normal Then
.Location = Me.Location
.Size = Me.Size
Else
.Location = Me.RestoreBounds.Location
.Size = Me.RestoreBounds.Size
End If
' En Visual Basic (de forma predeterminada) no hace falta guardar los datos de configuración,
' pero por si se cambia en las propiedades del proyecto
.Save()
End With
End Sub
''' <summary>
''' Al cerrarse el formulario principal,
''' se guadan los valores de configuración.
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub Form1_FormClosing(ByVal sender As Object, _
ByVal e As FormClosingEventArgs) _
Handles Me.FormClosing
guardarConfig()
Me.OpcionesBuscar1.EnEjecucion = False
End Sub
''' <summary>
''' Al cargarse (mostrarse) el formulario principal,
''' asignamos los valores de la configuración e inicializamos
''' el control de usuario
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles MyBase.Load
Me.OpcionesBuscar1.EnEjecucion = True
' Asignar el manejador del evento ExpanderExpandedChanged
AddHandler OpcionesBuscar1.ExpanderExpandedChanged, _
AddressOf OpcionesBuscar1_ExpanderExpandedChanged
With My.Settings
' Si es la primera vez, tendrá un valor -1
' por tanto, solo asignar la posición y tamaño si ya se ha ejecutado antes
If .Location.X > -1 Then
Me.Location = .Location
Me.Size = .Size
End If
' Asignar los elementos de los combos por medio de las propiedades
' definidas en el control de usuario
OpcionesBuscar1.Directorios = .ultimosDirectorios
OpcionesBuscar1.Filtros = .ultimosFiltros
OpcionesBuscar1.Buscar1 = .ultimosBuscar1
OpcionesBuscar1.Buscar2 = .ultimosBuscar2
OpcionesBuscar1.Poner1 = .ultimosPoner1
OpcionesBuscar1.Poner2 = .ultimosPoner2
'----------------------------------------------------------
' Como aún no está toda la funcionalidad no implementada,
' ocultamos o deshabilitamos los controles que no se deben
' tener en cuenta.
'----------------------------------------------------------
' Esta opción aún no está implementada
.expanderBuscarExpanded = False
.chkBuscarTexto = False
OpcionesBuscar1.chkBuscar.IsEnabled = False
.textoBuscar1 = ""
.textoBuscar2 = ""
' Esta opción aún no está implementada
.chkPoner = False
OpcionesBuscar1.chkPoner.Tag = False
OpcionesBuscar1.chkPoner.IsEnabled = False
.textoPoner1 = ""
.textoPoner2 = ""
' Esta opción aún no está implementada
.chkFecha = False
OpcionesBuscar1.chkFecha.IsEnabled = False
' Asignamos el resto de opciones
OpcionesBuscar1.cboDir.Text = .Directorio
OpcionesBuscar1.cboFiltro.Text = .Filtro
OpcionesBuscar1.chkConSubDir.IsChecked = .conSubDir
OpcionesBuscar1.chkIgnorarErrores.IsChecked = .IgnorarError
OpcionesBuscar1.cboTipoBuca.Text = .opcionBuscar
OpcionesBuscar1.cboBuscar1.Text = .textoBuscar1
OpcionesBuscar1.cboBuscar2.Text = .textoBuscar2
OpcionesBuscar1.cboPoner1.Text = .textoPoner1
OpcionesBuscar1.cboPoner2.Text = .textoPoner2
OpcionesBuscar1.chkBuscar.IsChecked = .chkBuscarTexto
OpcionesBuscar1.chkPoner.IsChecked = .chkPoner
OpcionesBuscar1.chkFecha.IsChecked = .chkFecha
' Si la fecha está vacía, usar la fecha actual
If String.IsNullOrEmpty(.Fecha) Then
OpcionesBuscar1.txtFecha.Text = DateTime.Now.ToShortDateString
Else
OpcionesBuscar1.txtFecha.Text = .Fecha
End If
' para saber si inicialmente .Fecha estaba vacio
fechaAsignada = OpcionesBuscar1.txtFecha.Text
OpcionesBuscar1.expanderBuscar.IsExpanded = .expanderBuscarExpanded
OpcionesBuscar1.expanderFichero.IsExpanded = .expanderFicheroExpanded
End With
With My.Application.Info
Me.LabelInfo.Text = .Title & " v" & .Version.ToString & " - " & .Copyright
End With
' Los dos botones de abrir fichero o directorio
' solo deben estar habilitados si hay datos en el ListView
Me.btnAbrirDir.Enabled = False
Me.btnAbrirFic.Enabled = False
Me.mnuFicAbrirFichero.Enabled = btnAbrirFic.Enabled
Me.mnuFicAbrirDir.Enabled = btnAbrirDir.Enabled
End Sub
''' <summary>
''' Cuando se pulsa en el botón de buscar.
''' Si ya se está buscando, se muestra cancelar,
''' con idea de cancelar la búsqueda (además de evitar la reentrada).
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnBuscar_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnBuscar.Click, mnuFicBuscar.Click
' Buscar de forma recursiva (si es necesario)
Static yaEstoy As Boolean
If yaEstoy Then
cancelar = True
Me.btnBuscar.Text = "Cancelando..."
Application.DoEvents()
Exit Sub
End If
yaEstoy = True
' Actualizar los contenidos de los combos
' (esto es para que se compruebe si el texto de cada combo
' está o no en la lista de elementos, si no está, se añade)
OpcionesBuscar1.ActualizarCombos()
' Guardar los datos de configuración y estado de la ventana
guardarConfig()
Me.lvFics.Items.Clear()
Me.LabelInfo.Text = "Buscando los ficheros..."
Me.Cursor = Cursors.AppStarting
Me.btnBuscar.Text = "Cancelar"
Me.Refresh()
' Esta colección se usará para saber los directorios examinados
' con idea de que no se comprueben más de una vez
' (por ejemplo si se indica el mismo directorio más de una vez)
dirExaminados.Clear()
' Como se pueden indicar varios directorios,
' creamos un array con cada uno de los valores que haya
' (esos valores estarán separados por punto y coma)
Dim dirs() As String
dirs = My.Settings.Directorio.Split(";".ToCharArray, StringSplitOptions.RemoveEmptyEntries)
For Each d As String In dirs
' Antes de hacer nada, comprobar si se ha cancelado
Application.DoEvents()
If cancelar Then Exit For
Try
' No comprobar los directorios vacios
' (por si se pone más de un punto y coma)
If String.IsNullOrEmpty(d.Trim()) Then Continue For
' Crear un objeto del tipo DirectoryInfo con el directorio a examinar
Dim di As New DirectoryInfo(d.Trim())
' Si no existe el directorio, pues nada que hacer
' aunque puede que no exista porque se haya indicado un fichero
' en ese caso, el valor de di.Attribute será FileAttributes.Archive
If di.Exists = False _
AndAlso di.Attributes = FileAttributes.Archive Then
di = New DirectoryInfo(Path.GetDirectoryName(di.FullName))
End If
If di.Exists Then
' ¡Empieza el espectáculo!
' Recorrer cada uno de los directorios principales indicados
' en este método se comprueba si se debe buscar en los subdirectorios.
recorrerDir(di)
End If
Catch ex As Exception
' Si se produce un error, mostrarlo
' y preguntar si se quiere cancelar.
If MessageBox.Show("Error: " & ex.Message & vbCrLf & _
"¿Quieres cancelar la búsqueda?", _
"Buscar ficheros", _
MessageBoxButtons.YesNo, _
MessageBoxIcon.Exclamation _
) = System.Windows.Forms.DialogResult.Yes Then
Exit For
End If
End Try
Next
' Mostrar el resultado de la búsqueda
Me.Cursor = Cursors.Default
Me.btnBuscar.Text = "Buscar"
' Los botones de abrir fichero o directorio
' solo habilitarlos si hay algo en el ListView
' La asignación de los ficheros hallados se hace en esta comprobación,
' para mostrar un mensaje más adecuado al no hallar ficheros
If Me.lvFics.Items.Count > 0 Then
Me.LabelInfo.Text = "Se han hallado " & Me.lvFics.Items.Count.ToString("#,###") & " ficheros"
Me.btnAbrirDir.Enabled = True
Me.btnAbrirFic.Enabled = True
Me.mnuFicAbrirFichero.Enabled = btnAbrirFic.Enabled
Me.mnuFicAbrirDir.Enabled = btnAbrirDir.Enabled
Else
Me.LabelInfo.Text = "No se han hallado ficheros."
End If
Me.Refresh()
cancelar = False
yaEstoy = False
End Sub
''' <summary>
''' Método recursivo para buscar en cada directorio,
''' se añadirá al ListView los ficheros hallados.
''' Aquí se comprueba si se debe seguir buscando en los subdirectorios,
''' si es así, se vuelve a llamar a este método.
''' </summary>
''' <param name="di">
''' Un objeto DirectoryInfo con el directorio a comprobar
''' </param>
''' <remarks></remarks>
Private Sub recorrerDir(ByVal di As DirectoryInfo)
' Antes de hacer nada, comprobar si se ha cancelado
Application.DoEvents()
If cancelar Then Exit Sub
' Añadir al directorio a comprobar a la lista de directorios
' pero solo si no estaba ya.
If dirExaminados.Contains(di.FullName.ToLower()) Then
Exit Sub
End If
dirExaminados.Add(di.FullName.ToLower())
Me.LabelInfo.Text = di.FullName & "..."
Me.LabelInfo.Refresh()
' Las especificaciones de los ficheros a buscar estarán separadas por puntos y comas,
' crear un array con cada una de esas especificaciones.
Dim especs() As String
especs = My.Settings.Filtro.Split(";".ToCharArray, StringSplitOptions.RemoveEmptyEntries)
Dim listaFics As New List(Of FileInfo)
Try
For Each espec As String In especs
' Asignar al array los ficheros de este directorio
' que concurdan con el filtro (especificación) indicada
Dim fics() As FileInfo = di.GetFiles(espec.Trim(), SearchOption.TopDirectoryOnly)
' Si hay ficheros coincidentes,
' agregarlos a la colección.
' Esto es para después usar lo que haya en la colección,
' de esa forma, estarán todos los ficheros que coincidan
' con las especificaciones (filtros) indicados.
If fics.Length > 0 Then
listaFics.AddRange(fics)
End If
Next
' Añadir al ListView los ficheros que se hayan encontrado
With Me.lvFics
For Each fi As FileInfo In listaFics
Dim lvi As ListViewItem = .Items.Add(fi.Name)
lvi.SubItems.Add(fi.DirectoryName)
Next
End With
' Si se deben comprobar los subdirectorios
If My.Settings.conSubDir Then
' obtener todos los subdirectorios
' y examinarlos llamando recursivamente
' a este mismo método
Dim dirs() As DirectoryInfo = di.GetDirectories()
For Each dir As DirectoryInfo In dirs
recorrerDir(dir)
Next
End If
Catch ex As DirectoryNotFoundException
' Si el error es que no existe el directorio,
' mostrar el mensaje, independientemente del valor de la opción de ignorar errores
If MessageBox.Show("Error, el directorio '" & di.FullName & "' " & vbCrLf & _
"no existe." & vbCrLf & _
"¿Quieres continuar?" & vbCrLf & _
"(pulsa NO para cancelar)", _
"Buscar en directorios", _
MessageBoxButtons.YesNo, _
MessageBoxIcon.Exclamation _
) = DialogResult.No Then
cancelar = True
Application.DoEvents()
End If
'Catch ex As IOException
' ' Este error se producirá si en lugar de un directorio
' ' se ha indicado un fichero
Catch ex As Exception
' Si se produce cualquier otro error y se ha indicado
' que se ignoren los errores, pues... se sigue...
' Normalmente esto se produce porque se accede a un fichero
' que está bloqueado o al que no se tiene autorización para accederlo.
If My.Settings.IgnorarError Then
Exit Sub
End If
' Si se llega aquí es porque no está marcada la opción de ignorar errores
' y se da la oportunidad de que el usuario decida lo que quiere hacer.
If MessageBox.Show("Error: " & ex.Message & vbCrLf & _
"¿Quieres continuar?" & vbCrLf & _
"(pulsa NO para cancelar)", _
"Buscar en directorios", _
MessageBoxButtons.YesNo, _
MessageBoxIcon.Exclamation _
) = DialogResult.No Then
cancelar = True
Application.DoEvents()
End If
End Try
End Sub
''' <summary>
''' Cuando se hace doble clic en un elemento del ListView
''' se comprueba si se ha pulsado en el nombre del fichero
''' o en el directorio, y así se hará una cosa u otra.
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub lvFics_MouseDoubleClick(ByVal sender As Object, _
ByVal e As MouseEventArgs) _
Handles lvFics.MouseDoubleClick
With lvFics
If .SelectedIndices.Count = 0 Then
Exit Sub
End If
' Comprobar en que columna se ha hecho doble clic
' El valor de e.X es relativo al control,
' por tanto, no hace falta añadir nada más.
If e.X < .Columns(0).Width Then
' El nombre
' Abrir el fichero indicado
' Combinar los paths para que se agregue el separador de directorio
' si así hiciera falta
Dim fic As String = Path.Combine(.SelectedItems(0).SubItems(1).Text, _
.SelectedItems(0).SubItems(0).Text)
Process.Start(fic)
Else
' El directorio
' Abrir la ventana con el directorio del fichero indicado
Dim dir As String = .SelectedItems(0).SubItems(1).Text
Process.Start("explorer.exe", dir)
End If
End With
End Sub
''' <summary>
''' Al pulsar en el botón Abrir Fichero,
''' abrir el fichero del elemento seleccionado en el ListView
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnAbrirFic_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnAbrirFic.Click, mnuFicAbrirFichero.Click
With lvFics
If .SelectedIndices.Count = 0 Then
Exit Sub
End If
' Abrir el fichero indicado
' Combinar los paths para que se agregue el separador de directorio
' si así hiciera falta
Dim fic As String = Path.Combine(.SelectedItems(0).SubItems(1).Text, _
.SelectedItems(0).SubItems(0).Text)
Process.Start(fic)
End With
End Sub
''' <summary>
''' Al pulsar en el botón Abrir directorio,
''' abrir el directorio del elemento seleccionado en el ListView
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnAbrirDir_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnAbrirDir.Click, mnuFicAbrirDir.Click
With lvFics
If .SelectedIndices.Count = 0 Then
Exit Sub
End If
' Abrir la ventana con el directorio del fichero indicado
Dim dir As String = .SelectedItems(0).SubItems(1).Text
Process.Start("explorer.exe", dir)
End With
End Sub
''' <summary>
''' Operaciones de arrastrar y soltar (drag & drop)
''' Esto es por si se suelta en cualquier parte del formulario
''' (no solo en el control de usuario)
''' </summary>
''' <remarks>
''' </remarks>
Private Sub txtDir_DragDrop(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles MyBase.DragDrop
If e.Data.GetDataPresent("FileDrop") Then
' Para asignar solo el fichero/directorio soltado
OpcionesBuscar1.cboDir.Text = CType(e.Data.GetData("FileDrop", True), String())(0)
End If
End Sub
Private Sub txtDir_DragEnter(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles MyBase.DragEnter
' Drag & Drop, comprobar con DataFormats
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
e.Effect = DragDropEffects.Copy
End If
End Sub
''' <summary>
''' Evento del control cuando se ocultan o muestran los expander
''' </summary>
''' <remarks>
''' Debido a que el diseñador de Windows.Forms algunas veces da error
''' al usar los controles WPF, la asignación del evento lo hago manualmente
''' </remarks>
Private Sub OpcionesBuscar1_ExpanderExpandedChanged() 'Handles OpcionesBuscar1.ExpanderExpandedChanged
' Actualizar el tamaño y la posición de lvFics según como estén los expanders
' Esto no es necesario, pero lo puse a ver porqué no daba los valores correctos...
'Me.OpcionesBuscar1.gridFichero.UpdateLayout()
'Me.OpcionesBuscar1.gridTexto.UpdateLayout()
' Calcular el alto, según esté visible o no cada grid de los expanders
Dim gH As Double = 0.0
If Me.OpcionesBuscar1.gridTexto.Visibility = Windows.Visibility.Visible Then
gH += Me.OpcionesBuscar1.gridTexto.ActualHeight
End If
If Me.OpcionesBuscar1.gridFichero.Visibility = Windows.Visibility.Visible Then
gH += Me.OpcionesBuscar1.gridFichero.ActualHeight
End If
' El valor 41 es porque el control tiene 215 de alto
' y he calculado el valor que habría que sumar...
' que algunas veces por la cuenta la vieja también funcionan las cosas, ;-)))
Dim opH As Integer = CInt(gH) + 41
' Los valores constantes son porque según el alto de 215 del control
' el lvFics tengrá .Top = 248 y .Height = 109
Me.lvFics.Top = 37 + opH
Me.lvFics.Height = Me.Height - opH - 137
lvFics.BringToFront()
End Sub
''' <summary>
''' Si se pulsa en la opción Salir del menú
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub mnuFicSalir_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles mnuFicSalir.Click
Me.Close()
End Sub
''' <summary>
''' La opción de examinar (elegir el directorio) se hace usando el código del control de usuario
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub mnuFicExaminar_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles mnuFicExaminar.Click
Me.OpcionesBuscar1.btnExaminarClick()
End Sub
End Class