Curso Básico de Programación
en Visual Basic
| Entrega
Veinticinco: 18/Oct/98 (la entrega de plata) por Guillermo "guille" Som |
Si quieres linkar con las otras entregas, desde el índice lo puedes hacer
Si esto de los números de las entregas fuese como los años de un matrimonio, con esta cumpliríamos las bodas de plata, pero esto no es ningún matrimonio ni nada que se le parezca... o casi, ya que es casi como un compromiso entre tú, el lector, y yo, el profe, así que vamos a ver si seguimos llevándonos bien para no tener que divorciarnos ni nada por el estilo.
La mejor forma de llevarnos bien es que cada uno haga lo que tiene que hacer, tú estudiar y practicar y yo dedicarme a seguir cumpliendo entregas... que por otro lado son menos pesadas que cumplir años...Hoy vamos a continuar con los eventos, esta será la última de la serie, que ya está bien.
En la próxima entrega haremos un pequeño repaso a cómo se deben declarar las variables y algunos otros consejos que espero que sigas a pies juntillas, o al menos los tenga casi siempre presentes... ya verás a que me refiero, pero ahora vamos a ver esos cuantos eventos que en algunos casos es conveniente saber utilizar.
Otros eventos.
Ya habrás comprobado que eventos hay para casi cualquier cosa, muchos de ellos no se suelen usar de forma habitual, y siquiera los habituales. La razón es porque no siempre es necesario interceptar todos los avisos que nos envían los controles. Según el tipo de control y en ciertas de estancias se usan unos u otros pero aún no he visto ninguna aplicación que necesitara manipular todo, qué digo todo, ni siquiera la mitad de ellos. Creo que sería una locura.
Hay que saber que algunos eventos son procesados por los controles o formularios cuando ese control o formulario está activo o recibe el foco, mientras esto no ocurra, el susodicho control estará en standby.
Evento GotFocus.
Cuando un control se convierte en activo y recibe el foco, produce el evento GotFocus, es decir que tiene el foco de atención del usuario; de igual forma, cuando deja de ser el control activo, hace sonar la alarma mediante el evento LostFocus.
El control en el que suele usarse estos eventos, de forma más habitual, es el TextBox.
Normalmente en el evento de GotFocus se suele seleccionar todo el texto que contenga, para hacerlo, como ya vimos con "profusión" en la entrega anterior, se suele usar un código como este:Private Sub Text1_GotFocus() With Text1 .SelStart = 0 .SelLength = Len(.Text) End With End SubAunque ya deberías saberlo, te vuelvo a repetir lo que hacen esas líneas de código:
| .SelStart = 0 | Le indica al control que la posición de inicio del texto seleccionado empiece por la posición cero, es decir la primera letra. |
| .SelLength = Len(.Text) | SelLength es la longitud del texto seleccionado y el valor que se le asigna es la longitud total del texto que haya en el control. |
Por tanto lo que se hace es
seleccionar todo el contenido del TextBox, las propiedades
SelStart y SelLength también se podrían usar para saber cuál
es el texto que hay seleccionado y poderlo usar en los casos de
búsqueda y sustitución de palabras. Pero hay otra forma más
rápida de saberlo, mediante la propiedad SelText
del TextBox, con esta propiedad nos indica que texto es el que
está seleccionado en ese momento.
Para almacenar en una variable el texto que actualmente está
seleccionado, haremos esto:
'Guardar en sBuscar el texto seleccionado en Text1 sBuscar = Text1.SelText
Evento LostFocus.
En el evento LostFocus se suele comprobar si el contenido es el que se espera que haya. Realmente es un pequeño problema el tener que validar los datos en este evento, ya que cuando se producen, inmediatamente hay otro control que recibe el foco y si resulta que los datos contenidos en ese control no son los que esperábamos se intente volver a darle el foco a este control, con lo cual se produciría un evento LostFocus en el control que recibió el foco cuando el primero lo perdió y si en ese segundo control también hay una comprobación de datos y éstos son erróneos, tendremos tal cacao que el pobre Visual Basic se terminará quejándose.
Aunque el que se habrá quejado habrá sido tú, ya que me imagino que no te has enterado de lo que acabo de decir, pero no te voy a dar un ejemplo para que lo entiendas, en vez de eso, te voy a poner un ejercicio y así de camino practicas un poco.
Crea un proyecto nuevo, en el Form
inserta dos TextBox y CommandButton, el botón se usará para
salir del programa, en cada uno de los TextBox se comprobará que
el contenido no esté vacio y no se permitirá salir del TextBox
en cuestión si el contenido no es el que esperamos que sea, por
ejemplo, podrías hacer que el primero sólo admita números y el
segundo sólo letras. Aunque esto se podría hacer de otra forma,
prefiero que lo hagas en el LostFocus, para que practiques.
Pero como te digo, si lo prefieres, puedes validar cualquier otra
cosa, por ejemplo que tenga algo escrito o cualquier otra cosa
que se te ocurra... yo no suelo validar casi nada en esos eventos
ya que normalmente suelen dar más quebraderos de cabeza que
ventajas. Pero de lo que se trata es que seas tú
el que decida que es lo mejor o peor para tus
programas, esto es extensible a cualquier otra cosa que yo u otra
persona pueda decirte: evalúalo primero y después decide si es
lo que realmente te hace falta.
Para que realmente funcionen esas
comprobaciones, asignales una cadena vacia nada más cargarse el
formulario y añade también código al botón de salir para que
descargue el formulario, así podrás comprobar que hasta que no
se cumplan las condiciones especificadas, no dejará de
"pitarte" para que escribas lo que debes escribir.
Por supuesto, estas comprobaciones se harán en los eventos
LostFocus de cada control y si el contenido no cumple las
condiciones que esperamos, habrá que volver a darle el foco,
usando el método SetFocus, además de un Beep
para que sepamos que los datos contenidos no son válidos.
El código a usar sería algo como esto:
Private Sub Text1_LostFocus()
'...
'Si el contenido del control no es válido entonces
Beep
Text1.SetFocus 'Volvemos a darle el foco
End Sub
Pruébalo y verás cómo ahora entenderás lo que digo, y si no lo entiendes al menos habrás visto cómo el Visual Basic se queja.
Bueno, vale, te pongo el código que no permitirá que los TextBoxes estén vacios y si está al cambiar de control, se quedará en ese, pero como te he dicho, esto volverá loco al VB.
'------------------------------------------------------------------
'Pruebas para la entrega veinticinco del curso básico (18/Oct/98)
'------------------------------------------------------------------
Option Explicit
Private Sub Form_Load()
'Borramos el contenido de los TextBox
Text1 = ""
Text2 = ""
'Hacemos que el Text1 sea el primero en recibir el foco
Text1.TabIndex = 0
End Sub
Private Sub cmdSalir_Click()
Unload Me
End Sub
Private Sub Text1_LostFocus()
'No permitir que el TextBox esté vacio
If Len(Trim$(Text1)) = 0 Then
Beep
Text1.SetFocus
End If
End Sub
Private Sub Text2_LostFocus()
'No permitir que el TextBox esté vacio
If Len(Trim$(Text2)) = 0 Then
Beep
Text2.SetFocus
End If
End Sub
Ejecuta el programa, el foco lo tendrá el primer cuadro de texto, sin escribir nada, pulsa la tecla TAB para cambiar al otro control, verá cómo el VB se vuelve turuta... Tendrás que pulsar Control+Break (Cttl+Inter) para pararlo...
En la versión 6 del VB se incluye un nuevo evento: Validate que será el que se use para "validar" el contenido de un TextBox, en lugar de hacerlo en el evento LostFocus. Sobre este evento ya veremos algo más adelante (en unas cuantas celebraciones más), aunque dentro de poco lo podrás ver en la sección sobre el VB6 que tengo en mis páginas, las cuales seguramente se sumarán en su momento a este curso... cuando les toque el turno.
Evento Change.
Otro evento relacionado con los
controles que permiten introducir información es: Change.
Este evento se disparará siempre que el contenido del control
cambie, por tanto en un TextBox se producirá si lo que hay en la
propiedad Text cambia.
Esto ya lo vimos en las primeras entregas, además de cómo no
usar este evento, para que no diese un error de desbordamiento de
la pila.
Arrastrar y Soltar controles y otros "objetos".
Pero no creas que esto terminar
aquí ya que te voy a explicar algo sobre cómo arrastrar y
soltar, ya que ésta es una de las peculiaridades del entorno
Windows y es una de las cosillas que deberías de tener siempre
en tus aplicaciones. Aunque el ejemplo y la explicación que te
voy a dar es para el Visual Basic 5, si quieres ver cómo se hace
con la versión anterior te recomiendo que te leas los trucos de
mis páginas.
También te voy a dar un ejemplo de cómo hacer que se puedan
mover los controles de tamaño y o posición dentro del
formulario, en tiempo de ejecución, claro. No es perfecto, pero
medio funciona, el problema que tiene es que hay que codificar en
muchos eventos, pero para ello he creado unos procedimientos para
hacer menos tedioso el tema...
La "versión" que tenía antes de esta
"utilidad", no permitía que se soltara un control
encima de otro, sólo permitía que se soltasen en el formulario,
osea, si en la nueva posición había otro control, no se dejaba
en ese sitio...
Pero como te digo, ahora va mucho mejor... después de el evento
OLEDragDrop veremos el código y un poco de explicación.
Evento OLEDragDrop.
Veamos primero cómo hacer que
nuestra aplicación permita que se suelte un fichero y cuando se
haga se muestre en un TextBox, por tanto no pruebes con ficheros
que no sean de texto.
Para poder hacer esto, hay que asignar las siguientes propiedades
del Form y del TextBox que mostrará el texto:
OLEDropMode = 1 ' Manual
El TextBox debería ser Multiline para que se muestre bien el
fichero soltado.
Para asignar a un TextBox el primer fichero de los que se sueltan en un formulario o cualquier control que tenga la propiedad OLEDropMode igual a uno, vamos a usar un procedimiento genérico, se supone que será para ficheros de texto, incluso documentos RTF si el control en cuestión es un RecichTextBox.
Private Sub AsignarFichero(unTextBox As Control)
'Asignar al TextBox indicado el contenido del fichero soltado
Dim sFic As String
Dim nFic As Long
Dim sText As String
'Asignar sólo el primer fichero
sFic = Data.Files(1)
'Abrirlo y leer el contenido
nFic = FreeFile
Open sFic For Input As nFic
sText = Input$(LOF(nFic), nFic)
Close nFic
'Asignarlo al control indicado
unTextBox = sText
End Sub
Para usar este procedimiento, lo llamaremos desde el evento OLEDragDrop del formulario y del control en cuestión:
Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _
Button As Integer, Shift As Integer, X As Single, Y As Single)
AsignarFichero Text3
End Sub
Private Sub Text3_OLEDragDrop(Data As DataObject, Effect As Long, _
Button As Integer, Shift As Integer, X As Single, Y As Single)
AsignarFichero Text3
End Sub
Ahora ya puedes "soltar" un fichero de texto en el formulario o el TextBox y se mostrará el contenido en ese TextBox.
Evento DragDrop y propiedad Drag
Ejemplo de cómo redimensionar y mover controles en tiempo de ejecución.
Ahora vamos a ver cómo mover controles por el formulario y soltarlos en la posición deseada. También vamos a ver cómo, mediante el uso de una llamada al API de Windows, podemos hacer que un control sea "redimensionable", la única pega para hacer que sea redimensionable, o si lo prefieres, la única condición, es que el control sea una "ventana", no te confundas, los controles en Visual Basic son controles, pero el Windows trata a algunos de ellos como si fuesen ventanas, por ejemplo el control TextBox es para Windows una ventana, sin embargo el control Label no lo es. Normalmente se puede saber si un control es "ventanero" si tiene la propiedad hWnd, ya que los que no son realmente una ventana, no tienen esa propiedad.
Veamos el código y las declaraciones para hacer que todo esto pueda funcionar.
Primero te explico que en cada control hay que hacer lo siguiente en los eventos MouseDown y DragDrop, salvo en el Form, como veremos después, ya que en el Form sólo se detectará la acción de soltar el control:
Private Sub NombreDelControl_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop NombreDelControl, Source, X, Y
End Sub
Private Sub NombreDelControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag NombreDelControl, Button, X, Y
End Sub
En los controles que no queramos permitir que se suelte, hay que "cancelar" la operación de Drag & Drop, por ejemplo, si tenemos un botón y no queremos que se suelte encima, habría que hacer algo como esto:
Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single)
'Cancelar la acción de soltado
CancelarDrag Source
End Sub
El procedimiento de calcular la nueva posición se encarga de hacer las comprobaciones pertinente para posicionar adecuadamente el control soltado; si el control que se mueve, está contenido a su vez en otro control, no se permite que se mueva fuera de su contenedor. Esta forma de actuar puedes cambiarla si quieres, aunque deberías tener en cuenta que el contenedor debe ser el control de destino o el formulario en caso de que se suelte sobre el form, también podrías hacer que al soltarse un control en otro, el que recibe el control fuese el contenedor... todo es cuestión de que hagas pruebas y lo adecues a tus gustos.
Una cosa que debes tener en cuenta si quieres mover los TextBox, es que no te permitirá seleccionar el texto usando el ratón, ya que al pulsarse con el ratón se llama al procedimiento que "avisa" que se está haciendo el Drag&Drop y al dejarlo "invisible", pierde el foco, aunque esto se soluciona en el procedimiento que calcula la nueva posición, podrías querer que no se pueda mover, ese es el caso del Text4 del ejemplo que pongo a continuación.
Ahora veamos el código de este
ejemplo,
el
código lo puedes conseguir pulsando en este link: codigo25.zip (6.62 KB)
'----------------------------------------------
'Prueba para redimensionar Pictures
' y mover controles (23/Sep/96)
'
'Revisado/mejorado: el 18/Oct/98
'
'©Guillermo 'guille' Som, 1996-98
'----------------------------------------------
Option Explicit
Dim frmName As String
Dim DY As Single
Dim DX As Single
Dim NumColumnas As Integer
Dim NumFilas As Integer
Dim bIniciando As Boolean
'Declaraciones del API para 32 bits
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetWindowPos Lib "user32" _
(ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cX As Long, ByVal cY As Long, _
ByVal wFlags As Long) As Long
Const GWL_STYLE = (-16)
Const WS_THICKFRAME = &H40000
'
Const SWP_DRAWFRAME = &H20
Const SWP_NOMOVE = &H2
Const SWP_NOSIZE = &H1
Const SWP_NOZORDER = &H4
Private Sub CmdSalir_Click()
Unload Me
End Sub
Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single)
'Cancelar la acción de soltado
CancelarDrag Source
End Sub
Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single)
'Cuando se suelta en el form, no especificar el destino
EndDragDrop Source, X, Y
End Sub
Private Sub Form_Load()
bIniciando = True
'Asignamos el nombre del formulario, ya que lo necesitaremos
'para saber si se mueve encima del form o de otro control
frmName = Me.Name
'Hacer estos controles redimensionables,
'usando el API
CambiarEstilo PicColum(0)
CambiarEstilo Text2
CambiarEstilo Picture1
CambiarEstilo Frame1
CambiarEstilo Picture3
CambiarEstilo Picture4
CambiarEstilo Text4
Text3 = "Left= " & Text3.Left & ", Top= " & Text3.Top
'Para crear filas/columnas en un Control
NumFilas = 2
Label1(0) = "Cabecera de la colunma"
Load Text1(1)
With Text1(1)
Set .Container = PicColum(0)
.Visible = True
.Top = Text1(0).Top + Text1(0).Height
End With
Load Label2(1)
With Label2(1)
.Visible = True
.Top = Label2(0).Top + Label2(0).Height
.Caption = "Fila 2"
End With
NumColumnas = 1
bIniciando = False
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Text2.Text = "X= " & Str$(X) & ", Y= " & Str$(Y)
End Sub
Private Sub Frame1_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Frame1
End Sub
Private Sub Frame1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Frame1, Button, X, Y
End Sub
Private Sub Label1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Label1(Index)
End Sub
Private Sub Label2_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Label2(Index)
End Sub
Private Sub PicColum_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, PicColum(Index)
End Sub
Private Sub PicColum_Resize(Index As Integer)
Dim k As Integer
Dim i As Integer
If bIniciando Then Exit Sub
'ajustar el ancho del Label y los texts
Label1(Index).Width = PicColum(Index).Width
For i = 0 To NumFilas - 1
k = i * NumColumnas + Index
Text1(k).Width = PicColum(Index).Width
Next
PicColum(0).Left = Label2(0).Width
For i = 0 To NumColumnas - 1
If i > 0 Then
PicColum(i).Left = PicColum(i - 1).Left + PicColum(i - 1).Width
End If
PicColum(i).Top = 0
Next
End Sub
Private Sub Picture1_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Picture1
End Sub
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Picture1, Button, X, Y
End Sub
Private Sub Picture2_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Picture2
End Sub
Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Picture2, Button, X, Y
End Sub
Private Sub Picture3_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Picture3
End Sub
Private Sub Picture3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Picture3, Button, X, Y
End Sub
Private Sub Picture4_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Picture4
End Sub
Private Sub Picture4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Picture4, Button, X, Y
End Sub
Private Sub Text1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Text1(Index)
End Sub
Private Sub Text2_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Text2
End Sub
Private Sub Text2_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
'Si no se quiere que pase por encima otro control
'CancelarDrag Source
End Sub
Private Sub Text2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Text2, Button, X, Y
End Sub
Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Text3
End Sub
Private Sub Text3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
IniciarDrag Text3, Button, X, Y
End Sub
Private Sub Text4_DragDrop(Source As Control, X As Single, Y As Single)
EndDragDrop Source, X, Y, Text4
End Sub
Private Sub Text4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'Si se quiere poder seleccionar con el ratón
'quitar la siguiente llamada, aunque entonces no se permitirá
'mover el control
'IniciarDrag Text4, Button, X, Y
End Sub
Private Sub CambiarEstilo(queControl As Control)
Dim Style As Long
On Local Error Resume Next
Style = GetWindowLong(queControl.hwnd, GWL_STYLE)
If Err Then
Err = 0
MsgBox "El control " & queControl.Name & " no permite que se redimensione", vbInformation
Exit Sub
End If
Style = Style Or WS_THICKFRAME
Style = SetWindowLong(queControl.hwnd, GWL_STYLE, Style)
Style = SetWindowPos(queControl.hwnd, _
Me.hwnd, 0, 0, 0, 0, SWP_NOZORDER Or _
SWP_NOSIZE Or SWP_NOMOVE Or SWP_DRAWFRAME)
End Sub
Private Sub CancelarDrag(Source As Control)
Source.Visible = True
Source.Drag vbCancel
End Sub
Private Sub EndDragDrop(Source As Control, X As Single, Y As Single, Optional Dest As Object)
Dim posX As Long
Dim posY As Long
If Dest Is Nothing Then
'Si no se especifica el control de origen,
'por ejemplo cuando se suelta en el formulario
posX = -30
posY = -30
'Si el nombre del contenedor del Source no es el nombre
'del destino, no moverlo.
'Esto ocurrirá cuando se mueve un control contenido en otro
'y el destino no es el control que lo contiene
If Source.Container.Name <> frmName Then
CancelarDrag Source
Exit Sub
End If
Else
'Si el control destino es el mismo que el contenedor del Source
'Esto ocurrirá cuando el control que se mueve se suelta
'en el control en que está contenido
If Dest.Name = Source.Container.Name Then
posX = -60
posY = -60
Else
'Si el nombre del contenedor del Source no es el nombre
'del destino, no moverlo.
'Esto ocurrirá cuando se mueve un control contenido en otro
'y el destino no es el control que lo contiene
If Source.Container.Name <> frmName Then
CancelarDrag Source
Exit Sub
End If
With Dest
'El nombre del formulario se asignará
'a la variable frmName
If .Container.Name = frmName Then
'Esto ocurrirá cuando se suelte un control
'en otro que esté contenido en el formulario,
'no contenido en otro control
posX = .Left
posY = .Top
Else
'Esto ocurrirá cuando se suelte en un control
'que está contenido en otro control
posX = .Container.Left + .Left + 60
posY = .Container.Top + .Top + 60
End If
End With
End If
End If
'Posicionar el control soltado
With Source
.Visible = True
.Move posX + X - DX, posY + Y - DY
.Drag vbEndDrag
.ZOrder
End With
'Si el Source es un textbox, darle el foco
If TypeOf Source Is TextBox Then
Source.SetFocus
End If
'Si se van a usar RichTextBox, hacer la comparación correspondiente
End Sub
Private Sub IniciarDrag(Source As Control, Button As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
DX = X
DY = Y
Source.Drag vbBeginDrag
'Cambiar a no visible, ya que si no,
'el form no detectaría que se ha soltado,
'si el puntero del ratón no sale del control.
Source.Visible = False
Source.Drag
End If
End Sub
Hasta aquí ha llegado esta "entrega de plata", si quieres hacer un comentario, guárdatelo para otra ocasión... je, je.
Parte de esta entrega la tenía escrita desde el 15 de julio de 1998, en total un folio manuscrito por las dos caras y pasado al Word usando el ViaVoice de IBM que aunque no me entiende, (seguramente porque yo no "dicto" bien), algo es algo y en cuanto le enseñe a entenderme seguramente las entregas llegarán con mayor rapidez. Aunque es algo "lentillo" y la verdad es que el código no hay forma de hacérselo entender, pero seguiré probando a ver si logro que algún día sepa de que le estoy hablando... al menos me rio un poco de lo que escribe cuando no me entiende...
Hasta la próxima que será... imagínatelo...
Nos vemos.
Guillermo
Nerja, 18 de Octubre de 1998
Las páginas
del Curso Básico se han visitado:
veces desde el 3/Sep/97 04:15 GMT