Especial Docking

Meter un formulario dentro de un control Picture

Para poder acoplarlo a otros controles de un formulario.
También se explica cómo meter otra aplicación "dentro" del Picture, (en el ejemplo se usa el bloc de notas)

 

Publicado el  25/Ene/2004
Revisión del 26/May/2004

 

Nota del 05/Nov/2015:
Aquí tienes un enlace para un código parecido pero para usar con .NET (tanto con VB.NET como con C#)

Incrustar una aplicación en un control o en un formulario

 



En este ejemplo te voy a mostrar cómo hacer que un formulario se pueda "meter" en un control PictureBox, con idea de poder simular acoplamiento de formularios con otros controles, es decir, tener un formulario con varios controles, por ejemplo un ToolBar, StatusBar, un TreeView, etc. y en otro de los controles tener un formulario.
De paso veremos cómo hacer que se pueda cambiar el tamaño de dos de esos controles (los que están en el centro del formulario) mediante una barra de división (Split). Aunque esto último está en otra página/artículo, sigue este link para verlo.

Normalmente tendremos un formulario principal, el cual puede tener un menú principal y también en la parte superior un ToolBar. Por otro lado, en la parte inferior también tendremos un StatusBar. En el centro, (en la parte principal del formulario), tendremos dos "paneles", uno a la izquierda y otro a la derecha. En el panel izquierdo podríamos insertar un control al estilo del Outlook con una serie de botones de opciones, etc. y en el panel derecho podríamos tener el contenido de otro formulario de nuestra aplicación.

Todo esto son "deseos", ya que en el ejemplo que te voy a mostrar, todos estos "posibles" controles serán del tipo PictureBox, aunque realmente el único que "debería" ser un PictureBox es el panel de la derecha, el que contendrá el "otro" formulario. Pero, para no complicar demasiado el ejemplo, he preferido dejarlos todos como controles Picture. Sigue las "recomendaciones" de los comentarios del ejemplo y verás que te resultará fácil usar otros controles.

 

Como te decía, el ejemplo tiene dos "trucos".

El primero es: incrustar (o meter) un formulario dentro de un control de tipo Picture.
Para lograr esto, usaremos una (realmente varias) función del API de Windows: SetParent.
De forma simple te diré los pasos que habría que seguir:
Tendremos dos funciones (procedimientos) que se encargarán de meter el formulario dentro del control y otro que ajustará el tamaño del formulario al que tenga dicho control.

Este es el código de esos dos procedimientos, los cuales estarán declarados en el mismo formulario principal, el que hará de contenedor:

' Mostrar el formulario indicado, dentro de picDock
Private Sub dockForm(ByVal formhWnd As Long, _
                     ByVal picDock As PictureBox, _
                     Optional ByVal ajustar As Boolean = True)
    ' Hacer el formulario indicado, un hijo del picDock
    ' Si Ajustar es True, se ajustará al tamaño del contenedor,
    ' si Ajustar es False, se quedará con el tamaño actual.
    Call SetParent(formhWnd, picDock.hWnd)
    posDockForm formhWnd, picDock, ajustar
    Call ShowWindow(formhWnd, NORMAL_eSW)
End Sub

' Posicionar el formulario indicado dentro de picDock
Private Sub posDockForm(ByVal formhWnd As Long, _
                        ByVal picDock As PictureBox, _
                        Optional ByVal ajustar As Boolean = True)
    ' Posicionar el formulario indicado en las coordenadas del picDock
    ' Si Ajustar es True, se ajustará al tamaño del contenedor,
    ' si Ajustar es False, se quedará con el tamaño actual.
    Dim nWidth As Long, nHeight As Long
    Dim wndPl As WINDOWPLACEMENT
    '
    If ajustar Then
        nWidth = picDock.ScaleWidth \ Screen.TwipsPerPixelX
        nHeight = picDock.ScaleHeight \ Screen.TwipsPerPixelY
    Else
        ' el tamaño del formulario que se va a posicionar
        Call GetWindowPlacement(formhWnd, wndPl)
        With wndPl.rcNormalPosition
            nWidth = .Right - .Left
            nHeight = .Bottom - .Top
        End With
    End If
    Call MoveWindow(formhWnd, 0, 0, nWidth, nHeight, True)
End Sub

Para usar estos procedimientos necesitaremos unas declaraciones de funciones del API de Windows, así como unos tipos definidos:

'------------------------------------------------------------------------------
' APIS para incluir las ventanas en un PictureBox
'------------------------------------------------------------------------------
'
' Para hacer ventanas hijas
Private Declare Function SetParent Lib "user32" _
    (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
'
' Para mostrar una ventana según el handle (hwnd)
' ShowWindow() Commands
Private Enum eShowWindow
    HIDE_eSW = 0&
    SHOWNORMAL_eSW = 1&
    NORMAL_eSW = 1&
    SHOWMINIMIZED_eSW = 2&
    SHOWMAXIMIZED_eSW = 3&
    MAXIMIZE_eSW = 3&
    SHOWNOACTIVATE_eSW = 4&
    SHOW_eSW = 5&
    MINIMIZE_eSW = 6&
    SHOWMINNOACTIVE_eSW = 7&
    SHOWNA_eSW = 8&
    RESTORE_eSW = 9&
    SHOWDEFAULT_eSW = 10&
    MAX_eSW = 10&
End Enum

Private Declare Function ShowWindow Lib "user32" _
    (ByVal hWnd As Long, ByVal nCmdShow As eShowWindow) As Long
'
' Para posicionar una ventana según su hWnd
Private Declare Function MoveWindow Lib "user32" _
    (ByVal hWnd As Long, ByVal x As Long, ByVal y As Long, _
    ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
'
' Para cambiar el tamaño de una ventana y asignar los valores máximos y mínimos del tamaño
Private Type POINTAPI
    x As Long
    y As Long
End Type
Private Type RECTAPI
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
Private Type WINDOWPLACEMENT
    Length As Long
    Flags As Long
    ShowCmd As Long
    ptMinPosition As POINTAPI
    ptMaxPosition As POINTAPI
    rcNormalPosition As RECTAPI
End Type
Private Declare Function GetWindowPlacement Lib "user32" _
    (ByVal hWnd As Long, ByRef lpwndpl As WINDOWPLACEMENT) As Long

Ahora, simplemente haremos que el formulario se "meta" en el contenedor y que al cambiar el tamaño del formulario, también se cambie el tamaño del formulario "incrustado".
En el siguiente código se supone que el formulario que queremos mostrar se llama "Form2" y que el control que contendrá dicho formulario se llama Picture1.
También tendremos un botón que se usará para mostrar dicho formulario.
Para que se vea el "efecto", tendremos a la izquierda un segundo control, en este ejemplo es otro PictureBox, pero, como te comentaba antes, puede ser cualquier otro control que queramos tener acoplado a la parte izquierda del formulario. En este caso ese control se llama picIzq y haremos que se acople a la izquierda, asignando a la propiedad Align el valor vbAlignLeft.

Private Sub cmdMostrarForm2_Click()
    '
    ' Asignar el Tag del formulario para saber que está incluido en el Picture
    Form2.Tag = valorDock
    '
    dockForm Form2.hWnd, Picture1, True
End Sub

Private Sub Form_Resize()
    ' ajustar el tamaño del Picture al del formulario
    ' sólo si no está minimizado
    If WindowState <> vbMinimized Then
        ' ajustar el Picture a todo el formulario
        Picture1.Move picIzq.Width, 0, ScaleWidth - picIzq.Width, ScaleHeight
    End If
End Sub

Private Sub Picture1_Resize()
    ' sólo si no está minimizado
    If WindowState <> vbMinimized Then
        ' posicionar el botón en el centro
        With cmdMostrarForm2
            .Left = (Picture1.ScaleWidth - .Width) \ 2
            .Top = (Picture1.ScaleHeight - .Height) \ 2
        End With
        '
        Dim oForm As Form
        '
        For Each oForm In Forms
            ' El tag del formulario incluido en el picture tendrá el Tag asignado
            ' con el valor "valorDock"
            If CStr(oForm.Tag) = valorDock Then
                posDockForm oForm.hWnd, Picture1
            End If
        Next
    End If
End Sub

En la parte de declaraciones del formulario tendremos una constante llamada valorDock que simplemente nos servirá para saber si un formulario está o no incluido dentro del picture.
Cuando queramos "meter" un formulario en el picture, asignaremos esa constante a la propiedad Tag y seguidamente llamaremos al procedimiento dockForm indicándole el formulario que queremos meter, además del control en el que queremos que se meta.

Cuando cambia el tamaño del formulario, el Picture1 se ajusta a ese nuevo tamaño. En este código sólo tenemos en cuenta que hay otro control a la izquierda, pero no arriba ni abajo, en caso de que tengamos otros controles arriba y/o abajo, tendremos que tener en cuenta esos controles a la hora de posicionar y dar tamaño al Picture1.

En el evento Resize del Picture1 se centra el botón y posteriormente recorremos todos los formularios que tenemos en la aplicación para comprobar si hay alguno que tenga ese valor "mágico" que indica que el formulario debe estar dentro del Picture. En caso de que así sea, llamamos al procedimiento posDockForm para que ajuste el tamaño y posición del formulario "incrustado".

En las siguientes capturas, puedes ver el formulario principal sin y con otro formulario dentro.


El formulario "normal"


El formulario con el otro formulario "incrustado"

Y eso es todo.

Para saber cómo agregar una barra que te permita cambiar el tamaño de los dos paneles, échale un vistazo a este otro ejemplo.

Nos vemos.
Guillermo

Pulsa aquí si quieres bajarte el código con el proyecto completo: dockVB6.zip 3.74 KB

En el código de completo se muestra cómo posicionar el botón Cerrar del segundo formulario en la parte inferior derecha.

Si quieres ver funcionando este ejemplo y el de cambiar el tamaño de los paneles centrales, puedes bajarte el código completo, en el que se incluye un ejemplo de cómo poner cualquier ventana (de la que sabemos el caption o título) en un picture: DockSplitVB6.zip 5.42 KB

Nota del 26/May/2004:
En el código de ejemplo tengo puesto como programa a incluir, el bloc de notas, pero como mi sistema operativo está en inglés, utilizo "Untitled - Notepad", así que tendrás que cambiar el nombre al que corresponda en español, que me imagino que es: "Sin título - Bloc de notas".
En el zip ya está incluido un comentario en el sitio que hay que hacer el cambio.
Este "bug" (si es que se le puede llamar así), ya me lo reportaron hace tiempo, pero ahora no recuerdo quién fue... cuando encuentre el mensaje, lo pondré...
Gracias a los que reportáis los fallos que de vez en cuando cometo... ¡que yo no soy perfecto!
 


El formulario con el Notepad dentro de un picture.


ir al índice