Trucos y rutinas para
Visual Basic
(3ª parte)

Actualizado el 24/Oct/2006


La página con los links de TODOS los trucos


Contenido:

  1. Posicionar el cursor al final de una línea de texto
  2. Acceder a un control por la tecla rápida sin necesidad de pulsar ALT+letra.
  3. Para los que tenemos poca memoria... y VB5
  4. Cómo simular sobreescribir e insertar en un TextBox
  5. Limitar la entrada de un TextBox sólo a números (rectificada 21/Sep/01)
    Limitar la entrada de un TextBox sólo a números (incluso notación científica) (13/Jul/02)
  6. Justificar el contenido de un TextBox
  7. Mostrar los elementos de un ComboBox mientras se escribe
  8. Sincronizar el contenido de dos ListBox
  9. Activar la instancia anterior de una aplicación al cargarla por segunda vez
  10. Desplazar los elementos de un ListBox
  11. Hacer referencia a un control usando una variable
  12. Otro procedimiento para esperar X segundos
  13. Más sobre la colección Forms y Controls (hacer referencia a un control o form usando variables)
  14. Cómo pasar parámetros opcionales de un procedimiento a otro, usando ParamArray. (15/Mar/99)
  15. Usar la clase cNum2Text en una hoja de Excel (21/Ago/00)
  16. Crear controles en tiempo de ejecución (31/May/01)
  17. DelayedForm: Mostrar un formulario hasta que se pulse un botón o transcurra un tiempo (26/Dic/01)
  18. Ejemplos de cómo usar los controles especiales de Visual Basic clásico (Oct/2002)
    (Los controles comunes de Windows y esas cosas...)
    Ejemplo de uso del control TabStrip (03/Mar/98)
    Ejemplo de uso del control CoolBar (05/Dic/2000)
    Ejemplo de uso del control Treeview (05/Oct/2002)
    Ejemplo de uso del control ListView (06/Oct/2002)
     
  19. Temas de Windows XP: consejos para usarlos desde Visual Basic clásico (30/Oct/2002)
  20. Usar un Servicio Web XML desde VB6 (20/Jun/2003)
  21. Cómo crear un proyecto de instalación para VB6 con Visual Studio Installer (14/Jul/2003)
  22. Cómo usar los lenguajes Scripts desde Visual Basic usando MSScript.ocx (05/Sep/1998 - 30/Jul/2003)
  23. Una rutina para automatizar los formularios que usen temas de XP (21/Ago/2003)
  24. Especial Docking:
    Poner un formulario dentro de un control picture (25/Ene/2004)
    Cambiar el tamaño de dos controles (split) (25/Ene/2004)
    Meter el bloc de notas (u otra aplicación) dentro de un picture (25/Ene/2004)
     
  25. Poner nuestra aplicación en el inicio de Windows (registro) (14/May/04)
  26. Trabajar con números muy, muy grandes (10/Ago/04)

1.- Posicionar el cursor al final de una línea de texto (4/Sep/97)

Ya sabes cómo seleccionar todo el texto de un TextBox, ahora puedes usar esto para posicionarte al final:

Text1.SetFocus			'Asegurarnos que reciba el foco
Text1.SelStart = Len(Text1)	'La posición del caracter inicial es la longitud del texto...
				'por tanto se posiciona al final

2.- Acceder a un control por la tecla rápida sin necesidad de pulsar ALT+letra. (21/Sep/97)

Este "truco" servirá para aquellos forms en los que necesitemos acceder a distintos controles que tienen una tecla de acceso rápido, pero sin necesidad de pulsar la combinación de teclas: Alt+letra_de_acceso.
Para los forms que tengan TextBoxes o cualquier otro control en el que haya que escribir, no se debe usar este truco, ya que impediría introducir esos caracteres, pero como hay ocasiones en las que se puede necesitar, por ejemplo si las entradas del form activo sólo son numéricas o bien si hacemos algo parecido a un MsgBox.
De todas formas, aquí está y si ves que te puede ser útil lo usas y si no, pues "aire" al truco y a otra cosa mariposa...

Este código funciona en cualquier versión de Visual Basic, en la versión 1 y 2 no lo he probado... ¿alguien las usa?

Sub Form_KeyPress (KeyAscii As Integer)
    'Comprobar si la tecla pulsada coincide con
    'alguna de acceso rápido
    '
    'NOTAS:
    '   Debe estar puesto Option Compare Text
    '   El KeyPreview del Form debe estar a True
    '   Esto no es demasiado útil si hay TextBoxes
    '       ya que no podrás escribir los caracteres
    '       de acceso rápido
    '       Pero para cualquier otra aplicación está bien
    '
    Dim ch As String
    Dim i%, j%

    'Detectar los errores producidos
    'al encontrar controles sin Caption
    On Local Error Resume Next

    ch = Chr$(KeyAscii)
    'Un bucle para todos los controles de este form
    For i = 0 To Me.Controls.Count - 1
        j = InStr(Me.Controls(i).Caption, "&" & ch)
        'Si tiene un código de acceso rápido...
        If j Then
            'Esto es para que descarte la tecla pulsada
            KeyAscii = 0
            'Enviamos la pulsación Alt+tecla
            SendKeys "%" & ch
            'nada más que hacer
            Exit For
        End If
    Next

    'Si se ha producido un error...
    Err = 0
    'restaurar la rutina de detección de errores
    On Local Error GoTo 0
End Sub

3.- Para los que tenemos poca memoria... y VB5 (22/Oct/97)

Realmente es una chorradilla de truco, pero lo mismo a tí no se te había ocurrido... (la verdad es que a mí tampoco...)
Si estás usando el VB5, sabrás que cuando usas un control, etc., al escribir el punto para poner la propiedad o método a usar, el VB te muestra las posibilidades, hasta aquí estamos de acuerdo, bien..., pues a mi me ocurre que muchas veces no recuerdo los nombres de los controles que tengo en un formulario, ¿que hacía? mostraba el formulario, pulsaba en el control que al que quería hacer referencia y miraba en la ventana de propiedades el nombre... pues hoy se me ocurre, así como el que no quiere la cosa, aunque más bien por probar, a poner Me. y ¡plas! ahí estaban los nombres de todos los controles...
Ya te dije que era una chorrada, pero no sabes lo que me acelera el usar los nombres que les pongo... 8-)

Una imagen de ejemplo


4.- Cómo simular sobrescribir e insertar en un TextBox (12/Ene/98)

Este truco está sacado de la Microsoft Knowledge Base - How to Emulate Overtype Mode in a Visual Basic Text Box, ID del Artículo: Q96210, por eso los comentarios los he dejado en inglés. Lo único que yo he añadido es el código del evento Text1_KeyDown para que funcione bien al mover el cursor si estamos en modo INSERT.

Este es el código, lo del Label es sólo a título informativo. ¡Que lo disfrutes!

Option Explicit

Const MODE_OVERTYPE = "overtype"
Const MODE_INSERT = "insert"


Private Sub Form_Load()
    Text1.Tag = MODE_INSERT
    Label1.Caption = MODE_INSERT
End Sub


Private Sub Text1_Change()
    ' You have taken some action that changed the text in the
    ' text box. Reset the SelLength if you are in overtype mode.
    If Text1.Tag = MODE_OVERTYPE And Text1.SelLength = 0 Then
        Text1.SelLength = 1
    End If
End Sub


Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)
    '
    'Esto es para manejar bien el movimiento del cursor
    '
    Select Case KeyCode
    ' Handle keys that move the caret position and reset the
    ' SelLength if you are in overtype mode:
    Case vbKeyLeft, vbKeyRight, vbKeyUp, vbKeyDown, vbKeyHome, vbKeyEnd, vbKeyPageUp, vbKeyPageDown
        If Text1.Tag = MODE_OVERTYPE Then
            Text1.SelLength = 0
        End If
    End Select
End Sub


Sub Text1_KeyPress(KeyAscii As Integer)
    ' If you press BACKSPACE and are in overtype mode,
    ' then set SelLength to 0 so the backspace will correctly
    ' delete the character to the left of the current caret
    ' position. SelLength will be reset when the Text1_Change
    ' event occurs following the backspace.
    If KeyAscii = vbKeyBack And Text1.Tag = MODE_OVERTYPE Then
        Text1.SelLength = 0
    End If
End Sub


Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer)
    Select Case KeyCode
    ' Toggle between insert and overtype modes.
    Case vbKeyInsert
        If Text1.Tag = MODE_OVERTYPE Then
            Text1.Tag = MODE_INSERT
            Label1.Caption = MODE_INSERT
        Else
            Text1.SelLength = 1
            Text1.Tag = MODE_OVERTYPE
            Label1.Caption = MODE_OVERTYPE
        End If
    ' Handle keys that move the caret position and reset the
    ' SelLength if you are in overtype mode:
    Case vbKeyLeft, vbKeyRight, vbKeyUp, vbKeyDown, vbKeyHome, vbKeyEnd, vbKeyPageUp, vbKeyPageDown
        If Text1.Tag = MODE_OVERTYPE Then
            Text1.SelLength = 1
        End If
    End Select
End Sub


Private Sub Text1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    ' You have clicked at a new location within the text box. Reset the
    ' SelLength if you are in overtype mode.
    If Text1.Tag = MODE_OVERTYPE And Text1.SelLength = 0 Then
        Text1.SelLength = 1
    End If
End Sub

  5b.- Limitar la entrada de un TextBox sólo a números (incluso notación científica) (13/Jul/02)

Esta es una rectificación del truco publicado el 21/Ene/1998 y rectificada el 21/Sep/2001

Lo que se hace en el evento KeyPress del textbox es comprobar si se han pulsado algunos de los signos considerados como numéricos, además de las teclas Intro y Backspace (borrar).
En el caso de las teclas consideradas numéricas, además del signo menos y el punto decimal, he puesto la coma, el signo más y las letras E y D, por si se quieren introducir números con notación científica.

Private Sub Text1_KeyPress(KeyAscii As Integer)
    '
    Select Case KeyAscii
    Case 13
        KeyAscii = 0        ' Para que no "pite"
        'SendKeys "{tab}"    ' Envía una pulsación TAB
    Case 8, 43 To 46, 48 To 57, 68, 69, 100, 101
    'Case 8, Asc("+") To Asc("."), Asc("0") To Asc("9"), Asc("D"), Asc("E"), Asc("d"), Asc("e")
        ' Sólo admitir teclas consideradas numéricas
        ' La E y D son para números con notación científica
        ' El 8 es la tecla Backspace (borrar hacia atrás)
    Case Else
        ' No es una tecla numérica, no admitirla
        KeyAscii = 0
        Beep
    End Select
End Sub

5.- Limitar la entrada de un TextBox sólo a números (21/Ene/98)
Rectificaciones realizadas el 21/Sep/01 y 13/Jul/02

Este truco es realmente una colaboración de Esteve, el que está con el gato en la foto de los que dan la cara, yo sólo le he "corregido" un pequeño fallillo que tenía el código que eme envió originalmente...

Realmente la base del truco es el uso de la función IsNumeric, el problema que había era que si se introducía un número decimal menor que 1, había que poner el CERO delante del signo decimal, este caso se resuelve añadiendo ese CERO al valor que se le pasa a esta función... con lo cual acepta cualquier número...
Además, y como regalo extra, se comprueba si se pulsa INTRO y en caso de ser así, se "manda" un TAB, además de evitar el BEEP que se produce al pulsar la tecla Intro.
Si no quieres enviar el TAB, simplemente comenta el SendKeys "{tab}" y asunto arreglado...

Private Sub Text1_KeyPress(KeyAscii As Integer)
    If KeyAscii = 13 Then
        KeyAscii = 0		' Para que no "pite"
        SendKeys "{tab}"	' Envía una pulsación TAB
    ElseIf KeyAscii <> 8 Then	' El 8 es la tecla de borrar (backspace)
	' Si después de añadirle la tecla actual no es un número...
        'If Not IsNumeric("0" & Text1.Text & Chr(KeyAscii)) Then
	' Corrección informada por: José Treviño (21/Sep/2001)
	If Not IsNumeric(Chr(KeyAscii)) Then
	    ' ... se desecha esa tecla y se avisa de que no es correcta
            Beep
            KeyAscii = 0
        End If
    End If
End Sub

6.- Justificar el contenido de un TextBox (22/Feb/98)

El tema de la justificación del contenido de un textbox es algo simple de solucionar, para ello se debe asignar a la propiedad Multiline el valor True, de esta forma la propiedad Alignment funciona correctamente.
Si sólo quieres que se muestre una línea, como la mayoría de los TextBox normales, el problema surge cuando el usuario pulsa Intro, al ser multiline, permite que se pulse Intro y se desplaza a la siguiente línea...
Para solucionar este pequeño inconveniente, simplemente asigna a 0 el valor de KeyAscii cuando el valor de esta sea 13 ó 10 (Intro o Control+Intro)


7.- Mostrar los elementos de un ComboBox mientras se escribe (4/Abr/98)

Esto no es nada nuevo, pero es una ampliación de un truco anterior y de una de las colaboraciones.
En esos se hacía con un TextBox y un ListBox, en este caso es sólo con un ComboBox, que tenga la propiedad Style a 0, para que se pueda escribir en él.
Lo que se consigue es que mientras se escriba en el cuadro de texto, se vaya mostrando el item que se parezca más a lo que estamos escribiendo (autocompletar que lo llaman).

Escribe el siguiente código en el form que contenga el Combo:

Private Sub Combo1_Change(Index As Integer)
    Static YaEstoy As Boolean

    On Local Error Resume Next

    If Not YaEstoy Then
        YaEstoy = True
        unCombo_Change Combo1(Index).Text, Combo1(Index)
        YaEstoy = False
    End If
    Err = 0
End Sub


Private Sub Combo1_KeyDown(Index As Integer, KeyCode As Integer, Shift As Integer)
    unCombo_KeyDown KeyCode
End Sub


Private Sub Combo1_KeyPress(Index As Integer, KeyAscii As Integer)
    unCombo_KeyPress KeyAscii
End Sub

Añade estas declaraciones y procedimientos en un módulo BAS,
(o en el mismo FORM, pero cambia el PUBLIC por PRIVATE):

Option Explicit

Dim Combo1Borrado As Boolean

Public Sub unCombo_KeyDown(KeyCode As Integer)
    If KeyCode = vbKeyDelete Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub


Public Sub unCombo_KeyPress(KeyAscii As Integer)
    'si se pulsa Borrar... ignorar la búsqueda al cambiar
    If KeyAscii = vbKeyBack Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub


Public Sub unCombo_Change(ByVal sText As String, elCombo As ComboBox)
    Dim i As Integer, L As Integer
    
    If Not Combo1Borrado Then
        L = Len(sText)
        With elCombo
            For i = 0 To .ListCount - 1
                If StrComp(sText, Left$(.List(i), L), 1) = 0 Then
                    .ListIndex = i
                    .Text = .List(.ListIndex)
                    .SelStart = L
                    .SelLength = Len(.Text) - .SelStart
                    Exit For
                End If
            Next
        End With
    End If
End Sub

9.- Activar la instancia anterior de una aplicación al cargarla por segunda vez (15/May/98)

Cuando se ejecuta una aplicación de Visual Basic, se puede saber, mediante la propiedad PrevInstance del objeto App, si dicha aplicación se está ejecutando.
El TIP que te traigo hoy es para activar la aplicación que se ejecuto por primera vez.
Es decir sólo quieres que haya una "copia" del programa ejecutándose y si se intenta ejecutar de nuevo, hacer que se "active" la copia que haya en ejecución, en lugar de una nueva.

He de aclarar que este truco sólo sirve si el Caption del programa es siempre el mismo.
Para hacer que se "active" la aplicación incluso si el caption se cambia, por ejemplo en el caso de que sea una aplicación MDI o que por cualquier razón cambies el Caption tendrás que "ingeniartelas" por tí mismo.
Lo que yo hago en esos casos, es crear una entrada en el registro o en un fichero INI con el caption que tiene mi aplicación cuando éste cambia, de esta forma puedo saber de forma fácil y rápida el nombre que necesito para activar esa instancia del programa.
También se podría averiguar examinando los títulos de las ventanas (aplicaciones) activas y de esta forma activarla, pero eso sería algo más complicado... pero si lo haces, me mandas el código y lo pondría para que otra gente lo viera.
Como pista para conseguirlo, podrías usar el código usado para saber si una aplicación se está ejecutando... ese código está en la utilidad ListVentanas que encontrarás en la sección GRATISWARE.

Vamos ya con el código para hacer eso de activar la aplicación que se está ejecutando.

Private Sub Form_Load()
    Dim sCaption As String
    
    'si ya se está ejecutando
    If App.PrevInstance Then
        'Guardar el caption de esta aplicación
        sCaption = Caption
        'Cambiar el caption actual para que no se active esta
        Caption = "cualquier cosa"
        
        'Activar la otra instancia
        AppActivate sCaption
        
        'Terminar esta copia del programa
        End
    End If
    
    'Continuar ya que no hay otra copia
    
End Sub

Esto es lo que habría que hacer si el caption de la aplicación cambia y no mantiene siempre el mísmo valor.
Es importante guardar el nuevo caption cada vez que éste se modifique.

Private Sub Form_Load()
    Dim sCaption As String
    
    'si ya se está ejecutando
    If App.PrevInstance Then
        'Leer del fichero de configuración el caption de la aplicación
        sCaption = GetSetting("Aplicacion.ini", "General", "Caption", Caption)
        
        'Cambiar el caption actual para que no se active esta
        Caption = "cualquier cosa"
        
        'Activar la otra instancia
        AppActivate sCaption
        
        'Terminar esta copia del programa
        End
    End If
    
    'Cuando se cambie el caption de la aplicación,
    'guardarlo en el fichero de configuración
    SaveSetting "Aplicacion.ini", "General", "Caption", Caption
    
End Sub

También se puede usar este método en el caso de que el inicio de la aplicación esté en un procedimiento SUB MAIN, en ese caso no podrás usar la propiedad Caption en la asignación de sCaption ni es necesario cambiarla para que no se active esta copia, siempre y cuando al iniciarse desde el módulo BAS aún no se haya mostrado el form.


11.- Hacer referencia a un control usando una variable (23/May/98)

Ya sabes que para asignar un valor de una propiedad de un control debes hacer lo siguiente:
NombreControl.Propiedad = Valor
Por ejemplo para asignar el Caption de Command1, sería:
Command1.Caption = "Nuevo caption"

Pero puede que te encuentres en la necesidad de hacer referencia a un control por medio de una variable, por ejemplo en el caso de que crees una clase que manipule controles pero sólo sabe de ese control el nombre y nada más.
Pues bien, en esos casos, puedes usar la colección de controles que tiene cada Form. Usando el mismo ejemplo de asignar el Caption del Command1, pero siendo la variable unControl$ la que tiene el nombre, se puede hacer esto:
Controls(unControl).Caption = "Nuevo Caption"

Si el control está dentro de un array de controles, se tendrá que hacer esto:
Controls(unControl)(Indice).Caption = "Nuevo Caption del indice " & Indice

Esta forma de usar los controles, la tuve que usar en una clase que manipulaba unas etiquetas y unos contenedores, para no obligarme a usar siempre el mismo nombre en las etiquetas y contenedores.


12.- Otra procedimiento para esperar X segundos (28/Ago/98)

Pues eso, otra forma de esperar un número determinado de segundos.

'Si se quiere usar de forma GLOBAL, insertarlo en un Módulo BAS y declararlo como público
Private Sub Wait(ByVal nSec As Integer)
    'Esperar un número de segundos
    Dim t1 As Date, t2 As Date

    t1 = Second(Now)
    t2 = t1 + nSec
    Do
        DoEvents
    Loop While t2 > Second(Now)
End Sub

13.- Más sobre la colección Forms y Controls (hacer referencia a un control o form usando variables) (11/Oct/98)

Esto es una ampliación/aclaración sobre el Tip 11, y viene a cuento por unas pruebas hechas en una consulta recibida, que por cierto, se me quedó, como muchas otras en el tintero...

La cuestión es la siguiente:
Modificar propiedades de controles usando una variable tanto para el form como para el control.

Según el Tip 11, se puede referenciar a una propiedad de un control de la siguiente forma:
Controls(nombre_del_control).Propiedad = Valor_de_la_propiedad
También se puede asignar ese control a una variable de tipo Control, para posteriormente referenciar a las diferentes propiedades:

'
Dim tControl As Control

Set tControl = Controls(sNombreControl)

tControl.BackColor = 0&

Por tanto, se supone que se debería poder hacer esto otro para poder modificar esa misma propiedad:
Forms(nombre_form).Controls(nombre_control).Propiedad = valor_propiedad
o bien esto otro:

'
Dim tForm As Form

Set tForm = Forms(sNombreForm)

tForm.BackColor = vbRed

Pues no... al menos a mi no me ha funcionado... me da Type Mismatch (error 13)
El tema de querer hacerlo así, está en poder usar una rutina genérica que permita cambiar algunas propiedades de algunos controles en cualquier form, pero usando variables para indicar esos Forms y esos Controles... (las propiedades deben especificarse "explícitamente", ya que no existe ninguna colección de propiedades).

La solución que he encontrado para hacer esto es la siguiente:
Se busca el nombre del form en cuestión en la colección Forms y se asigna a una variable del tipo Form, después se puede acceder al control indicado usando la colección controls, como se explica un poco más arriba.
Veamos el código de un procedimiento genérico (público) que permite asignar ciertas propiedades... (recuerda que sólo es un ejemplo, así que no me eches en cara que es una chorrada, aunque si tienes el VB6 verás que es muchísimo más simple gracias a CallByName)

'
Public Sub Propiedades(ByVal elForm As String, _
                       ByVal elControl As String, _
                       ByVal laPropiedad As String, _
                       ByVal elValor As Variant)
    'Los parámetros se indican como cadena de caracteres,
    'salvo el último que indica el valor a asignar
    
    Dim tmpForm As Form
    Dim tForm As Form
    Dim tControl As Control
    
    'Recorremos la colección Forms en busca del form indicado
    For Each tmpForm In Forms
        'Si es el mismo nombre, este es el form que queremos
        If tmpForm.Name = elForm Then
            'Asignarlo a la variable
            Set tForm = tmpForm
        End If
    Next
    'Si no se ha encontrado ese form, avisarlo mediante un error
    If tForm Is Nothing Then
        Err.Raise vbObjectError + 1000, _
                  "Propiedades", _
                  "No se ha hallado el form indicado por " & elForm

    Else
        'Para detectar el error de asignación del control
        On Local Error Resume Next
        
        'Asignamos el control deseado a la variable
        Set tControl = tForm.Controls(elControl)
        
        If Err Then
            Err = 0
            'No atrapar los errores, sino no se mostraría el nuestro...
            On Local Error GoTo 0
            Err.Raise vbObjectError + 1000, _
                      "Propiedades", _
                      "No se ha hallado el control indicado por " & elControl & _
                      " en el form " & elForm
        End If
        
        'interceptamos las propiedades que podemos manipular
	'si se deja esto de LCase(laPropiedad), los nombres deben estar en minúsculas
	'También puedes usar Option Compare Text en el módulo.
        Select Case LCase(laPropiedad)
        Case "backcolor"
            tControl.BackColor = elValor
        Case "forecolor"
            tControl.ForeColor = elValor
        Case "caption"
            tControl.Caption = elValor
        Case "text"
            tControl.Text = elValor
        Case Else
            'etc.
        End Select
        '
        'En VB6 se puede usar CallByName para asignar el valor de una propiedad:
	'evitandote todo el mogollón de comparaciones...
        'CallByName tControl, laPropiedad, VbLet, elValor
        '

    End If
End Sub


'Para usarlo:
Propiedades Me.Name, "Label1", "Caption", "Hola Mundo"

En resumen: si se quiere obtener un "objeto" form usando Forms("nombre del form"), no se puede...


 

ir al índice