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.SelTextEvento 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 SubPrué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 SubEjecuta 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 SubPara 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 SubAhora 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 SubEn 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 SubEl 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 SubHasta 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.
GuillermoNerja, 18 de Octubre de 1998