Soporte de Delphi para Aero Glass y la propiedad DoubleBuffered: ¿qué está pasando y cómo los usamos?
composition dwm (4)
Estoy confundido por el soporte de Delphi 2009/2010 para las características de Aero Theme Glass en Windows, y por lo que significa exactamente DoubleBuffered, y lo que tiene que ver con Aero Glass. Descubrí que DoubleBuffered no es solo una propiedad de VCL, sino que también se encuentra en .NET WinForms . Inicialmente me pregunté si establecería algún tipo de bit de estilo de ventana utilizado por la biblioteca de controles comunes, o qué. ¿Por qué se usa y cuándo se debe usar?
[Actualización: Debo decir que sé lo que es "doble buffer", como técnica general para la reducción del parpadeo, lo que me preguntaba es, ¿por qué tiene CUALQUIER COSA que ver con los controles de renderizado en un panel Aero Glass en Windows Vista / Windows 7, y en particular, ¿por qué un BOTÓN de todas las cosas necesita tener doble búfer establecido verdadero, para trabajar sobre vidrio ?. La entrada del blog vinculada a continuación parece más informativa.]
En particular, estoy confundido por la propiedad DoubleBuffered, y quiero saber, por qué existe, y cuál es su relación entre el soporte de vidrio y la propiedad de doble amortiguación en una forma y un control. Cuando lee artículos de C ++ como este , ve que no se menciona el doble almacenamiento en búfer.
[Actualización2: la siguiente contiene algunos errores de hechos, y ha sido enmendada:]
Encontré algunos desarrolladores de C ++ hablando de cómo pueden llamar a SetLayeredWindowAttributes para evitar el problema de "negro se vuelve vidrio" que causa la composición de DWM / Aero cuando lo enciendes en tu aplicación Win32 clásica [sin embargo, el enlace del blog a continuación me dice que esto ya no funciona en Windows 7, y en realidad solo funcionó brevemente en Vista, hasta que Microsoft lo bloqueó]. [Begin WRONG Idea] ¿No deberíamos usar algún otro color, como el magenta brillante y convertirlo en el color de transparencia del vidrio? [Fin de la Idea INCORRECTA]
¿Cuáles son las reglas para cuando DoubleBuffered debe establecerse y no establecerse, y por qué se agregó DoubleBuffered a la VCL en primer lugar? ¿Cuándo causará problemas cuando se configure? (Parece que el escritorio remoto es un caso, pero ¿es ése el único caso?) Y cuando no está configurado, obtenemos una representación del texto del botón, probablemente porque parece que Delphi no cambia el valor predeterminado "renderizar negro como el cristal" "en el Aero DWM.
Me parece que la renderización de Aero Glass se está haciendo fundamentalmente de una manera extraña o difícil de entender [por el propio Windows, no por Delphi, que simplemente envuelve esta funcionalidad], y que una gran cantidad de código fuente de VCL interno en 2009/2010 en las clases en StdCtrls tienen que hacer un montón de lógica compleja para renderizar correctamente en Aero Glass, y aún así tiene muchos problemas y me parece que está mal, y que esto podría estar detrás de esta pregunta relacionada y el problema de la GC. [Actualización3: Muchas fallas técnicas de renderización en el cristal, en el VCL se están ejecutando mal dentro de los controles comunes, lo que parece que a Microsoft no le importa solucionarlo. En resumen, las correcciones de código de Delphi VCL no pueden reparar el hecho de que la antigua biblioteca de controles comunes de Windows y la característica de composición moderna [pero peculiar] de Aero Glass no se gustan mucho entre sí y no funcionan bien juntas. Gracias Microsoft por construir una tecnología de tan alta calidad y desatarla en el mundo.]
Y si aún no era lo suficientemente divertido; ¿Por qué tenemos ParentDoubleBuffered?
[Actualización 30 de julio: Esta pregunta me resulta interesante porque creo que muestra que trabajar en la API de Windows para resolver este problema, cuando se tiene un gran marco VCL existente, es un problema difícil ".
Acerca de DoubleBuffer
.NET puede tener uno, y puede tener el mismo nombre y el mismo propósito que el de Delphi, pero Delphi está implementando DoubleBuffer desde cero y supongo que .NET hace lo mismo. No se utilizan bits de estilo de ventana implementando esto.
DoubleBuffer y Glass Aero
Bastante simple: no configure DoubleBuffer para los controles que se sientan en Glass. Para que funcione DoubleBuffering uno tiene que ser capaz de inicializar el "Buffer", pero ¿con qué inicializarlo para Glass? DoubleBuffering no es necesario para los controles estándar de Windows (incluido TButton). Para los nuevos controles que necesitan tanto superficies transparentes como un comportamiento de doble amortiguador, uno puede usar las api de Windows en capas.
Obtener controles para trabajar en Glass
Paso 1:
TForm1 = class(TForm)
...
protected
procedure CreateWindowHandle(const Params: TCreateParams); override;
...
end;
procedure TForm15.CreateWindowHandle(const Params: TCreateParams);
begin
inherited;
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
SetLayeredWindowAttributes(Handle, RGB(60, 60, 60), 0, LWA_COLORKEY);
end;
Paso 2 , este debe ser el manejador OnPaint de tu formulario:
procedure TForm15.FormPaint(Sender: TObject);
var rClientRect:TRect;
begin
if GlassFrame.Enabled then
begin
rClientRect := ClientRect;
Canvas.Brush.Color := RGB(60, 60, 60);
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(rClientRect);
if not GlassFrame.SheetOfGlass then
begin
rClientRect.Top := rClientRect.Top + GlassFrame.Top;
rClientRect.Left := rClientRect.Left + GlassFrame.Left;
rClientRect.Right := rClientRect.Right - GlassFrame.Right;
rClientRect.Bottom := rClientRect.Bottom - GlassFrame.Bottom;
Canvas.Brush.Color := clBtnFace;
Canvas.FillRect(rClientRect);
end;
end;
end;
Paso 3 : establecer GlassFrame.Enabled = True; Establezca todas las demás propiedades de Glass, agregue controles al formulario, donde lo desee. Puede estar en Glass o en cualquier otro lugar. Asegúrese de que los controles no tengan "DoubleBuffered = True". Eso es, disfruta. He probado con TButton, TCkBox y TEdit.
... EDITAR ...
Desafortunadamente, al usar este método, "Vidrio" se trata como una superficie 100% transparente, y no lo es, parece vidrio, pero no se comporta como el vidrio. El problema con la transparencia del 100% es que, si hace clic en esa área transparente, su clic se dirige a la ventana detrás de su ventana. Horrible.
En el momento de escribir estas líneas, estoy bastante seguro de que no hay API para cambiar el color predeterminado de la tecla NEGRA para el cristal original (google encuentra innumerables publicaciones en blogs y foros sobre cómo se necesita usar dibujos personalizados para controles que se sientan en el vidrio y no hay función para cambiar eso en la lista de funciones de DWM en MSDN ). Sin cambiar el color NEGRO predeterminado, la mayoría de los controles no se pueden procesar correctamente porque escriben texto usando clWindowText y eso es NEGRO. Un truco sugerido que se encuentra en varios foros es cambiar el color de la transparencia mediante la API SetLayeredWindowAttributes. ¡Y funciona! Una vez hecho esto, el texto en negro en los controles muestra el lanzamiento, pero desafortunadamente el vidrio ya no es de vidrio, el vidrio se parece al vidrio pero se comporta como 100% de transparencia. Esto prácticamente invalida esta solución y muestra un doble estándar por parte de Microsoft: el BLACK original no se comporta como el 100% de transparencia, sin embargo, si lo cambiamos a algo mejor, se comporta como una transparencia del 100%.
En mi opinión, el pensamiento común de usar controles personalizados en Glass es incorrecto. Es lo único que podría funcionar, pero está mal, porque se supone que debemos usar controles que son consistentes en toda la plataforma: la sugerencia de controles personalizados abre la puerta a aplicaciones incoherentes similares a winamp, donde cada usuario recrea la rueda para adaptarse a sus ideas artísticas. Incluso si un desarrollador logra recrear fielmente cualquier control de Windows dado y hacerlo funcionar en vidrio, la "solución" es solo temporal y debe volverse a crear para la próxima versión de Windows. Sin mencionar uno probablemente debería tener múltiples variantes para las versiones existentes de Windows.
La otra solución es usar ventanas estratificadas con UpdateLayeredWindow. Pero ese es un DOLOR por muchas razones.
Esto es un callejón sin salida para mí. Pero le daré a la pregunta una bandera "favorita", si aparece algo mejor, me gustaría saberlo.
DoubleBuffered es una técnica de gráficos estándar utilizada para reducir el parpadeo. Básicamente, dibuja una nueva versión de su formulario en un segundo lienzo y luego lo cambia por el actual. Ese intercambio es rápido, incluso si el proceso de dibujo es lento. No existe una relación directa entre eso y Aero: puede usar cualquiera o ambos de forma independiente. El búfer doble ha estado presente durante mucho tiempo en Delphi, pero ahora tenemos muchos más ciclos de procesador por actualización de pantalla, es menos necesario. Que puede ser por qué no has oído hablar de eso.
El búfer doble es algo que solo debe usar de forma reactiva: si ve un parpadeo cuando su aplicación está repintando la pantalla, enciéndala y vea lo que sucede. En ese caso, su primer recurso sería DisableUpdates / EnableUpdates (vea la ayuda de Delphi para aquellos) y la API de Windows LockWindowUpdate (ditto).
ParentDoubleBuffered, como la mayoría de las propiedades Parent ..., le dice si este formulario usará la propiedad DoubleBuffered de su principal. Esto le permite establecer la propiedad una vez en el formulario principal de su aplicación y hacer que afecte a cada formulario que cree. O no, si configura esa propiedad como falsa.
Esta es una situación típica cuando tienes un código compatible con versiones anteriores: hay cosas que rara vez se usan hoy pero que aún funcionan (y que a veces son necesarias), aunque la mayoría de la gente nunca debe preocuparse por ellas. Para la mayoría de la gente, están allí y puedes ignorarlos, pero para algunos de nosotros son desesperadamente necesarios (tenemos un par de formas muy, muy complejas que redibujamos en patrones ocasionalmente complejos para detener el parpadeo)
OK, intentaré aclarar un poco las cosas.
En primer lugar, el doble buffering es una técnica muy estándar cuando se trata de renderizado en pantalla. Lo uso todo el tiempo, por ejemplo en mi componente de editor de texto. Imagina que el texto necesita ser redibujado. En pocas palabras, primero FillRect(ClientRect)
todo el cliente rect, más o menos por FillRect(ClientRect)
, luego dibujo las líneas, desde la primera hasta la última visible, desde el primer personaje hasta el último visible. Pero esto se verá muy desagradable para el usuario final, que tendrá unos pocos milisegundos más o menos con una pantalla borrada, entre dos estados con casi el mismo contenido de texto.
La solución es hacer todo el dibujo en un mapa de bits fuera de la pantalla , y simplemente dibujar el mapa de bits fuera de la pantalla en la pantalla cuando se complete. Entonces, si el marco anterior y el nuevo son idénticos, no pasará nada con la pantalla, en gran contraste con el caso sin búfer doble, donde la pantalla mostrará un rectángulo blanco vacío durante unos pocos milisegundos, es decir, la pantalla parpadeará Siempre uso doble buffer para todos mis controles visuales, y esto realmente mejora la calidad y la sensación de ellos, enormemente. En un sistema moderno con gigabytes de memoria (RAM), el aumento en el uso de memoria no es un problema. Y en casi todos los casos, el intercambio de los buffers es más que suficientemente rápido (aunque hay bastantes píxeles para copiar).
A menudo se puede observar la falta de doble buffer cuando se cambia el tamaño de las ventanas. A veces solo parpadean como h ** l. También podría ser interesante observar que las tecnologías como OpenGL son intrínsecamente de doble amortiguación (en la mayoría de los casos, al menos).
Así que el doble buffering no tiene nada que ver con las hojas de vidrio. De hecho, la mayoría de los descendientes de Delphi TWinControl
tienen las propiedades DoubleBuffered
y ParentDoubleBuffered
( reference ).
La propiedad ParentDoubleBuffered
está relacionada con DoubleBuffered
de la misma manera que ParentColor
está relacionada con Color
, ParentShowHint
con ShowHint
, ParentFont
con Font
, etc. Simplemente decide si el control debe heredar el valor del parámetro de su ventana principal o no.
Ahora a la pregunta sobre el vidrio. Bueno, es de conocimiento común que, en general, es incómodo agregar controles a una hoja de vidrio (o un marco de vidrio), al menos en una aplicación de VCL. Alguien debería escribir un largo artículo en un blog sobre cómo hacerlo de manera adecuada ...
Su pregunta ha provocado una publicación de blog de CR en Delphi Haven ...
En cuanto al desbordamiento de pila, acabo de notar una pregunta bastante detallada (realmente, un conjunto de preguntas) sobre Aero Glass.