Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Usar temas de Windows XP con .NET Framework

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)


Publicado el 14/Oct/2002
Actualizado el 27/Jul/2003


Nota del 30/Oct/02:
Si quieres saber cómo solventar los problemas que se pueden presentar con el Visual Basic clásico, sigue este link.
Nota del 27/Dic/02: Por "olvido" no actualicé está página... y este comentario no estaba visible.
27/Jul/2003: Cómo crear temas de XP con Visual Studio .NET 2003 sin usar un .manifest

 

Hace ya unos cuantos meses, (en enero de este año), publiqué una colaboración de Víctor Sánchez sobre cómo usar los temas de Windows XP en Visual Basic (versión clásica) y como resulta que este tema está "floreciendo" nuevamente, (me refiero a que he visto varias consultas en las news), pues creo que es conveniente que explique cómo hacerlo en .NET Framework (válido tanto para Visual Basic .NET como para C#)

Recordando cómo hacerlo con VB clásico:
Lo primero que hay que decir, es que, para que en las versiones de Visual Basic anteriores a .NET, se puedan usar los estilos de Windows XP, tenemos que hacer una llamada al API de Windows, en concreto a InitCommonControls.

En lo que concierne a los temas en Visual Basic clásico, aquí te comento algunas "cosillas" que deberías saber:

 

Usar los temas de Windows XP con lenguajes de .NET Framework

Una vez aclarado un poco el tema de las versiones de Visual Basic anteriores a .NET, vamos a ver que es lo que tenemos que hacer para que las aplicaciones creadas con los lenguajes .NET utilicen los temas de Windows XP.

En teoría sólo es necesario añadir un fichero .manifest al mismo path que el ejecutable.
No es necesario hacer ninguna llamada al API InitCommonControls.
El problema es que no todos los controles adoptarían el nuevo look. En realidad los controles basados en "button", tal como el propio botón o los Options y Check, además del GroupBox (lo que en VB es un Frame) y las etiquetas no mostrarán el nuevo aspecto, al menos de forma predeterminada.
¿Cómo solucionarlo?
Muy fácil, cambiando la propiedad FlatStyle para que tenga un valor System.
Esa propiedad, por defecto tiene el valor Standard, y gracias a ese valor, podemos incluir imágenes en esos controles... si cambiamos el valor de Standard a System, podremos darle el aspecto de los temas de Windows XP, pero perderemos la posibilidad de que puedan contener imágenes o, en el caso de las etiquetas, que la alineación del texto pueda ser cualquiera de las que podemos asignar a TextAlign, ya que si cambiamos el FlatStyle a System, sólo se podrá usar la posición TopLeft, (que es la predeterminada).

Pero además de usar el fichero .manifest, también podemos incluir ese fichero en el ejecutable como un recurso, así nos evitaremos tener que incluir dicho fichero en el mismo directorio que el ejecutable, con la posibilidad de que algún "graciosillo" lo elimine, quitándole el nuevo look a la aplicación.

¿Cómo incluir el .manifest en el propio ejecutable?

Primero tendremos que crear el fichero .manifest, el cual tendrá el siguiente aspecto:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="CompanyName.ProductName.YourApplication"
    type="win32"
/>
<description>Your application description here.</description>
<dependency>
    <dependentAssembly>
       <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="X86"
          publicKeyToken="6595b64144ccf1df"
          language="*"
       />
    </dependentAssembly>
</dependency>
</assembly>

Dicho fichero se llamará igual que el ejecutable (incluida la extensión) y tendrá como extensión .manifest, aunque para poder incluirlo como recurso no es necesario que tenga esa "nomenclatura".

Abrimos el Visual Studio y seleccionamos "abrir archivo" del menú archivos (File>Open>File...) y buscamos el ejecutable en el que queramos insertar el .manifest.


Se mostrarán los recursos que actualmente tenga.
Pulsamos con el botón derecho del ratón y seleccionamos "Añadir recurso..."


Se mostrará un cuadro de diálogo en el cual pulsaremos en el botón "Importar..."


Seleccionamos el fichero .manifest que hemos creado:


Y nos pedirá el nombre del tipo de recurso, escribimos RT_MANIFEST y pulsamos aceptar.


Se mostrará dicho fichero en formato binario, no le hacemos ni caso y pulsamos en la ventana del ejecutable.
Seleccionamos el nuevo recurso, para que se muestre la ventana de propiedades.


En la cual cambiaremos el ID para que indique un uno.


Por último guardamos el ejecutable.

Ya podemos usar el ejecutable para que use los temas de Windows XP sin necesidad de incluir un fichero .manifest.
Nota: Si quieres, puedes borrar el fichero .manifest, ya que no lo necesitarás más, salvo que modifiques el ejecutable.

 

Automatizar el cambio de FlatStyle del valor Standard a System

Debido a que puede ser tedioso tener que cambiar la propiedad FlatStyle en todos los controles que tengamos en nuestro formulario, te muestro a continuación un procedimiento (y cómo usarlo), para hacerlo de forma automática.

El código de ejemplo es tanto  para Visual Basic .NET como para C#, en ambos se tiene en cuenta si los botones tienen imágenes asignadas, en cuyo caso no se cambia el valor de la propiedad FlatStyle.
En las capturas de los formularios, el control con la bola roja es un botón con ese icono como imagen.
Realmente habría que tener en cuenta también otras propiedades, por si esa imagen no está asignada directamente sino por medio de un ImageList... pero esas pruebas no las he hecho... (y tampoco se si realmente son necesarias).

Debido a que, a diferencia de las versiones anteriores de Visual Basic, en la colección Controls del formulario ya no se incluyen todos los controles, tenemos que "revisar" cada uno de los controles para saber si a su vez estos contienen otros controles, con idea de que también se cambie el valor de la propiedad FlatStyle... cuando veas el código seguramente lo comprenderás.

En el código he añadido un timer, para que el formulario se muestre durante unos segundos con el aspecto que tendría si no se cambiaran los valores de FlatStyle y después el que tendrá una vez cambiado al valor System.

Este es el aspecto del formulario, tanto para VB.NET como para C#.


El formulario en tiempo de diseño.

 


El formulario en ejecución, con los temas aplicados.

 

Convertir el procedimiento en uno genérico.
Si quieres, puedes convertir el procedimiento en una clase, con idea de que lo puedas usar de forma genérica en cualquier formulario. En el código de dicha clase, (en el cual el método de conversión está declarado Shared, para que puedas usarlo sin necesidad de crear una nueva instancia), no se hacen asignaciones al ListBox ni al ListView ya que esas asignaciones sólo las he usado para que puedas ver los controles contenedores y los que no lo son.

El código de la clase (CambiarEstiloXP), se muestra tanto para Visual Basic .NET como para C#
 

Espero que todo este "rollo" te pueda ser de utilidad o que al menos aclare un poco este tema de los "temas" de Windows XP.

¡Que lo disfrutes!

Nos vemos.
Guillermo


Temas XP para Visual Studio .NET 2003 (.NET Framework 1.1)
(sin crear ficheros .manifest ni modificar los recursos)

Pues que mirando otras cosillas del Visual Studio .NET 2003 (el que usa el .NET Framework 1.1), me encontré con un método de la clase Application que permite indicarle al .NET de que utilice los temas de Windows XP en las aplicaciones creadas con el .NET Framework 1.1 sin necesidad de crear un fichero .manifest.

Te voy a explicar como si fuese un FAQ... a ver si así lo entiendes a la primera... (es que hoy ma dao por ahí)

¿Cómo se consigue usar los estilos XP sin usar un fichero .manifest?
Esto se consigue llamando al método EnableVisualStyles de la clase Application.

¿Dónde hay que hacer esa asignación?
Esa asignación hay que hacerla al principio del método Sub Main, antes del Application.Run(New nombreFormulario).
Que, por otra parte, es lo que se suele hacer en cualquier aplicación de .NET, aunque nosotros no lo hagamos...

Si nosotros no escribimos ese código... ¿quién lo escribe por nosotros?
Bueno, debo aclarar que sólo los que usemos Visual Basic .NET no tenemos que escribir ese código del Sub Main en las aplicaciones de Windows, ya que es el propio IDE de Visual Studio .NET el que lo hace por nosotros.
Si programas con C#, ese código lo verás, ya que el Visual Studio .NET lo incorpora en los nuevos proyectos de C#, pero para los programadores de Visual Basic, el VS lo oculta, será para que no nos liemos... o porque nos consideran más "torpes"... en fin...

Pero... si quiero incluir esa línea para que use los temas de XP... ¿cómo "puñetas" lo hago?
Pues... escribiendo ese procedimiento por tu cuenta...

Vale, muy bien, pero... ¿cómo escribo ese código?
Ah, aquí quería yo llegar... je, je, ¿comprendes ahora porqué se nos considera más "torpes"...? (es broma)
El VS.NET no oculta el procedimiento Main a los programadores de Visual Basic .NET, (ya que realmente no está en ningún lado del código), lo que hace es añadirlo al ejecutable, pero si lo escribimos nosotros, el VS lo respeta y lo deja, (además de usarlo para iniciar la aplicación).
El código lo puedes escribir en cualquier parte de la "clase" del formulario.

¡Ah!, que lo que quieres es que te muestre el código del procedimiento Main... ¡pues haberlo dicho antes!
Aquí tienes uno que se usaría para iniciar un formulario llamado Form1:

<STAThread()> _
Public Shared Sub Main()
    ' habilitar los estilos XP sin necesidad de crear un .manifest
    ' para que sea efectivo, en los controles que tengan la propiedad
    ' FlatStyle, debe asignarse el valor System
    ' (por defecto tienen el valor: Standard)
    Application.EnableVisualStyles()
    Application.Run(New Form1)
End Sub

Como habrás podido adivinar, (si has leído los comentarios), no sólo basta con añadir esa llamada al método EnableVisualStyles, sino que además hay que cambiar el valor de la propiedad FlatStyle (de los controles que la tengan, como por ejemplo las etiquetas y botones), y asignarle el valor FlatStyle.System.

Este link de la ayuda de Visual Studio .NET 2003 te dará más información sobre el método EnableVisualStyles:
ms-help://MS.MSDNQTR.2003FEB.3082/cpref/html/frlrfSystemWindowsFormsApplicationClassEnableVisualStylesTopic.htm

¿Puedo automatizar el cambio del valor de la propiedad FlatStyle?
Si no quieres cambiar manualmente el valor de FlatStyle, (que por defecto es Standard), a System, puedes hacerlo automáticamente... Eso es lo que hace precisamente el código de mi clase CambiarEstiloXP mostrado más abajo, (este es el link del código para VB y este otro es el código para C#)

Bueno... aparte del cachondeo... espero que ahora tengas más claro cómo poder usar los estilos XP en tus aplicaciones, ya sean de Visual Basic clásico como de .NET (en cualquiera de sus versiones).

Por supuesto, como ya he dicho, esto también funciona para C#. El código para C# del procedimiento Main quedaría así:

[STAThread]
static void Main() 
{
    Application.EnableVisualStyles();
    Application.Run(new Form1());
}

En C++ (creo) no se usa el procedimiento Main de la misma forma que en VB y C#, por tanto podemos llamar a EnableVisualStyles en el constructor de la clase, algo así:

Form1(void)
{
    Application::EnableVisualStyles();
    InitializeComponent();
}

Por supuesto, en los proyectos de Visual Basic .NET y de C# también podemos llamar al método que habilita los "estilos visuales" en el constructor del formulario. En el caso de Visual Basic, habrá que hacerlo justo después de la llamada al constructor de la clase base (MyBase.New)

Seguramente te preguntarás que si se puede hacer en el constructor (que siempre está incluido en el código) ¿por qué he dicho que habría que hacerlo en el método Main? Pues, porque eso es lo que la documentación muestra en los ejemplos... y como yo me fío de lo que dice la documentación, (o casi), pues... pero al probar el código para C++ y tener que incluirlo en el constructor, (ya que el ejemplo de la documentación usa un método main, pero no funciona... ¡para que te fíes de la documentación!), pues lo probé con el VB y también funcionó... así que... pues eso...
Indagando un poco en los ficheros incluidos en el proyecto de C++, he visto que el procedimiento main realmente se llama APIENTRY _tWinMain y se incluye en el fichero Form1.cpp... es que... esto del C++ definitivamente no es lo mío... y eso que con el Visual Studio .NET 2003 la cosa es más fácil...

En fin, que ya está bien... que es mu tarde y mañana quiero ir a la playa... ¡hasta luego!

Nos vemos.
Guillermo

P.S.
Aquí tienes el listado de los ejemplos que he utilizado, tanto para Visual Basic .NET como para C# y C++, pero recuerda que sólo funcionará con la versión 2003 del Visual Studio .NET (o con el SDK del .NET Framework 1.1)
Este es el link al ZIP con el código de ejemplo: vs2003EnableVisualStyles.zip (23.1 KB)


 

Código para Visual Basic.NET (VB.NET) El código para Visual Basic .NET

Este es el código del procedimiento que hace las asignaciones a la propiedad FlatStyle:
Aquí se tiene en cuenta si el control es un botón y si tiene una imagen asignada, de ser así, no se cambia el valor de FlatStyle.


Private Sub cambiarEstilo(ByVal tControl As Control)
    ' Cambiar el estilo del control...
    ' sólo si es uno de los indicados
    Select Case tControl.GetType.Name
        Case "Label"
            CType(tControl, Label).FlatStyle = FlatStyle.System
        Case "CheckBox"
            CType(tControl, CheckBox).FlatStyle = FlatStyle.System
        Case "RadioButton"
            CType(tControl, RadioButton).FlatStyle = FlatStyle.System
        Case "Button"
            ' Si el botón tiene asignada la propiedad Image     (11/Oct/02)
            ' no cambiarlo...
            Dim tButton As Button
            tButton = CType(tControl, Button)
            If tButton.Image Is Nothing Then
                tButton.FlatStyle = FlatStyle.System
            End If
            'CType(tControl, Button).FlatStyle = FlatStyle.System
        Case "GroupBox"
            CType(tControl, GroupBox).FlatStyle = FlatStyle.System
    End Select
    '
    ' Cambiar también los controles contenidos en cada control...
    ' Mostrar la información en el ListView
    If tControl.Controls.Count > 0 Then
        With ListView1.Items.Add(tControl.Name)
            .SubItems.Add(tControl.Controls.Count.ToString)
        End With
        '
        Dim tControl2 As Control
        '
        For Each tControl2 In tControl.Controls
            cambiarEstilo(tControl2)
        Next
    Else
        ListBox1.Items.Add(tControl.Name)
    End If
End Sub

 

Esta es la forma de usarlo, realmente no es necesario que la llamada se haga en el evento Tick del temporizador, se puede hacer en el evento Load del formulario.
El usarlo en el temporizador es sólo para que se muestre el aspecto "normal" durante unos segundos y después se vea el cambio al nuevo look.


Private Sub fTemasXP_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    '
    With ListView1
        .Columns.Clear()
        .Columns.Add("Contenedor", 90, HorizontalAlignment.Left)
        .Columns.Add("controles", 60, HorizontalAlignment.Right)
        .Items.Clear()
    End With
    '
    ListBox1.Items.Clear()
    '
    Show()
    '
    ' Mostrar el formulario y después cambiar los estilos
    ' daremos tres segundos de margen... para que se vea la diferencia
    Timer1.Interval = 3000
    Timer1.Enabled = True
End Sub
'
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    ' Desconectamos el timer, ya no lo necesitamos más...
    Timer1.Enabled = False
    '
    ' Cambiamos los estilos de los controles
    ' como es una función recursiva, empezamos con el formulario
    cambiarEstilo(Me)
    '
End Sub

 


Código para C Sharp (C#)El código para C#

Este es el código del procedimiento que hace las asignaciones a la propiedad FlatStyle:
Aquí se tiene en cuenta si el control es un botón y si tiene una imagen asignada, de ser así, no se cambia el valor de FlatStyle.


private void cambiarEstilo(Control tControl)
{
    // Cambiar el estilo del control...
    // sólo si es uno de los indicados
    switch(tControl.GetType().Name)
    {
        case "Label":
            ((Label)tControl).FlatStyle = FlatStyle.System;
            break;
        case "CheckBox":
            ((CheckBox)tControl).FlatStyle = FlatStyle.System;
            break;
        case "RadioButton":
            ((RadioButton)tControl).FlatStyle = FlatStyle.System;
            break;
        case "Button":
            //((Button)tControl).FlatStyle = FlatStyle.System;
            Button tButton = (Button)tControl;
            if(tButton.Image == null)
                tButton.FlatStyle = FlatStyle.System;
            break;
        case "GroupBox":
            ((GroupBox)tControl).FlatStyle = FlatStyle.System;
            break;
    }
    //
    // Cambiar también los controles contenidos en cada control...
    //
    // Si este control contiene controles...
    if( tControl.Controls.Count > 0 )
    {
        // Mostrar la información en el ListView
        ListViewItem tItem = ListView1.Items.Add(tControl.Name);
        tItem.SubItems.Add(tControl.Controls.Count.ToString());
        //
        // Procesar cada control contenido en este control
        foreach(Control tControl2 in tControl.Controls)
            cambiarEstilo(tControl2);
    }
    else
        // si no contiene más controles, añadirlo al ListBox
        ListBox1.Items.Add(tControl.Name);
}

 

Esta es la forma de usarlo, realmente no es necesario que la llamada se haga en el evento Tick del temporizador, se puede hacer en el evento Load del formulario.
El usarlo en el temporizador es sólo para que se muestre el aspecto "normal" durante unos segundos y después se vea el cambio al nuevo look.


private void fTemasXP_Load(object sender, System.EventArgs e)
{
    //
    ListView1.Columns.Clear();
    ListView1.Columns.Add("Contenedor", 90, HorizontalAlignment.Left);
    ListView1.Columns.Add("controles", 60, HorizontalAlignment.Right);
    ListView1.Items.Clear();
    //
    ListBox1.Items.Clear();
    //
    this.Show();
    //
    // Mostrar el formulario y después cambiar los estilos
    // daremos tres segundos de margen... para que se vea la diferencia
    timer1.Interval = 3000;
    timer1.Enabled = true;
}

private void timer1_Tick(object sender, System.EventArgs e)
{
    //
    // Desconectamos el timer, ya no lo necesitamos más...
    timer1.Enabled = false;
    //
    // Cambiamos los estilos de los controles
    // como es una función recursiva, empezamos con el formulario
    cambiarEstilo(this);
    //
}

Código para Visual Basic .NET (VB.NET)
El código de la clase genérica (para Visual Basic .NET)

Para usarla simplemente se indicará el nombre de la clase y el procedimiento:
CambiarEstiloXP.CambiarEstilo(Me)
 

 


'------------------------------------------------------------------------------
' Clase para cambiar los estilos para poder usar temas de XP        (11/Oct/02)
'
' ©Guillermo 'guille' Som, 2002
'------------------------------------------------------------------------------

Public Class CambiarEstiloXP
    ' Al estar declarado como Shared, podemos usarlo sin crear
    ' una instancia de la clase
    Public Shared Sub CambiarEstilo(ByVal tControl As Control)
        ' Cambiar el estilo del control...
        ' sólo si es uno de los indicados
        Select Case tControl.GetType.Name
            Case "Label"
                CType(tControl, Label).FlatStyle = FlatStyle.System
            Case "CheckBox"
                CType(tControl, CheckBox).FlatStyle = FlatStyle.System
            Case "RadioButton"
                CType(tControl, RadioButton).FlatStyle = FlatStyle.System
            Case "Button"
                ' Si el botón tiene asignada la propiedad Image     (11/Oct/02)
                ' no cambiarlo...
                Dim tButton As Button = CType(tControl, Button)
                If tButton.Image Is Nothing Then
                    tButton.FlatStyle = FlatStyle.System
                End If
            Case "GroupBox"
                CType(tControl, GroupBox).FlatStyle = FlatStyle.System
        End Select
        '
        ' Cambiar también los controles contenidos en cada control...
        If tControl.Controls.Count > 0 Then
            Dim tControl2 As Control
            '
            For Each tControl2 In tControl.Controls
                CambiarEstilo(tControl2)
            Next
        End If
    End Sub
End Class

Código para C Sharp (C#)
El código de la clase genérica (para C#)

Para usarla simplemente se indicará el nombre de la clase y el procedimiento:
CambiarEstiloXP.cambiarEstilo(this)
 


using System;
using System.Windows.Forms;

namespace XPThemeCS
{
    /// 
    /// Summary description for CambiarEstiloXP.
    /// 
    public class CambiarEstiloXP
    {
        public static void cambiarEstilo(Control tControl)
        {
            // Cambiar el estilo del control...
            // sólo si es uno de los indicados
            switch(tControl.GetType().Name)
            {
                case "Label":
                    ((Label)tControl).FlatStyle = FlatStyle.System;
                    break;
                case "CheckBox":
                    ((CheckBox)tControl).FlatStyle = FlatStyle.System;
                    break;
                case "RadioButton":
                    ((RadioButton)tControl).FlatStyle = FlatStyle.System;
                    break;
                case "Button":
                    Button tButton = (Button)tControl;
                    if(tButton.Image == null)
                        tButton.FlatStyle = FlatStyle.System;
                    break;
                case "GroupBox":
                    ((GroupBox)tControl).FlatStyle = FlatStyle.System;
                    break;
            }
            //
            // Cambiar también los controles contenidos en cada control...
            //
            // Si este control contiene controles...
            if( tControl.Controls.Count > 0 )
            {
                // Procesar cada control contenido en este control
                foreach(Control tControl2 in tControl.Controls)
                    cambiarEstilo(tControl2);
            }
        }
    }
}

la Luna del Guille o... el Guille que está en la Luna...