Establecer una imagen como fondo en un formulario MDI

Descripción general

Este documento muestra un ejemplo de código para mostrar una imagen como fondo de un formulario MDI

[TOC] Tabla de Contenidos


↑↑↑

Establecer una imagen como fondo en un formulario MDI


↑↑↑

A modo de introducción

En este documento (Cambiar el fondo de un formulario MDI) se analiza porqué hay que escribir el código que se muestra a continuación. Es recomendable su lectura (que además de corta, está muy bien escrita) para que comprendas el porqué de las clases que se usan


↑↑↑

Establecer una imagen de fondo con la propiedad BackgroundImage

La propiedad BackgroundImage obtiene y/o establece la imagen de fondo que se mostrará en un control, y se encuentra implementada en la clase base Control, de la cual la hereda la clase Form. En cambio, la clase MdiClient sobrescribe la propiedad implementada en Control, aunque en definitiva, lo que hace es asignar y recuperar la imagen desde su propia clase base, tal y como se recomienda que se haga con aquellas clases derivadas que reemplazan la propiedad BackgroundImage, a fin de extender la implementación de la clase base.

En principio, simplemente podemos asignar cualquier imagen de un formato permitido a la propiedad BackgroundImage del formulario MDI. Por ejemplo, en tiempo de ejecución le asignaría un objeto tipo Image en el evento Load del formulario:


↑↑↑

Un poco de dódigo

Private Sub FormImagenFondoUno_Load( _
            ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load

    ' Poner una imagen como fondo del formulario
    Call EstablecerImagenFondoParaFormLoad()
End Sub



#Region "Proceso de cambio de la imagen de fondo de un Formulario MDI"

''' <summary>
'''   Esta función tiene que ser llamada 
'''   por el evento FormLoad del Form
''' </summary>
Private Sub EstablecerImagenFondoParaFormLoad()
    Try
        ' Asignamos una imagen al fondo del formulario.
        Me.BackgroundImage = _
        Image.FromFile("C:\Mis imagenes\Imagen1.jpg")

        ' Ajustamos la imagen dentro del rectángulo
        ' cliente del control.
        ' Por ejemplo.: 
        ' Si la imagen es muy 'grande' y ponemos *center* 
        ' la imagen no se ve entera, solo se ve la parte 
        ' superior izquierda que *entra* en el formulario
        Me.BackgroundImageLayout = ImageLayout.Stretch

    Finally
        ' No hago nada
        ' Si no se carga la imagen se muestra 
        ' el color de fondo por defecto
    End Try
End Sub


''' <summary>
'''  Volver a redibujar el formulario para que se vuelva a 
'''  dibujar la imagen y quede bien y no como a 'trozos'
''' </summary>
''' <remarks>
'''  Cuando se cambia el tamaño del formulario, la imagen 
'''  tiene problemas para dibujarse y se queda como a "capas".
'''  Con este evento forzamos a que se dibuje totalmente 
'''  y se resuelve el problema
''' </remarks>
Private Sub FormImagenFondoUno_Resize( _
        ByVal sender As Object, ByVal e As System.EventArgs) _
         Handles Me.Resize
    Me.Refresh()
End Sub

#End Region

Una observación. Cuando desee mover los formularios secundarios dentro del área cliente del formulario MDI principal, sobre todo si la imagen de fondo es de un tamaño considerable. Observará que le cuesta trabajo mover los formularios, dejando éstos su rastro a través del fondo del propio formulario MDI. Esto se debe al valor de la propiedad BackgroundImageLayout, que por defecto tiene el valor ImageLayout.Tile, con lo cual, la imagen se dispone en mosaico por toda el área cliente del formulario.

Para eliminar esa cierta pesadez, puede aumentar el rendimiento estableciendo a la propiedad BackgroundImageLayout cualquier valor distinto a Tile.:

Otro problema que surge es cuando cambiamos el tamaño del formulario MDI, la imagen no se *redibuja* bien, sino que se queda como a *capas*. Para evitar este efecto se usa el evento [Form.Resize], en el que se obliga al formulario a que se vuelva a redibuja otra vez.


↑↑↑

Establecer una imagen de fondo dibujando la imagen con Paint

Otra alternativa a establecer una imagen al fondo del formulario, consiste en dibujar el fondo del formulario con la imagen de cualquier archivo gráfico, usando el evento Paint del formulario, o el evento Paint del control MdiClient subyacente.

En principio, el evento Paint del formulario permite utilizar este proceso en los formularios normales y corrientes, es decir, aquellos que no sean MDI principales, pero en realidad no tiene mucho sentido.

En los formularios MDI, se puede dibujar directamente sobre el fondo del formulario, pero no se vería absolutamente nada, porque como he indicado anteriormente (Ver (Cambiar el fondo de un formulario MDI) el control MdiClient subyacente se superpone sobre el fondo del propio formulario MDI, y observaríamos el típico color gris oscuro característico del control MdiClient.

La solución pasa por detectar el control MdiClient subyacente, y dibujar la imagen sobre su propio fondo, utilizando para ello el evento Paint del objeto MdiClient:

Para aplicar esta técnica es necesario declarar una variable objeto con eventos que referencie al objeto MdiClient subyacente existente en todos los formularios MDI principales. Será en el evento Paint del objeto MdiClient declarado, donde dibujaremos el fondo de la aplicación utilizando para ello la imagen seleccionada.


↑↑↑

Un poco de código

Private Sub FormMdiFondoImagen_Load( _
        ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    Call EstablecerImagenFondoParaFormLoad()
    Call NuevoHijo()
End Sub


Private Sub NuevoHijo()
    Dim f As New Form
    f.MdiParent = Me
    f.Text = " Formulario Hijo - " & _
             Me.MdiChildren.Length.ToString()
    f.Show()
End Sub



#Region "Imagen de fondo para un formulario MDI usando [MdiClient]"


''' <summary>
'''  Contendrá el control que representa al área cliente MDI
''' </summary>
''' <remarks>Se emplea por el evento Form Resize
'''  Si se declara [WithEvents] hay que escirbir 
'''  el evento [_ctlMdiClient.Paint]
'''  si no hay que asignar el manejador 
'''  [AddHandler _ctlMdiClient.Paint, AddressOf PintarFondo]
'''</remarks>
Private WithEvents _ctlMdiClient As MdiClient = Nothing


''' <summary>
'''  Contendrá la imagen que se dibujara en 
'''  el fondo del formulario MDI
''' </summary>
''' <remarks>
'''  Debe estar a nivel de clase porque se 
'''  emplea en varios eventos
''' </remarks>
Private _imagenFondo As Image = Nothing



''' <summary>
'''   Esta función tiene que ser llamada 
'''   por el evento FormLoad del Form
''' </summary>
''' <remarks></remarks>
Private Sub EstablecerImagenFondoParaFormLoad()

    '--------------------------------------------------------
    'ATENCION
    ' Para que todo esto funcione, esta función tiene que 
    ' ser llamada desde el evento Load del formulario
    '--------------------------------------------------------

    '--------------------------------------------------------
    ' asignamos y cargamos la imagen que se 
'''  mostrará en el fondo del formulario
    Try
        _imagenFondo = _
             Image.FromFile("C:\Mis imagenes\Imagen1.jpg")
    Catch ex As Exception
        _imagenFondo = Nothing
    End Try

    '-------------------------------------------------------
    ' Estamos buscando en control que representa el área cliente MDI 
    '  La función [GetMdiContainer] comprobará si existe 
    '  un control MdiClient en el formulario indicado, 
    '  devolviendo la referencia al citado control.
    '  o el valor Nothing si no existe
    _ctlMdiClient = GetMdiContainer(Me)

End Sub



''' <summary>
'''  La función comprobará si existe un control MdiClient
'''  en el formulario indicado, devolviendo la referencia
'''  al citado control. o el valor Nothing si no existe
''' </summary>
''' <param name="frm">El formulario en el que se busca</param>
''' <returns> 
'''    La referncia al objeto 
'''    <see cref="MdiClient">[MdiClient]</see> si existe 
'''    o un valor Nothing si no existe en el formulario 
''' </returns>
Private Shared Function GetMdiContainer( _
                 ByVal frm As Form) _
             As MdiClient

    '--------------------------------------------------------
    ' La función comprobará si existe un control MdiClient
    ' en el formulario indicado, devolviendo la referencia
    ' al citado control. O el valor Nothing si no existe
    '--------------------------------------------------------
    ' Estamos buscando el control que representa 
    ' el área cliente MDI 
    Dim resultado As MdiClient = Nothing
    Dim ctl As Control
    For Each ctl In frm.Controls
        If ctl.GetType = GetType(MdiClient) Then
            ' ¡¡Encontrado!!
            resultado = CType(ctl, MdiClient)
            ' no hace falta seguir buscando mas
            Exit For
        End If
    Next
    '
    Return resultado
End Function




''' <summary>
''' Función que pinta el fondo del área cliente
''' </summary>
Private Sub PintarFondo( _
            ByVal sender As Object, _
            ByVal e As System.Windows.Forms.PaintEventArgs)

    ' Evitar el problema de referencia nulas
    ' por errores en la carga de la imagen
    If Not (Me._imagenFondo Is Nothing) Then
        Using grf As Graphics = e.Graphics
            ' Dibujamos dentro del área cliente 
            ' del objeto MdiClient
            ' con el objeto Image correspondiente.
            grf.DrawImage(_imagenFondo, 0, 0, _ctlMdiClient.Width, _ctlMdiClient.Height)

            ' También se puede dibujar sobre el 
            ' área cliente del formulario.
            ' grf.DrawImage(img, 0, 0, Me.Width, Me.Height)

            ' Destruimos el objeto Graphics.
            ' dispose() se ejecuta en [End Using]
        End Using
    End If
End Sub



''' <summary>
'''  Capturar el evento Paint del control [MdiClient].
'''  Evento Paint de la variable de clase que tiene la instancia
'''  del control MdiClient y que está definida como [WithEvents]
''' </summary>
Private Sub _ctlMdiClient_Paint( _
                ByVal sender As Object, _
                ByVal e As System.Windows.Forms.PaintEventArgs) _
            Handles _ctlMdiClient.Paint

    ' Llamar a la funcion que hace el 
    ' trabajo de  dibujar la imagen
    Call PintarFondo(sender, e)
End Sub



''' <summary>
'''  Capturar el evento Resize del control [MdiClient].
'''  Evento Paint de la variable de clase que tiene la instancia
'''  del control MdiClient y que está definida como [WithEvents]
''' </summary>
''' <remarks>  
'''  <para>
'''    Resuelve el problema de redibujar la imagen 
'''    cuando se cambia el tamaño del formulario
''' </para> 
'''  <para>
'''    Cuando se cambia el tamaño del formulario, la imagen 
'''    tiene problemas para dibujarse y se queda como a "capas".
'''    Con este evento forzamos a que se dibuje totalmente 
'''    y se resuelve el problema
''' </para> 
''' </remarks>
Private Sub _ctlMdiClient_Resize( _
            ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles _ctlMdiClient.Resize

    ' Llamar a la funcion que hace el 
    ' trabajo de  dibujar la imagen
    Call PintarFondo(sender, New PaintEventArgs( _
                Me._ctlMdiClient.CreateGraphics, _
                New Rectangle(Me._ctlMdiClient.Location, Me._ctlMdiClient.Size)))
End Sub


#End Region

En realidad, la afirmación de que no se puede usar el evento Resize del formulario, no es del todo correcta porque si se puede utilizar el evento Resize del formulario pero, evidentemente, hay que hacer un poco de trampa.

Otro comentario, se podría usar de la misma manera e evento Paint del formulario.

A continuación tienes un ejemplo

'---------------------------------------------------------
' Alternativa
' Usar el evento resize del formulario MDI
'--------------------------------------------------------
''' <summary>
'''  Capturar el evento Resize del [Formulario].
''' </summary>
''' <remarks>  
'''  <para>
'''    Resuelve el problema de redibujar la imagen 
'''    cuando se cambia el tamaño del formulario
''' </para> 
'''  <para>
'''    Cuando se cambia el tamaño del formulario, la imagen 
'''    tiene problemas para dibujarse y se queda como a "capas".
'''    Con este evento forzamos a que se dibuje totalmente 
'''    y se resuelve el problema
''' </para> 
''' </remarks>
Private Sub Evento_Resize( _
            ByVal sender As Object, _
            ByVal e As System.EventArgs) _
        Handles MyBase.Resize

    ' Evitar problemas con objetos Nothing
    If Not (Me._ctlMdiClient Is Nothing) Then
        '------------------
        ' Llamar a la funcion que hace el 
        ' trabajo de  dibujar la imagen
        '------------------
        ' Observa la *trampa* al llamar a la función [PintarFondo]  
        ' el objeto [Sender] no es el formulario sino que se  
        ' cambia al control [MdiClient].  
        ' De la misma forma el evento [EventArgs] se cambia  
        ' al evento [PaintEventArgs] y se vuelve a usar para  
        ' ello al control [MdiClient] en lugar del formulario.
        '------------------
        Call PintarFondo( _
            Me._ctlMdiClient, _
            New PaintEventArgs( _
                Me._ctlMdiClient.CreateGraphics, _
                New Rectangle(Me._ctlMdiClient.Location, _
                              Me._ctlMdiClient.Size)))
    End If

End Sub

↑↑↑

A.2.Enlaces

[Para saber mas]
[Grupo de documentos]
[Documento Index]
[Documento Start]
[Imprimir el Documento]