在C ++ Win32中创建透明窗口

时间:2010-10-19 15:36:50

标签: c++ winapi gdi+ transparency alphablending

我正在创建一个非常简单的Win32 C ++应用程序,它的唯一目的就是只显示一个半透明的PNG。窗口不应该有任何镶边,并且所有不透明度都应该在PNG中控制。

我的问题是,当窗口下的内容发生变化时,窗口不会重新绘制,因此PNG的透明区域会在应用程序最初启动时与窗口下方的内容“卡住”。

这是我设置新窗口的行:

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

对于RegisterClassEx的调用,我有这个背景设置:

wcex.hbrBackground = (HBRUSH)0;

这是WM_PAINT消息的处理程序:

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

需要注意的一点是,应用程序始终停靠在屏幕左侧,不会移动。但是,当用户打开,关闭或移动窗口时,应用程序下面的内容可能会发生变化。

当应用程序首次启动时,它看起来很完美。 PNG的透明(和类似透明)部分完美展现。但是,当应用程序下面的背景发生变化时,后台不会更新,它会在应用程序首次启动时保持不变。实际上,WM_PAINT(或WM_ERASEBKGND在后台更改时不会被调用)。

我已经玩了很长一段时间了,已经接近100%正确,但不是那么。例如,我尝试将背景设置为(HBRUSH)NULL_BRUSH,我尝试处理WM_ERASEBKGND。

当窗口下的内容发生变化时,可以做些什么来重新绘制窗口?

2 个答案:

答案 0 :(得分:38)

通过使用本系列第1部分和第2部分中的代码,我能够完全按照自己的意愿行事:http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

这些博客文章讨论的是在Win32 C ++中显示启动画面,但它与我需要做的几乎相同。我相信我缺少的部分是使用GDI +而不是仅仅使用GDI +将PNG绘制到窗口,我需要使用UpdateLayeredWindow函数和正确的BLENDFUNCTION参数。我将粘贴下面的SetSplashImage方法,可以在上面链接的第2部分中找到:

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);
}

答案 1 :(得分:18)

使用SetLayeredWindowAttributes,这允许您设置将变为透明的蒙版颜色,从而允许背景通过。

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

您还需要使用分层标志配置窗口,例如

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

之后它很简单:

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

当您的PNG包含要与背景混合的半透明像素时,这会变得更加复杂。您可以尝试查看此CodeProject文章中的方法:

Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above