Colección de iconos Silk ampliada

Son muchas las librerías de Iconos gratuitas, pero pocas tan buenas y profesionales como Silk. La verdad es que he usado sus iconos en infinidad de proyectos y todavía no me he cansado de ellos. Por si las moscas, la gente de FAMFAMFAM ha ampliado la colección actualizando la versión de la colección a la 1.3, dándole todavía más posibilidades a esta magnífica colección.


Suerte tenemos de este tipo de iniciativas los que hemos nacido tan negados para el tema del diseño…


[Crossposting desde http://www.tonirecio.com]

Primer aniversario MorePlus.es

Como se dice en estos casos, parece que fue ayer, pero está claro que no, que ya han pasado 365 días, y que día a día el proyecto iniciado por Javier Conesa tiene más seguidores y a buen seguro que seguirá así muchos años más.


También está claro que el proyecto no se puede catalogar como novedoso, y no goza del respaldo de grandes patrocinadores o empresas, pero cuenta con algo mucho mejor, el cariño con el que está hecho. Tanto Javier como los colaboradores entre los cuales tengo el privilegio de constar, participamos del proyecto con muchas ganas de compartir experiencias y conocimientos, y creo que las visitas registradas en el portal hacen justicias a este empeño, con lo que tanto colaboradores como lectores hacen de moreplus.es una iniciativa de las que enriquecen a todos, lo que le asegura un futuro espléndido y muchos éxitos.


MoreCongratulations!!!


[Crossposting desde http://www.tonirecio.com]

Desktop Window Manager, los entresijos de la interfaz visual de Windows Vista (2ª Parte)

En la primera parte del presente artículo introducimos los conceptos básicos de la composición de escritorio y las particularidades de esta tecnología aplicadas a Windows Vista. También vimos cómo empezar a trabajar con esta tecnología desde .NET. En esta segunda entrega trataremos de implementar un pequeño ejemplo algo más completo. El objetivo que vamos a tratar de encarar no es más que una simple aplicación que emule el Flip de Windows Vista. Manos a la obra; El Flipador Pues bien, del dicho al hecho. Vamos a tratar de hacer nuestro propio conmutador de ventanas, al cual me he atrevido a bautizar como El Flipador J. El trabajo se puede desglosar mediante los siguientes pasos:


  1. Obtener todas las ventanas abiertas por el sistema operativo.

  2. Crear una ventana para mostrar las miniaturas, a la que redimensionaremos en función del número de ventanas obtenidas en el paso anterior.

  3. Dibujar las miniaturas en la ventana.

  4. Aplicar el efecto glass.
NOTA: Puede resultarnos útil, de cara a simplificar la programación, conocer una de las limitaciones que Windows Vista asigna a la gestión de ventanas en modo de composición de escritorio, y es que “únicamente” podremos mantener abiertas de forma simultánea 52 ventanas. ¿Suficiente? Espero que sí… Para recuperar todas las ventanas abiertas recurriremos al API de Windows user32.dll, y más concretamente la función EnumWindows, a la que indicaremos un delegado que será invocado para cada una de las ventanas, mediante el cual determinaremos que pantallas deben procesarse y dónde crearemos la lista de ventanas definitiva que conformarán el conjunto de miniaturas. Para cada iteración de EnumWindows se proporciona un puntero a una ventana, que usaremos en conjunto con la función GetWindowLongA, también incluida en user32.dll, para obtener todos los datos del formulario, mediante los cuales podemos determinar si la ventana tiene bordes y título, en cuyo caso la añadiremos a nuestra lista. Como último recurso de la librería user32.dll también usaremos la función GetWindowText, con la que podremos obtener el título de la ventana. Para confeccionar la lista, usaremos una pequeña clase con los datos que necesitemos de los obtenidos de GetWindowLongA y GetWindowText. De este modo podremos dar por finalizado el primer paso de nuestro proyecto (Código 4). Definición de constantes (Código 4a)
1 public static readonly int GWL_STYLE = 16;
2 public static readonly ulong WS_VISIBLE = 0x10000000L;
3 public static readonly ulong WS_BORDER = 0x00800000L;
4 public static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;
Importar declaraciones (Código 4b)
 1 //Gestión de ventanas Win32
2 [DllImport(«user32.dll«)]
3 public static extern int EnumWindows(EnumWindowsCallback
4 funcionCallBack, int parametrosFuncion);
5 public delegate bool EnumWindowsCallback(IntPtr manejador, int
6 parametros);
7 [DllImport(«user32.dll«)]
8 public static extern ulong GetWindowLongA(IntPtr manejador, int
9 indice);
10 [DllImport(«user32.dll«)]
11 public static extern void GetWindowText(IntPtr hWnd, StringBuilder
12 lpString, int nMaxCount);
Clase Ventana (Código 4c)
1 class Ventana
2 {
3 public string Titulo;
4 public IntPtr Manejador;
5 }
Implementación (Código 4d)
 1 List<Ventana> ventanas;
2
3 private bool Callback(IntPtr manejador, int parametros)
4 {
5 //Comprobar que no se trate de la propia ventana
6 //y recoger la información de la ventana en caso de
7 //tener bordes y título
8 if (this.Handle != manejador && (GetWindowLongA(manejador, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
9 {
10 StringBuilder tituloVentana = new StringBuilder(100);
11 GetWindowText(manejador, tituloVentana, tituloVentana.Capacity);
12 Ventana ventana = new Ventana();
13 ventana.Manejador = manejador;
14 ventana.Titulo = tituloVentana.ToString();
15 ventanas.Add(ventana);
16 }
17 return true;
18 }
19 //Obtener lista de ventanas
20 ventanas = new List<Ventana>();
21 EnumWindows(Callback, 0);
Ahora nos toca determinar que espacio debe ubicar cada una de las miniaturas, y que tamaño debe tener la pantalla contenedora. Para ello iteraremos la lista de ventanas que ya tenemos confeccionada, y para cada una de ellas crearemos una miniatura y calcularemos la posición de la misma. Usaremos también la ubicación calculada para dibujar un panel de forma paralela a cada una de las miniaturas, lo que nos permitirá simplificar la tarea de capturar eventos relacionados con las miniaturas, ya que pintaremos los nuevos paneles con negro, consiguiendo así que una vez que apliquemos el efecto glass los paneles no se muestren, pudiendo emular el efecto de que el panel responde a los eventos de la miniatura correspondiente (Código 5). Definición de constantes (Código 5a)
1 public static readonly int DESPLAZAMIENTO_VERTICAL = 40;
2 public static readonly int ANCHO_MINIATURA = 160;
3 public static readonly int ALTO_MINIATURA = 120;
4 public static readonly int ESPACIADO_HORIZONTAL = 10;
5 public static readonly int ESPACIADO_VERTICAL = 10;
6 public static readonly int MAXNUM_MINIATURAS_HORIZONTAL = 4;
Implementación (Código 5b)
 1 Form flipador = new Form();
2 //Iniciar proceso de cálculo del tamaño de la ventana
3 int anchoVentana = 10 + ESPACIADO_HORIZONTAL;
4 int altoVentana = DESPLAZAMIENTO_VERTICAL + 30 + ESPACIADO_VERTICAL +
5 ALTO_MINIATURA + ESPACIADO_VERTICAL;
6 int fila = 1; int columna = 1;
7 //Recorrer cada una de las ventanas
8 foreach (Ventana ventana in ventanas)
9 {
10 //Crear panel receptor de eventos
11 Panel p = new Panel();
12 p.Tag = ventana;
13 IntPtr miniatura;
14 p.BackColor = Color.Black;
15 p.Parent = flipador;
16 p.Top = DESPLAZAMIENTO_VERTICAL + ESPACIADO_VERTICAL + (fila 1) * (ALTO_MINIATURA + ESPACIADO_VERTICAL);
17 p.Left = ESPACIADO_HORIZONTAL + (columna 1) * (ANCHO_MINIATURA + ESPACIADO_HORIZONTAL);
18 p.Height = ALTO_MINIATURA; p.Width = ANCHO_MINIATURA;
19 //Capturar eventos del panel
20 p.MouseMove += new
21 System.Windows.Forms.MouseEventHandler(panel_MouseMove);
22 p.MouseClick += new
23 System.Windows.Forms.MouseEventHandler(panel_Click);
24 //Registrar miniatura
25 miniatura = DwmRegisterThumbnail(flipador.Handle, (IntPtr)(ventana.Manejador));
26 ActualizarMiniatura(miniatura, p.Left, p.Top, p.Left + ANCHO_MINIATURA, p.Top + ALTO_MINIATURA);
27 //Recalcular tamaño ventana
28 if (fila == 1) anchoVentana = anchoVentana + ESPACIADO_HORIZONTAL + ANCHO_MINIATURA;
29 if (columna == MAXNUM_MINIATURAS_HORIZONTAL)
30 {
31 fila += 1; columna = 0;
32 altoVentana = altoVentana + ESPACIADO_VERTICAL + ALTO_MINIATURA;
33 }
34 columna += 1;
35 }
36 if (columna == 1) altoVentana = altoVentana ESPACIADO_VERTICAL ALTO_MINIATURA;
37 flipador.Height = altoVentana;
38 flipador.Width = anchoVentana;
39 flipador.Refresh();
40 flipador.Show();
Ya que hemos aprovechado los paneles para capturar eventos, usaremos MouseMove mostrar el título de la ventana representada por la miniatura que el cursor del ratón sobre vuele, y el evento MouseClick para maximizar dicha ventana. Mostrar la ventana seleccionada Implementaremos el evento MouseClick con la ayuda de la función SetWindowPos de la librería user32.dll. Invocaremos una primera vez la función para activar la pantalla, y una segunda, para maximizarla por encima de la ventana de selección (Código 6). Definición de constantes (Código 6a)
1 public static readonly int SWP_SHOWWINDOW = 3;
Importar declaraciones (Código 6b)
1 [DllImport(«user32.DLL«)]
2 private extern static void SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int wFlags);
Implementación (Código 6c)
1 private void panel_Click(object sender, EventArgs e)
2 {
3 Panel p;
4 Ventana v;
5 p = (Panel)sender;
6 v = (Ventana)p.Tag;
7 ShowWindow(v.Manejador, 0);
8 ShowWindow(v.Manejador, SWP_SHOWWINDOW);
9 }
Mostrar títulos de ventana De cara a mostrar el título de la miniatura la primera opción será usar un control label, pero este camino acabaremos por desestimarlo debido a problemas como la máscara de trasparencia (dificultades para mostrar texto en negro), o por el renderizado que afecta a la calidad de los gráficos, produciendo contornos entrecortados en el perfil de los textos si queremos mostrarlo sobre la superficie semitransparente. Dado que al escribir sobre una superficie glass estamos en un entorno totalmente gráfico, hemos de usar herramientas capaces de suavizar el contorno de nuestros textos para mostrar resultados más profesionales. La buena noticia es que por fin entramos en territorio administrado, es decir, dejamos de un lado las API’s para trabajar con librerías nativas de .NET, y en este caso concreto System.Drawing, mediante la cual usaremos la tecnología GDI+ para llevar a cabo nuestros propósitos. Ahora podremos escribir textos en color negro sin problemas con las transparencias (Código 7).
 1 Graphics g = flipador.CreateGraphics();
2
3 private void panel_MouseMove(object sender, MouseEventArgs e)
4 {
5 Panel p;
6 Ventana v;
7 p = (Panel)sender;
8 v = (Ventana)p.Tag;
9 g.Clear(Color.Black);
10 GraphicsPath lienzo = new GraphicsPath();
11 SolidBrush brocha = new SolidBrush(Color.Black);
12 lienzo.AddString(v.Titulo, new FontFamily(«Arial«), (int)FontStyle.Bold, 20, new Point(10, 10), StringFormat.GenericDefault);
13 g.SmoothingMode = System.Drawing.Drawing2D.
14 SmoothingMode.HighQuality;
15 g.FillPath(brocha, lienzo);
16 }

Puliendo el resultado Nuestro Flipador v1.0 ya está terminado (Imagen 5), y como podemos observar tiene un aspecto francamente atractivo. Le hemos aplicado el efecto glass tal y como mostrábamos en el segundo fragmento de código, y podemos sentirnos satisfechos por el trabajo, a no ser que seamos un “pelín” quisquillosos. Vamos a fijarnos en el siguiente detalle: Si examinamos con atención la imagen 5 y comparamos el texto del título de la ventana de la aplicación, con el texto del título de la miniatura, veremos que a parte de la obvia diferencia de tamaños, el título del formulario muestra un atractivo detalle en forma de sombreado claro, que facilita la lectura al contrastar con el color de la fuente. Este detalle se escapa del ámbito de competencias de DWM, siendo una característica concreta del tema visual Aero Glass, con lo que si queremos conseguir los mismos resultados en nuestra aplicación deberemos recurrir a la librerías que usa Windows para gestionar los temas visuales del escritorio, UxTheme.dll y Gdi32.dll. La técnica a grandes rasgos consiste en capturar el espacio de memoria donde se mostrará el texto, dibujarlo, y solicitar al gestor de estilo que lo renderice según unos parámetros determinados; en este caso, el efecto “glaseado” bajo el texto (Código 8).


Imagen 5


image


Definición de constantes y estructuras (Código 8a)

 1 private const int DTT_COMPOSITED = 8192;
2 private const int DTT_GLOWSIZE = 2048;
3 private const int DTT_TEXTCOLOR = 1;
4
5 [StructLayout(LayoutKind.Sequential)]
6 private class BITMAPINFO {
7 public int biSize; public int biWidth; public int biHeight;
8 public short biPlanes; public short biBitCount;
9 public int biCompression; public int biSizeImage;
10 public int biXPelsPerMeter; public int biYPelsPerMeter;
11 public int biClrUsed; public int biClrImportant;
12 public byte bmiColors_rgbBlue; public byte bmiColors_rgbGreen; public byte bmiColors_rgbRed;
13 public byte bmiColors_rgbReserved;}
14
15 [StructLayout(LayoutKind.Sequential)]
16 private struct RECT {
17 public int Left; public int Top; public int Right; public int Bottom;
18 public RECT(int left, int top, int right, int bottom) {
19 Left = left; Top = top; Right = right; Bottom = bottom; }
20 public RECT(Rectangle rectangle) {
21 Left = rectangle.X; Top = rectangle.Y; Right = rectangle.Right; Bottom = rectangle.Bottom; }
22 public Rectangle ToRectangle() {
23 return new Rectangle(Left, Top, Right Left, Bottom Top); }
24 public override string ToString() {
25 return «Left: « + Left + «, « + «Top: « + Top + «, Right: « + Right + «, Bottom: « + Bottom; } }
26
27 [StructLayout(LayoutKind.Sequential)]
28 private struct POINT {
29 public POINT(int x, int y) { this.x = x; this.y = y; }
30 public int x; public int y; }
31
32 StructLayout(LayoutKind.Sequential)]
33 private struct DTTOPTS {
34 public int dwSize; public int dwFlags;
35 public int crText; public int crBorder;
36 public int crShadow; public int iTextShadowType;
37 public POINT ptShadowOffset;
38 public int iBorderSize; public int iFontPropId;
39 public int iColorPropId; public int iStateId;
40 public bool fApplyOverlay;
41 public int iGlowSize; public int pfnDrawTextCallback;
42 public IntPtr lParam; }
43
Importar declaraciones (Código 8b)
 1 [DllImport(«gdi32.dll«, ExactSpelling = true, SetLastError = true)]
2 private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
3 [DllImport(«gdi32.dll«, ExactSpelling = true)]
4 private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
5 [DllImport(«gdi32.dll«, ExactSpelling = true, SetLastError = true)]
6 private static extern bool DeleteObject(IntPtr hObject);
7 [DllImport(«gdi32.dll«, ExactSpelling = true, SetLastError = true)]
8 private static extern bool DeleteDC(IntPtr hdc);
9 [DllImport(«gdi32.dll«)]
10 private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr
11 hdcSrc, int nXSrc, int nYSrc, uint dwRop);
12 [DllImport(«UxTheme.dll«, CharSet = CharSet.Unicode)]
13 private static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref RECT pRect, ref DTTOPTS pOptions);
14 [DllImport(«gdi32.dll«)]
15 private static extern IntPtr CreateDIBSection(IntPtr hdc, BITMAPINFO pbmi, uint iUsage, int ppvBits, IntPtr hSection, uint dwOffset);
Implementación (Código 8c)
 1 private void panel_MouseMove(object sender, MouseEventArgs e)
2 {
3 Panel p;
4 Ventana v;
5 p = (Panel)sender;
6 v = (Ventana)p.Tag;
7 //Identificar el dispositivo de video
8 IntPtr salida = g.GetHdc();
9 //Crear un contexto del dispositivo de video
10 IntPtr contextoSalida = CreateCompatibleDC(salida);
11 //Crear estructura de imagen
12 BITMAPINFO estructuraImagen = new BITMAPINFO();
13 estructuraImagen.biSize = Marshal.SizeOf(estructuraImagen);
14 estructuraImagen.biWidth = margenes.Width;
15 estructuraImagen.biHeight = margenes.Height;
16 estructuraImagen.biPlanes = 1;
17 estructuraImagen.biBitCount = 32;
18 estructuraImagen.biCompression = 0;
19 //Crear imagen independiente
20 IntPtr imagen = CreateDIBSection(salida, estructuraImagen, 0, 0, IntPtr.Zero, 0);
21 SelectObject(contextoSalida, imagen);
22 // Establecer espacio y formato del texto
23 TextFormatFlags formatoTexto = TextFormatFlags.SingleLine | TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter;
24 Rectangle margenes = new Rectangle(0, 0, 800, 50);
25 IntPtr fuente = new Font(«Tahoma«, 20).ToHfont();
26 SelectObject(contextoSalida, fuente);
27 System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer (System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active);
28 //Capturar el texto mostrado, renderizarlo para aplicar el
29 //efecto glass, y el resultado almacebarlo en la imagen independiente
30 DTTOPTS opciones = new DTTOPTS();
31 opciones.dwSize = Marshal.SizeOf(typeof(DTTOPTS));
32 opciones.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
33 opciones.crText = ColorTranslator.ToWin32(Color.Black);
34 opciones.iGlowSize = 15; // This is about the size Microsoft Word 2007 uses
35 RECT margenesExtendidos = new RECT(0, 0, margenes.Right margenes.Left,
36 margenes.Bottom margenes.Top);
37 DrawThemeTextEx(renderer.Handle, contextoSalida, 0, 0, v.Titulo, 1, (int)formatoTexto, ref margenesExtendidos, ref opciones);
38 //Mostrar el resultado del renderizado
39 const int SRCCOPY = 0x00CC0020;
40 BitBlt(salida, margenes.Left, margenes.Top, margenes.Width, margenes.Height, contextoSalida, 0, 0, SRCCOPY);
41 //Liberar recursos
42 DeleteObject(fuente);
43 DeleteObject(imagen);
44 DeleteDC(contextoSalida);
45 g.ReleaseHdc(salida);
46 }

Ahora sí que ya tenemos todas las piezas de puzzle encajadas y podemos dar por refinados los más pequeños detalles (Imagen 6) de nuestra aplicación.


Imagen 6


image


Conclusiones Mediante una correcta gestión de las posibilidades de WDM podemos conseguir resultados profesionales para nuestras aplicaciones diseñadas para Windows Vista sin que ello repercuta en el rendimiento de nuestro código, obteniendo atractivos efectos para nuestras interfaces de usuario. Un placer para los ojos que los usuarios de nuestras soluciones sabrán agradecer.


[Crossposting desde http://www.tonirecio.com]

Desktop Window Manager, Los entresijos de la interfaz visual de Windows Vista (1ª Parte)

Uno de los temas más comentados del nuevo Windows Vista es sin duda las novedades incluidas en su interfaz visual. Trasparencias en las ventanas, Windows Flip o Windows Flip3D tienen un denominador común, el uso de la nueva API de Windows Vista, Desktop Window Manager, o como la conocen sus más allegados, la WoWAPI. ¿Pero qué posibilidades brinda a los programadores esta nueva librería?Uno de los aspectos más potenciados y destacables de Windows Vista, nuevo sistema operativo de escritorio de Microsoft, es sin lugar a dudas su interfaz gráfica, y en particular el nuevo estilo creado para presentar sus características, el Aero Glass, pero este estilo no es más que un cliente o fachada de la característica más importante de toda la interfaz de Vista, la composición de escritorio.


El poder de delegar


En las versiones previas de Windows, la visualización de las ventanas en pantalla se realiza mediante accesos directos a zonas de memoria de video, de forma estándar, usando las funciones mínimas de los dispositivos de visualización, para asegurar así un nivel total de compatibilidad. Ahora gracias a Windows Vista, cuando el sistema operativo necesita mostrar una ventana por pantalla, ya no interactúa directamente con el hardware, sino que delega esta misión a la capa de composición de escritorio, bautizada en esta ocasión como Desktop Window Manager y que en lo sucesivo denominaremos DWM.


La simple idea de delegar tareas que hasta el momento se venían aplicando directamente puede conducirnos a la conclusión de que estamos sobrecargando la arquitectura innecesariamente, y eso sería cierto si no tuviéramos en cuenta el detalle más importante, y es que DWM no sólo actúa de puente para trasladar la información a visualizar hasta el dispositivo de salida, si no que lo hará teniendo en cuenta las características de dicho dispositivo.


Aceleración gráfica


Hasta la llegada de Vista la elección de una tarjeta gráfica para un ordenador de escritorio destinado a ejecutar Windows venía condicionada casi exclusivamente a la siguiente sencilla pregunta: ¿Piensas ejecutar videojuegos? O dicho de un modo más técnico: ¿Vas a necesitar que tu máquina dé soporte a DirectX? Si la respuesta a las anteriores preguntas era negativa, la elección se simplificaba en grado sumo, ya que con tarjetas gráficas sencillas se obtienen resoluciones, profundidades de color y rendimientos suficientes para ejecutar aplicaciones de escritorio de forma fluida y correcta. Si por el contrario nos adentrábamos en territorio jugón la elección de un dispositivo pensado para soportar DirectX venía siendo mucho más compleja, y por qué no decirlo, bastante más cara. Esta situación no entrañaría ninguna particularidad destacable si no fuera por un hecho muy habitual para muchos usuarios de Windows, y es que en muchas ocasiones se encuentran con que su tarjeta de video en el contexto de un videojuego es capaz de mover gran cantidad de polígonos, a la vez que les aplica efectos visuales y técnicas de sombreado complejas, pero que cuando acaban de jugar y vuelven al escritorio, se pueden encontrar que algo tan simple como arrastrar una ventana sobre otra, puede ocasionar que no se redibuje de forma fluida el contenido de la ventana sobre volada, produciendo efectos visuales molestos y poco vistosos (Imagen 1).


Imagen 1


Imagen 1


Mediante DWM las aplicaciones de escritorio también tienen acceso a las características de aceleración gráfica de los dispositivos de visualización, que hasta la fecha venían usando en exclusiva los videojuegos y las aplicaciones implementadas mediante DirectX. Con Windows Vista y la composición de escritorio podemos disfrutar de un entorno de trabajo más elegante y productivo, con un mejor rendimiento, aprovechando todos los recursos que nos ofrece el hardware de nuestras máquinas.


Primera toma de contacto


DWM reside en disco dentro del ejecutable dwm.exe, pero como programadores accederemos a sus funcionalidades mediante su interfaz pública dwmapi.dll. Para conocer en detalle la interfaz expuesta por DWM no existe mejor recurso que la documentación en línea de Microsoft que podemos consultar en el siguiente enlace: http://msdn2.microsoft.com/en-us/library/aa969540.aspx


Lamentablemente dwmapi.dll no expone una interfaz COM ni soporta código administrado, con lo que deberemos redefinir la interfaz, de forma íntegra o parcial, mediante importaciones de la librería dentro de nuestras aplicaciones.


Detectar y establecer el entorno


Antes de usar la composición de escritorio es bueno tener en cuenta que versiones de Windows Vista como la Home Basic, no soportan composición de escritorio, ni todas las configuraciones de hardware son capaces de soportarlo, con lo que podemos encontrarnos con que las ventajas inherentes de esta tecnología visual no siempre se mostrarán disponibles. También podemos hallar escenarios donde a pesar de disponer y soportar todos los prerrequisitos la composición de escritorio se encuentre desactivada. Es por ello, que nuestro primer objetivo desde el punto de vista de un programador será detectar el estado del sistema de composición de escritorio, y activarlo o desactivarlo dependiendo de la situación y nuestros intereses.


Para ello usaremos las funciones DwmIsCompositionEnabled y DwmEnableComposition contenidas en dwmapi.dll. Mediante la primera consultaremos el estado del servicio de composición de escritorio, y con la segunda podremos modificar el estado del mismo. De este modo, con tan sólo una sentencia (Código 1) podemos conmutar el estado del servicio de composición de escritorio.


Importar declaraciones:


 


1 [DllImport(«dwmapi.dll«, PreserveSig = false)]
2 public static extern bool DwmIsCompositionEnabled();
3 [DllImport(«dwmapi.dll«, PreserveSig = false)]
4 public static extern void DwmEnableComposition(bool Activo);

 


Ejemplo de uso:


 


1 //Conmutar el estado de la composición de escritorio
2 DwmEnableComposition(!DwmIsCompositionEnabled());

 


El efecto glass


El usuario que por primera vez se enfrente al estilo Aero Glass de Windows Vista, enseguida se dará cuenta de que ahora muchos de los elementos del sistema operativo se muestran de forma semitransparente. El efecto glass es la primera característica que los formularios de nuestras aplicaciones podrán heredar sin tener que escribir ni una sola línea de código, aunque con una única salvedad, las transparencias no se aplicarán a la zona de cliente, es decir, allí donde podemos desplazar nuestros controles para dibujar el contenido de los formularios, sino que únicamente afectarán al contorno y título de la ventana (Imagen 2).


Imagen 2


Imagen 2


Gracias a DWM no tendremos que conformarnos con las limitaciones del comportamiento estándar respecto a los formularios de nuestras aplicaciones. Mediante la función DwmExtendFrameIntoClientArea, también incluida en dwmapi.dll,  podemos extender el comportamiento y la apariencia del marco de la ventana al conjunto de la misma (Imagen 3). Tan sólo deberemos indicar los márgenes que deseamos que queden excluidos de dicho comportamiento, aunque para conseguir los resultados deseados deberemos realizar un pequeño paso más, consistente en pintar de color negro el fondo de la ventana (Código 2). De este modo la máscara de trasparencia coincidirá con la máscara del color de fondo, activando así el efecto.


Imagen 3


Imagen 3


Definir las estructuras necesarias:


1 [StructLayout(LayoutKind.Sequential)]
2 public class MARGINS
3 {
4 public int margenIzquierdo, margenDerecho;
5 public int margenSuperior, margenInferior;
6 public MARGINS(int izquierda, int arriba,
7 int derecha, int abajo)
8 {
9 margenIzquierdo = izquierda; margenSuperior = arriba;
10 margenDerecho = derecha; margenInferior = abajo;
11 }
12 }

Importar declaraciones:


 


1 [DllImport(«dwmapi.dll«, PreserveSig = false)]
2 public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, MARGINS pMargins);

 


Ejemplo de uso:


1 MARGINS margenes = new MARGINS(1, 0, 0, 0);
2 if (DwmIsCompositionEnabled())
3 {
4 //MUY IMPORTANTE! Poner el color de fondo en negro
5 gf.BackColor = Color.Black;
6 DwmExtendFrameIntoClientArea(this.Handle, margenes);
7 }

Si lo que deseamos es aplicar el efecto a la ventana completa emplearemos márgenes negativos.


Vistas en miniatura


La otra novedad incluida en la interfaz Windows Vista, y disponible mediante composición de escritorio, son las miniaturas de ventanas (thumbnails), mediante las cuales podremos monitorizar la interfaz visual de cualquiera de las ventanas que tengamos abiertas. Podemos apreciar esta característica tanto en la barra de tareas (Imagen 4), como en las dos versiones de flip (la estándar y la tridimensional), el conmutador de aplicaciones de Windows.


Imagen 4


Imagen 4


Disponemos de esta nueva característica mediante la función DwmRegisterThumbnail. Tan solo deberemos indicar la ventana que deseamos monitorizar, así como que otra ventana mostrará dicho escaner visual. Usando la función DwmUpdateThumbnailProperties podremos modificar las propiedades de la vista en miniatura, controlando características como el  tamaño o la opacidad de la misma.


Como ejemplo (Código 3) podemos crear dos instancias de un formulario, modificar su color de fondo, uno en rojo y el otro en azul, para posteriormente crear un tercer formulario que monitorice la actividad visual de los dos anteriores. Una vez ejecutado el último formulario, podremos redimensionar el tamaño de cualquiera de las dos ventanas coloreadas, viendo como las vistas en miniatura reflejan al instante los cambios efectuados.


Definir las estructuras necesarias:


1 [StructLayout(LayoutKind.Sequential)]
2 public class DWM_THUMBNAIL_PROPERTIES
3 {
4 public uint interruptores;
5 public RECT destino;
6 public RECT fuente;
7 public byte opacidad;
8 [MarshalAs(UnmanagedType.Bool)]
9 public bool visible;
10 [MarshalAs(UnmanagedType.Bool)]
11 public bool fuenteSoloCliente;
12 public const uint DWM_TNP_RECTDESTINO = 0x00000001;
13 public const uint DWM_TNP_RECTFUENTE = 0x00000002;
14 public const uint DWM_TNP_OPACIDAD = 0x00000004;
15 public const uint DWM_TNP_VISIBLE = 0x00000008;
16 public const uint DWM_TNP_FUENTESOLOCLIENTE = 0x00000010;
17 }


Importar declaraciones:


 


1 [DllImport(«dwmapi.dll«, PreserveSig = false)]
2 public static extern IntPtr DwmRegisterThumbnail(
3 IntPtr destino, IntPtr fuente);
4 [DllImport(«dwmapi.dll«, PreserveSig = false)]
5 public static extern void DwmUpdateThumbnailProperties(
6 IntPtr miniatura,
7 DWM_THUMBNAIL_PROPERTIES propiedadesMiniatura);

 

Ejemplo de uso:

1 private void ActualizarMiniatura(IntPtr miniatura,
2 int margenIzquierdo, int margenSuperior, int ancho, int alto){
3 if (miniatura != IntPtr.Zero)
4 {
5 // Crear una estructura de propiedades de miniatura
6 DWM_THUMBNAIL_PROPERTIES propiedadesMiniatura;
7 propiedadesMiniatura = new DWM_THUMBNAIL_PROPERTIES();
8 // Establecer que propiedades se pueden actualizar:
9 // – Visibilidad, opacidad, posición y tamaño de la
10 // miniatura y sección de la ventana a monitorizar
11 propiedadesMiniatura.interruptores = DWM_THUMBNAIL_PROPERTIES.DWM_TNP_VISIBLE +
12 DWM_THUMBNAIL_PROPERTIES.DWM_TNP_OPACIDAD +
13 DWM_THUMBNAIL_PROPERTIES.DWM_TNP_RECTDESTINO +
14 DWM_THUMBNAIL_PROPERTIES.DWM_TNP_FUENTESOLOCLIENTE;
15 // Indicamos el tamaño y posición de la miniatura
16 propiedadesMiniatura.destino =
17 new DWMLibrary.RECT(margenIzquierdo, margenSuperior, ancho, alto);
18 // Solicitamos monitorizar el global de la ventana,
19 // tanto la zona de cliente, como la de sistema
20 propiedadesMiniatura.fuenteSoloCliente = false;
21 // Asignamos la visibilidad y opacidad
22 propiedadesMiniatura.opacidad = 255;
23 propiedadesMiniatura.visible = true;
24 // Asignamos las nuevas propiedades a la miniatura
25 DwmUpdateThumbnailProperties(miniatura,
26 propiedadesMiniatura);
27 }
28 }
En el formulario que visualizará las miniaturas:

1 // Crear los formularios a monitorizar, uno rojo y el otro azul
2 IntPtr miniaturaRoja; IntPtr miniaturaAzul;
3 Form formularioRojo = new Form();
4 Form formularioAzul = new Form();
5 formularioRojo.BackColor = Color.Red;
6 formularioAzul.BackColor = Color.Blue;
7 formularioRojo.Show();
8 formularioAzul.Show();
9 if (DwmIsCompositionEnabled())
10 {
11 // Capturar miniaturas
12 miniaturaRoja = DWMLibrary.DwmRegisterThumbnail(this.Handle,
13 (IntPtr)(formularioRojo.Handle));
14 miniaturaAzul = DWMLibrary.DwmRegisterThumbnail(this.Handle,
15 (IntPtr)(formularioAzul.Handle));
16 // Posicionar las miniaturas
17 ActualizarMiniatura(miniaturaRoja, 10, 10, 110, 110);
18 ActualizarMiniatura(miniaturaAzul, 10, 120, 110, 230);
19 }

Resumen


Hasta el momento hemos introducido los conceptos básicos de la composición de escritorio y las particularidades de esta tecnología aplicadas a Windows Vista. También hemos podido ver cómo empezar a trabajar con esta tecnología desde .NET.


Más adelante trataremos de implementar un pequeño ejemplo algo más completo.


[Crossposting desde http://www.tonirecio.com]