Создание прозрачного окна в C ++ Win32 - PullRequest
44 голосов
/ 19 октября 2010

Я создаю то, что должно быть очень простым приложением 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 ]

38 голосов
/ 19 октября 2010

Я смог сделать именно то, что хотел, используя код из Части 1 и 2 этой серии: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

В этих сообщениях блога говорится об отображении заставки в Win32 C ++, ноэто было почти идентично тому, что мне нужно было сделать.Я считаю, что часть, которую я пропустил, заключалась в том, что вместо того, чтобы просто рисовать PNG в окне с помощью GDI +, мне нужно было использовать функцию 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);
}
18 голосов
/ 19 октября 2010

Используйте 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:

Классные, полупрозрачные и фигурные диалоговые окна со стандартными элементами управления для Windows 2000 и выше

...