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 (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-)
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/02Este 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 SubAñ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 SubEsto 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 SubTambié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 " & IndiceEsta 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 = vbRedPues 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...