Antonio,
Antonio Linares wrote:Carlos,
gracias por tu ayuda. La verdad es que no veo nada extraño salvo que hay dos erasebkgnd seguidos
He estado probando bastante el control y observando los mensajes que se reciben en cada ocacion. He controlado los hDC para asegurarme que no son nulos.
El control que estoy usando para probar no es de ninguna clase específica de Windows, por lo que al crear la clase del control se llama a Create sin parámetros para crear una nueva clase de ventana, TOutLook2. El estilo de las ventanas de esta clase es WS_CHILD, WS_VISIBLE, WS_CLIPSIBLINGS
Cuando mi control se pinta normalmente, y es mi aplicación la ventana activa, la secuencia de mensajes que se recibe es la siguiente, coincidiendo con la primera parte del post.:
1) WM_PAINT -> que se procesa a:
::BeginPaint()
::Paint()
::EndPaint()
2) la invocación de BeginPaint para obtener el DC produce que Windows envíe dos mensajes:
WM_NCPAINT // Pintado del área no cliente de la ventana o control
WM_ERASEBKGND // Borrar el fondo
que resulta en el segundo caso en la llamada a EraseBackgnd que si lo ponemos a 1 hacemos que se valide el fondo como pintado, o bien si retornamos 0 se llama a la funcion normal de pintado de windows. Como en nuestro método PAINT pintamos todo incluyendo el fondo, es preferible retornar 1. Ademas mejora el tema del parpadeo.
3) Como mi control tiene otros controles hijos que son subclases de la clase Static de windows, se reciben luego una sucesion de WM_CTLCOLORSTATIC para que la ventaan padre defina los colores del hijo. Esto ya esta definido en la clase windows.
4) por ultimo llamamos a EndPaint para liberar el DC.
Al recibir el WM_ERASEBKGND, se adjunta un DC con el cual pintar, que además tiene clipeada la region a actualizar.
pero.... que sale mal?
Un detalle importante a recordar es que cuando una ventana está parcialmente invisible (como en nuestro caso) al recibir el mensaje de pintado y su correspondiente DC, este viene 'clipeado', es decir, se ha bloqueado parte de la pantalla para que no podamos pintar en algunos sitios, por ejemplo, el area ocupada por las ventanas superpuestas. Es por eso que en los fallos de pintado vemos esas formas extrañas de rayas, que son las áreas que no se han pintado correctamente, el resto estaba 'clipeado' cuando se recibió el mensaje WM_PAINT para que no lo dibujásemos, porque o bien esa area corresponde a otra ventana o bien porque no es necesario repintarlo. Dicho esto...
En el segundo bloque de mensajes se vé algo más complejo de interpretar pero para quien interactua con las ventanas para producir la secuencia de mensajes es un poco más sencillo de entender. Siempre estoy tratando de producir el error, entíendase entonces que muevo la ventana superpuesta de forma lenta para que la ventana inferior tenga que trabajar mucho pintando trocito a trocito.
entonces empieza con
1: WM_PAINT
que luego pasa como es de esperarse de acuerdo con la secuencia 'correcta'
2: WM_NCPAINT
3: WM_ERASEBKGND
cuando se reciben estos mensajes no se debe olvidar que la region a actualizar esta clipeada, es decir que cuando pintamos solo se actualiza en una parte de la pantalla y no toda el area de nuestro control.
peeero, luego me repite
4: WM_NCPAINT
5: WM_ERASEBKGND
6: WM_CTLCOLORSTATIC
... esto no es lo esperado! Como se explica? Sencillo: mientras estaba procesándose WM_ERASEBKGND en el punto (3), algun usuario cabroncete movio algo en la pantalla que requiere que se actualice otro trocito de la pantalla, digamos otra region, entonces nos manda nuevamente a borrar el fondo de ese trozo nuevo, clipeado, tarea que debiera cumplirse en 4 y 5. Luego aparentemente sigue el pintado normal ya que llega hasta 6, es decir ya está pintando uno de los controles hijos, pero cuando aun no ha llegado al final... el usuario vuelve a mover las ventanas y genera una nueva necesidad de pintado. Y comienza un nuevo ciclo de pintado, ya que al recibir:
7: WM_PAINT
todavía no se habían terminado de pintar los controles hijos.
Este ciclo de pintado SI se cumple de manera completa, pero, claro, la region clipeada es la que corresponde al nuevo movimiento, y la region antigua quedo a medio camino, sin pintarse de manera completa.
sefiní.
Perdón por la larga charla, pero es que es lo que he podido lograr discernir despues de horas de debuguear.
Ahora mi duda es... como evitarlo? Ya le quite el doblebuffer, he probado todas las combinaciones de EraseBkgnd... no sé. Cualquier sugerencia es bienvenida.
Hasta mañana,
Carlos.