Crear una ventana transparente en C ++ Win32

Estoy creando lo que debería ser una aplicación Win32 C ++ muy simple cuyo único propósito es mostrar SOLO un PNG semitransparente. La ventana no debe tener ningún cromo, y toda la opacidad debe controlarse en el PNG.

Mi problema es que la ventana no se vuelve a pintar cuando el contenido debajo de la ventana cambia, por lo que las áreas transparentes de PNG se “atascan” con lo que estaba debajo de la ventana cuando la aplicación se inició inicialmente.

Aquí está la línea donde configuro la nueva ventana:

hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0); 

Para la llamada a RegisterClassEx, tengo este conjunto para el fondo:

 wcex.hbrBackground = (HBRUSH)0; 

Aquí está mi controlador para el mensaje WM_PAINT:

  case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); Gdiplus::Graphics graphics(hdc); graphics.DrawImage(*m_pBitmap, 0, 0); EndPaint(hWnd, &ps); break; } 

Una cosa a tener en cuenta es que la aplicación siempre está acoplada a la izquierda de la pantalla y no se mueve. Pero, lo que está debajo de la aplicación puede cambiar a medida que el usuario abre, cierra o mueve ventanas debajo de ella.

Cuando la aplicación comienza por primera vez, se ve perfecto. Las partes transparentes (y simi-transparentes) del PNG se muestran perfectamente. PERO, cuando el fondo debajo de la aplicación cambia, el fondo NO se actualiza, simplemente permanece igual desde que se inició la aplicación. De hecho, WM_PAINT (o WM_ERASEBKGND no se llama cuando cambia el fondo).

He estado jugando con esto durante bastante tiempo y he estado cerca de estar 100% en lo cierto, pero no del todo. Por ejemplo, he intentado configurar el fondo para (HBRUSH) NULL_BRUSH y he intentado manejar WM_ERASEBKGND.

¿Qué se puede hacer para que la ventana se vuelva a pintar cuando cambie el contenido?

Pude hacer exactamente lo que quería utilizando el código de la Parte 1 y Parte 2 de esta serie: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

Esas publicaciones de blog hablan de mostrar una pantalla de bienvenida en Win32 C ++, pero era casi idéntica a lo que tenía que hacer. Creo que la parte que me faltaba era que, en lugar de simplemente pintar PNG en la ventana usando GDI +, necesitaba usar la función UpdateLayeredWindow con el parámetro BLENDFUNCTION adecuado. Pegaré el método SetSplashImage a continuación, que se puede encontrar en la Parte 2 en el enlace de arriba:

 void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash) { // get the size of the bitmap BITMAP bm; GetObject(hbmpSplash, sizeof(bm), &bm); SIZE sizeSplash = { bm.bmWidth, bm.bmHeight }; // get the primary monitor's info POINT ptZero = { 0 }; HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); MONITORINFO monitorinfo = { 0 }; monitorinfo.cbSize = sizeof(monitorinfo); GetMonitorInfo(hmonPrimary, &monitorinfo); // center the splash screen in the middle of the primary work area const RECT & rcWork = monitorinfo.rcWork; POINT ptOrigin; ptOrigin.x = 0; ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2; // create a memory DC holding the splash bitmap HDC hdcScreen = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash); // use the source image's alpha channel for blending BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 255; blend.AlphaFormat = AC_SRC_ALPHA; // paint the window (in the right location) with the alpha-blended bitmap UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash, hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA); // delete temporary objects SelectObject(hdcMem, hbmpOld); DeleteDC(hdcMem); ReleaseDC(NULL, hdcScreen); } 

Use SetLayeredWindowAttributes, esto le permite establecer un color de máscara que se volverá transparente, permitiendo así pasar el fondo.

http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx

También necesitará configurar su ventana con el indicador de capas, por ejemplo

 SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); 

Después de eso es bastante simple:

 // Make red pixels transparent: SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY); 

Cuando su PNG contiene píxeles semitransparentes que desea mezclar con el fondo, esto se vuelve más complicado. Puede intentar ver el enfoque en este artículo de CodeProject:

Diálogos fríos, semitransparentes y con forma con controles estándar para Windows 2000 y superiores

    Intereting Posts