Publicado el 14/Oct/2002
|
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:
- Si usamos esa API desde un formulario, la llamada a dicha función hay que hacerla en el evento Form_Initialize (no funciona en el evento Form_Load).
- En el caso de que tengamos más de un formulario, no es necesario realizar esa llamada en cada uno de los formularios, de hecho sólo es necesario hacerla antes de que se muestre ningún formulario.
- De hecho, si no se realiza la llamada antes de mostrar el primer formulario, la aplicación no se cargará... lo mismo ocurre si existe un .manifest y el ejecutable no está "preparado" para usarlos... es decir, no se usa la llamada a InitCommonControls.
- Por tanto, también puede llamarse a esa función del API desde el Sub Main (si es que ese procedimiento es el que sirve como punto de entrada a la aplicación).
- Hay que tener en cuenta que los OptionButtons (Options o RadioButtons) que se incluyan dentro de un Frame, se verán negros... por lo tanto no podrá leerse el texto, sin embargo si esos Options están fuera del Frame (o contenidos en un Picture, aunque estén en el Frame), se verán bien.
- Los frames que estén contenidos en otros frames no mostrarán el texto de forma correcta, (nuevamente se soluciona "metiendo" el segundo frame dentro de un picture).
- En las pruebas que he hecho, los CheckBoxes (Check) si se ven bien dentro de un Frame.
- Los botones que contienen imágenes no se muestran con el estilo de XP.
- Con respecto al fichero .manifest (el que tendrá el mismo nombre que el ejecutable además de esa extensión), hay que saber que no se pueden usar acentos ni eñes, al menos con el formato que "normalmente" se ve por ahí, el que usa encoding="UTF-8" (no se si hay alguna codificación que los acepte).
- Para ver el aspecto de un fichero .manifest, puedes ver la colaboración antes comentada o en la propia documentación de Visual Studio .NET
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.
GuillermoTemas 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 SubComo 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.
GuillermoP.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)
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
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); // }
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
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); } } } }