Soporte de Delphi para Aero Glass y la propiedad DoubleBuffered - ¿qué está pasando y cómo los usamos?


Estoy confundido por el soporte de Delphi 2009/2010 para las características de Aero Theme Glass en Windows, y por qué, exactamente DoubleBuffered significa, y lo que tiene que ver con Aero glass. He encontrado que DoubleBuffered no es solo una propiedad en la VCL, 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 debe usarse?

[Actualizar: Debo decir que sé lo que "doble buffering" es, como una técnica general para la reducción de parpadeo, lo que me preguntaba es, ¿por qué tiene algo que ver con los controles de renderizado en un panel de vidrio Aero en Windows Vista/Windows 7, y en particular por qué un BOTÓN de todas las cosas necesita tener doble buffering establecido true, para trabajar sobre el vidrio?. La entrada del blog enlazada 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 búfer en una forma y un control se establecen. Cuando lees artículos de C++ como este ves que no hay mención de doble buffering.

[Actualización 2: Lo siguiente contenía algunos errores fácticos, y ha sido modificado:]

Encontré algunos desarrolladores de C++ hablando sobre cómo pueden llamar a SetLayeredWindowAttributes para evitar el problema de "black becomes glass" que causa la composición DWM/Aero cuando lo enciendes en tu clásica aplicación Win32 [sin embargo, el enlace del blog a continuación me dice que esto ya no funciona en Windows 7, y en realidad solo trabajó brevemente en Vista, hasta que Microsoft lo bloqueó]. [Begin WRONG Idea] ¿No deberíamos usar algún otro color, como magenta brillante y hacer que se convierta en el color de transparencia del vidrio? [Fin Idea EQUIVOCADA]

¿Cuáles son las reglas para cuando DoubleBuffered se debe establecer y no establecer, y por qué se agregó DoubleBuffered a la VCL en primer lugar? ¿Cuándo causará problemas cuando esté configurado? (Parece escritorio remoto es un caso, pero es que el único caso?) y cuando no está configurado, obtenemos un renderizado defectuoso del texto del botón, muy probablemente porque parece que Delphi no cambia el valor predeterminado "renderizar negro como vidrio" en el DWM Aero.

Me parece que la representación de Aero Glass se está haciendo fundamentalmente de una manera extraña o difícil de entender [por Windows en sí, no por Delphi, que simplemente envuelve esta funcionalidad], y que una gran cantidad de código fuente VCL interno en 2009/2010 en las clases en StdCtrls tiene que hacer una gran cantidad de lógica compleja para representar cosas correctamente en Aero Glass, y sin embargo todavía tiene un montón de problemas y me parece que se ha hecho mal, y que esto podría estar detrás de esta pregunta relacionada, y el problema de control de calidad. [Update3: Una gran cantidad de fallos de renderizado en el vidrio, en la VCL se están renderizando mal dentro de los controles comunes, que al parecer, Microsoft no se preocupa por la fijación. En resumen, las correcciones de código VCL de Delphi no pueden corregir el hecho de que las ventanas antiguas comunes la biblioteca de controles y la moderna [pero peculiar] función de composición de Aero Glass no se gustan mucho y no funcionan bien juntos. Gracias Microsoft por construir una tecnología de tan alta calidad y liberarla en el mundo.]

Y si no fue lo suficientemente divertido todavía; ¿Por qué tenemos ParentDoubleBuffered?

[Actualización de julio 30: Esta pregunta es interesante para mí porque creo que muestra que el trabajo en la API de Windows para resolver este problema, cuando se tiene una gran marco VCL existente, es un problema difícil difícil.]

Author: Community, 2010-07-29

4 answers

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 asumo que. NET hace lo mismo. No se usan bits de estilo de ventana implementando esto.

DoubleBuffer y Glass Aero

Bastante simple: No configure DoubleBuffer para los controles que se sientan en el vidrio. Para que el DoubleBuffering funcione, 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 (incluyendo TButton). Para los nuevos controles que necesitan superficies transparentes y un comportamiento similar al de doublebuffer, se pueden usar las api de Windows en capas.

Conseguir que los controles funcionen en el vidrio

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 controlador OnPaint de su 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; Establecer todas las demás propiedades de vidrio, añadir controles a la forma, donde quieras. Puede estar en vidrio o en cualquier otro lugar. Asegúrese de que los controles no tienen "DoubleBuffered = True". Eso es, disfrútalo. He probado con TButton, TCkBox y TEdit.

... EDITAR ...

Desafortunadamente, usando este método, el "vidrio" se trata como una superficie 100% transparente, y no lo es, parece vidrio, pero no se comporta como vidrio. El problema con la transparencia del 100% es que, si hace clic en esa área transparente, el clic va a la ventana detrás de la ventana. Horrible.

En el momento de escribir esto, estoy bastante seguro de que no hay ninguna API para cambiar el color predeterminado de la tecla NEGRA para el vidrio original (google encuentra innumerables publicaciones en blogs y foros sobre cómo debe usar el dibujo personalizado para los controles que se sientan en el vidrio y no hay ninguna función para cambiar eso en la lista de funciones DWM en MSDN). Sin cambiar el color NEGRO predeterminado, la mayoría de los controles no pueden renderizarse correctamente porque escriben texto usando clWindowText y eso es NEGRO. Uno sugirió truco encontrado en varios foros es cambiar el color de transparencia utilizando la API SetLayeredWindowAttributes. Y funciona! Una vez hecho eso, el texto negro en los controles muestra tiro, pero desafortunadamente el vidrio ya no es vidrio, el vidrio parece 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 NEGRO original no se comporta como 100% de transparencia sin embargo, si lo cambiamos a algo mejor, se comporta como 100% transparencia.

En mi opinión, el pensamiento común de usar controles personalizados en vidrio es incorrecto. Es lo único que podría funcionar, pero está mal, porque se supone que debemos usar controles que sean consistentes en toda la plataforma: sugerir controles personalizados abre la puerta a aplicaciones inconsistentes, como 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 hacer que funcione en vidrio, el "fix" es solo temporal y necesita ser recreado para la próxima versión de Windows. Sin mencionar que uno probablemente debería tener múltiples variantes para las versiones existentes de Windows.

La otra solución es usar ventanas en capas con UpdateLayeredWindow. Pero eso es un DOLOR por muchas razones.

Este 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.

 15
Author: Cosmin Prund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-30 06:43:02

Su pregunta ha provocado una entrada de blog de CR en Delphi Haven...

Mirando el desbordamiento de la pila, acabo de noté una pregunta bastante detallada (realmente, conjunto de preguntas) acerca de Aero vidrio.

 7
Author: Roddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-29 10:49:00

DoubleBuffered es una técnica gráfica estándar utilizada para reducir el parpadeo. Básicamente, dibujas una nueva versión de tu formulario en un segundo lienzo y luego lo intercambias por el actual. Ese intercambio es rápido, incluso si el proceso de dibujo es lento. No hay una relación directa entre eso y Aero - puedes usar cualquiera o ambos de forma independiente. El buffering doble ha existido mucho tiempo en Delphi, pero ahora tenemos muchos más ciclos de procesador por actualización de pantalla, es menos necesario. Que puede ser la razón por la que no has oído hablar de él.

El doble búfer es algo que solo debes usar de forma reactiva: si ves parpadeo cuando tu aplicación está repintando la pantalla, enciéndela y verás qué sucede. En ese caso, aunque su primer recurso sería DisableUpdates / EnableUpdates (consulte la ayuda de Delphi para ellos) y la API de Windows LockWindowUpdate (ídem).

ParentDoubleBuffered, como la mayoría de los padres... propiedades, le indica si este formulario utilizará la propiedad DoubleBuffered de su padre. Esto le permite establecer la propiedad una vez en el formulario principal de la aplicación y hacer que afecte a cada formulario que cree. O no, si establece esa propiedad en false.

Esta es una situación típica cuando tiene código compatible con versiones anteriores: hay cosas que rara vez se usan hoy en día pero que aún funcionan (y a veces siguen siendo necesarias), a pesar de que la mayoría de la gente nunca necesita preocuparse por ellas. Para la mayoría de la gente solo están ahí 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)

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-29 06:08:32

OK, voy a tratar de enderezar las cosas un poco.

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. Imagine que el texto necesita ser redibujado. En pocas palabras, primero borro todo el rect del cliente, más o menos por FillRect(ClientRect), luego dibujo las líneas, desde el primero hasta el último visible, cada una desde el primer carácter hasta el último visible. Pero esto se verá muy feo hasta el final usuario, que tiene unos pocos milisegundos más o menos con una pantalla despejada, entre dos estados de casi el mismo contenido textual.

La solución, es hacer todo el dibujo a 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 no doble búfer, donde la pantalla mostrará un rectángulo blanco vacío durante unos pocos milisegundos, es decir, la pantalla parpadeará. Siempre uso doble buffering 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 del uso de memoria no es de ninguna manera un problema. Y en casi todos los casos, el intercambio de los búferes es más que lo suficientemente rápido (aunque hay bastantes píxeles para copiar).

A menudo se puede observar la falta de doble búfer al cambiar el tamaño de las ventanas. A veces simplemente parpadean como h**l. También podría ser interesante notar que tecnologías como OpenGL son inherentemente de doble búfer (en la mayoría de los casos, al menos).

Así que el doble buffering no tiene nada en particlar que ver con láminas de vidrio. De hecho, la mayoría de los descendientes de Delphi TWinControl tiene las propiedades DoubleBuffered y ParentDoubleBuffered (referencia).

La propiedad ParentDoubleBuffered está ralada a DoubleBuffered de la misma manera que ParentColor está relacionada con Color, ParentShowHint to ShowHint, ParentFont a Font, etc. Se simplemente decide si el control debe heredar o no el valor del parámetro de su ventana padre.

Ahora a la pregunta sobre el vidrio. Bueno, es un conocimiento muy 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 VCL. Alguien debería escribir un largo artículo de blog discutiendo cómo hacerlo correctamente...

 1
Author: Andreas Rejbrand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-29 12:22:04