Cómo asignar un color o una imagen al fondo de un formulario MDI principal |
Por Enrique Martínez Montejo |
Última revisión: 02/09/2007 |
Ni que decir tiene que a muchos programadores de Visual Basic no les hace ni pizca de gracia el color de fondo que presentan los formularios que actúan de Interfaz de Múltiples Documentos, los llamados MDI principales, ya que el color gris oscuro que presenta el fondo de la aplicación, no les parece muy atractivo del todo.
El color de fondo de los formularios MDI viene dado por el color de sistema SystemColors.AppWorkspace, que referencia al elemento Fondo de la aplicación de la ficha Apariencia del cuadro de diálogo Propiedades de Pantalla, que como es sabido, se puede acceder al mismo a través del Panel de Control, o pulsando con el botón secundario del ratón sobre el escritorio de Windows. En dicho cuadro de diálogo, el usuario puede elegir la combinación de colores del sistema que crea conveniente, o bien, modificar el color de cualquier elemento de los existentes. Por tanto, estamos ante una preferencia más del usuario, de la misma forma que si se tratara de cualquier otra preferencia regional personal, como por ejemplo, el símbolo decimal de los números o el formato de fecha corta que se utilizará.
Aquellos que me conocen por mis participaciones en los grupos de noticias públicos de Microsoft, saben que soy inflexible en cuanto a las preferencias que el usuario tiene establecidas en su sistema operativo, las cuales hay que respetar, nos gusten o no, de la misma forma que nosotros queremos que se respete nuestra decisión de haber adquirido un coche de color amarillo fosforescente, por poner un ejemplo. Por tanto, los colores que el usuario tenga definidos en su sistema, no van a ser menos que el símbolo de moneda que tenga establecido.
También, y a decir verdad, los colores definidos en una combinación de ellos, viene establecida de manera predeterminada, y en algunos casos, dichas combinaciones de colores no son de lo más acertadas, por lo que la inmensa mayoría de usuarios, bien porque son conformistas o desconocen la manera de modificar los colores, tienen definido el color gris oscuro (o aquel color formado por los componentes de Rojo, Verde y Azul equivalentes a los valores 128, 128, 128, respectivamente) como el color de fondo de la aplicación, o lo que es lo mismo, el color de fondo que tendrán los formularios que actúan como MDI principales.
Con el siguiente artículo, aparte de enseñar como asignar un color o una imagen al fondo de los formularios MDI, pretendo también explicar lo que sucede internamente cuando decidimos que un formulario cualquiera de nuestra aplicación, actúe como formulario MDI principal.
Esta clase representa el contenedor para los formularios secundarios de Interfaz de Múltiples Documentos (MDI), y no se puede heredar, ya que se encuentra declarada como NotInheritable. Dicha clase hereda directamente de la clase System.Windows.Forms.Control, que es la clase base para todos los controles, incluidos los formularios, que aunque no heredan directamente de Control, tienen a ésta clase dentro de su jerarquía de herencia.
Es fácil indicar que un formulario actúe de MDI principal: simplemente hay que establecer el valor True a la propiedad IsMdiContainer, bien en tiempo de diseño o en tiempo de ejecución.
Al establecer el valor True a la propiedad IsMdiContainer, automáticamente se crea una instancia de la clase MdiClient que, como control que es (porque hereda de la clase Control), se añade a la colección Controls del formulario, de igual manera que se añaden al formulario los controles TextBox o Button.
Una vez creada la instancia del objeto MdiClient, en el 90 y pico por 100 de los casos, el color de fondo del formulario cambia a gris oscuro, aunque se haya definido explícitamente el valor de la propiedad BackColor del formulario MDI. Pero en realidad, lo que sucede es que el control MdiClient se sobrepone sobre el formulario MDI propiamente dicho, de tal forma que nos hace creer que es el fondo del formulario MDI, pero no es así; es el fondo propio del control MdiClient que Visual Basic ha creado automáticamente.
Esto lo puede comprobar fácilmente, cuando abra el formulario MDI en el Diseñador de formularios de Visual Studio, y pasa sucesivamente de la ventana del diseñador a la ventana de código, y viceversa. Observará que el formulario, primero establece su color de fondo, y posteriormente se colorea de gris oscuro, si la propiedad IsMdiContainer tiene el valor True.
Llegado a este punto, ya sabemos que lo que estamos observando es el color de fondo del control MdiClient creado, o el color del valor de su propiedad BackColor, la cual hereda de la clase base Control, que por defecto toma el valor del color de sistema SystemColors.AppWorkspace, por tanto, será el valor de la propiedad BackColor de objeto MdiClient el que deberemos modificar para que ese fondo superpuesto existente en nuestro formulario MDI, se muestre con el color que nosotros deseemos asignarle.
El ejemplo que muestro a continuación enseña la manera de crear un formulario MDI principal en tiempo de ejecución, sin necesidad de asignar el valor True a la propiedad IsMdiContainer del formulario, porque en su lugar vamos a crear una instancia de la clase MdiClient que posteriormente añadiremos a la colección Controls del formulario.
Public
Class CreateMDIForm
Inherits System.Windows.Forms.Form
Public
Shared
Sub Main()
Application.EnableVisualStyles()
Application.Run(New CreateMDIForm)
End
Sub
Public
Sub
New()
InitializeComponent()
End
Sub
Private
Sub InitializeComponent()
Me.SuspendLayout()
'
'CreateMDIForm
'
Me.ClientSize =
New System.Drawing.Size(292, 266)
Me.Name = "CreateMDIForm"
Me.Text = "Cómo crear un formulario MDI en
tiempo de ejecución."
Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
Me.ResumeLayout(False)
End
Sub
Protected
Overrides
Sub OnLoad(ByVal e
As EventArgs)
' Indicamos que se trata de un formulario MDI
principal,
' y le asignamos un color de fondo
cualquiera.
'
SetMdiContainer(Me,
Color.AliceBlue)
'
Comprobamos que el formulario es MDI principal.
'
MessageBox.Show(Me.IsMdiContainer.ToString)
' Creamos
un formulario MDI secundario de demostración.
'
Dim frm
As Form =
New Form
With frm
.MdiParent =
Me
.Text =
"Formulario MDI secundario."
.StartPosition = FormStartPosition.CenterScreen
.Show()
End
With
End
Sub
Private
Sub SetMdiContainer(ByVal frm
As Form,
ByVal formBackColor
As Color)
' Creamos
una nueva instancia de la clase MdiClient
'
Dim mdi
As MdiClient =
New MdiClient
' Le
asignamos el color de fondo.
'
mdi.BackColor = formBackColor
' Añadimos
el control a la colección Controls
' del formulario.
'
frm.Controls.Add(mdi)
End
Sub
End
Class
Cómo crear un formulario MDI en tiempo de ejecución
Si ha ejecutado el código fuente, creo que sobran los Comentarios. Digamos que el código fuente contenido en el procedimiento SetMdiContainer, es la manera correcta de crear un formulario MDI principal en tiempo de ejecución, si nuestra intención es tener el control sobre la apariencia del formulario.
Salvo que en el formulario haya añadido más controles, si comprueba el número de controles existentes en el formulario. observará que la propiedad Count de la colección Controls devuelve un único control, que se corresponderá con el control MdiClient que hemos creado y añadido a la colección de controles del formulario MDI principal, lo que viene a confirmar que el objeto MdiClient es otro control más que se añade al formulario para que actúe de contenedor para otros formularios secundarios MDI.
Si en lugar de crear el formulario MDI principal en tiempo de ejecución, lo ha creado en tiempo de diseño (asignando el valor True a la propiedad IsMdiContainer), también podemos referenciar el control MdiClient subyacente que Visual Basic ha creado por nosotros. Simplemente hay que recorrer la colección de controles para detectar cual de ellos es de la clase MdiClient. Pero en este supuesto, solamente podemos modificar el color de fondo del formulario, o mejor dicho, del control MdiClient, en tiempo de ejecución, tal y como muestra el siguiente ejemplo.
Protected
Overrides
Sub OnLoad(ByVal e
As EventArgs)
' Obtenemos si procede el
contenedor MdiClient del formulario
'
Dim mdi
As MdiClient = GetMdiContainer(Me)
' Si no es un contenedor MDI
abandonamos el procedimiento.
'
If (mdi
Is
Nothing)
Then
Return
' Modificamos el color de fondo
'
mdi.BackColor = Color.AliceBlue
' Creamos un formulario MDI
secundario de demostración.
'
Dim frm
As Form =
New Form()
With frm
.MdiParent =
Me
.Text =
"Formulario MDI secundario."
.StartPosition =
FormStartPosition.CenterScreen
.Show()
End
With
End
Sub
Private
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.
For
Each ctrl
As Control
In frm.Controls
If (TypeOf ctrl
Is MdiClient)
Then
Return
DirectCast(ctrl, MdiClient)
End
If
Next
' No existe ningún control
MdiClient.
'
Return
Nothing
End
Function
Cómo obtener el control MdiClient existente en un formulario MDI
Ahora que ya conoce la forma de cambiar el color de fondo de los formularios MDI principales, voy a pasar a detallar la manera de asignar una imagen de fondo a dichos formularios.
Antes de explicar la manera de asignar una imagen para el fondo de un formulario MDI, voy a explicar brevemente la propiedad BackgroundImage.
Esta propiedad obtiene y 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:
Protected
Overrides
Sub OnLoad(ByVal e
As EventArgs)
' Asignamos una imagen al
fondo del formulario.
'
Me.BackgroundImage = Image.FromFile("C:\Mis
imagenes\Imagen1.jpg")
End
Sub
Pero llegado este momento, el problema surge 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 mosáico 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:
Protected
Overrides
Sub OnLoad(ByVal e
As EventArgs)
' Asignamos una imagen al
fondo del formulario.
'
Me.BackgroundImage = Image.FromFile("C:\Mis
imagenes\Imagen1.jpg")
' Centramos la imagen dentro del
rectángulo cliente del control.
'
Me.BackgroundImageLayout = ImageLayout.Center
End
Sub
¡Ahora, sí! Los formularios secundarios se mueven con más agilidad.
Para no tener que acordarnos de asignar un valor adecuado a la propiedad BackgroundImageLayout del formulario, otra alternativa a establecer una imagen al fondo del formulario, pasaría por dibujar el propio fondo del formulario con la imagen de cualquier archivo gráfico, en el evento Paint del formulario.
En principio, ésta alternativa sólo sería factible para los formularios normales y corrientes, es decir, aquellos que no sean MDI principales. En éstos últimos, aunque también se puede dibujar directamente sobre el fondo del formulario, no se vería absolutamente nada, porque como he indicado anteriormente en el apartado la clase MdiClient, el control MdiClient subyacente se superpone sobre el fondo del propio formulario MDI, y observaríamos el típico color gris oscuro característico, o cualquier otro color que se le haya asignado a la propiedad BackColor del control MdiClient. Nuevamente, la solución pasaría por detectar el control MdiClient subyacente, y dibujar la imagen sobre su propio fondo, utilizando para ello el evento Paint del objeto MdiClient:
Public
Class CreateMDIForm
Inherits System.Windows.Forms.Form
Private
WithEvents mdi
As MdiClient
Private img
As Image
Public
Shared
Sub Main()
Application.EnableVisualStyles()
Application.Run(New CreateMDIForm)
End
Sub
Public
Sub New()
InitializeComponent()
End
Sub
Private
Sub InitializeComponent()
Me.SuspendLayout()
'
'CreateMDIForm
'
Me.ClientSize =
New System.Drawing.Size(626, 266)
Me.Name =
"CreateMDIForm"
Me.Text = "Cómo crear un formulario MDI en
tiempo de ejecución."
Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
Me.ResumeLayout(False)
End
Sub
Protected
Overrides
Sub OnLoad(ByVal e As
EventArgs)
' Indicamos
que el formulario actuará de MDI principal.
'
Me.IsMdiContainer =
True
' Obtenemos
si procede el contenedor MdiClient del formulario
'
mdi = GetMdiContainer(Me)
' Si no es
un contenedor MDI abandonamos el procedimiento.
'
If (mdi
Is
Nothing)
Then
Return
' Creamos
el objeto Image.
'
img = Image.FromFile("C:\Mis
imagenes\Imagen1.jpg")
' Creamos
un formulario MDI secundario de demostración.
'
Dim frm
As Form =
New Form
With frm
.MdiParent =
Me
.Text =
"Formulario MDI secundario."
.StartPosition = FormStartPosition.CenterScreen
.Show()
End With
End
Sub
Protected
Overrides
Sub OnFormClosed( _
ByVal e
As System.Windows.Forms.FormClosedEventArgs)
'
Destruimos el objeto Image.
'
img.Dispose()
End
Sub
Private
Sub mdi_Paint(ByVal sender
As Object, _
ByVal e
As System.Windows.Forms.PaintEventArgs) _
Handles mdi.Paint
'
Referenciamos el objeto Graphics.
'
Dim grf
As Graphics = e.Graphics
' Dibujamos
dentro del área cliente del objeto MdiClient
' con el objeto Image
correspondiente.
'
grf.DrawImage(img, 0, 0, mdi.Width,
mdi.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.
'
grf.Dispose()
End
Sub
Private
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.
For
Each ctrl
As Control
In frm.Controls
If (TypeOf ctrl
Is MdiClient)
Then
Return
DirectCast(ctrl, MdiClient)
End
If
Next
' No existe
ningún control MdiClient.
'
Return
Nothing
End
Function
End
Class
Cómo dibujar el fondo de un formulario MDI con un archivo gráfico
Aplicando la técnica descrita en el ejemplo anterior, no es necesario asignar valores algunos a las propiedades BackgroundImage y BackgroundImageLayout, aunque sí 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 MdiClent declarado, donde dibujaremos el fondo de la aplicación utilizando para ello la imagen seleccionada, en lugar de hacerlo en el evento Paint del formulario MDI, porque aquí se dibujaría el fondo del formulario, pero nunca se visualizaría.
En fin. Espero que el artículo le haya servido para conocer un poco más los entresijos existentes en los formularios MDI principales, de las aplicaciones desarrolladas con Visual Basic .net.
Otros tips de interés:
Otros enlaces de interés:
Índice de la colección de ejemplos de las clases del marco de trabajo de .NET
Enrique Martínez Montejo - 2007
NOTA: La información contenida en este artículo, así como el código fuente incluido en el mismo, se proporciona COMO ESTÁ, sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo explicado, recomendado o sugerido en el presente artículo.
NOTE: The information contained in this article and source code included therein, is provided AS IS without warranty of any kind, and confers no rights. You assume any risk to implement, use or run it explained, recommended or suggested in this article.