【问题标题】:Can't draw in title bar on WM_NCPAINT?无法在 WM_NCPAINT 的标题栏中绘图?
【发布时间】:2020-09-05 04:30:54
【问题描述】:

我试图在标题栏区域绘制一些东西来表示 X,因为没有 WS_CAPTION,它只使用 WS_EX_TOOLWINDOW | WS_EX_TOPMOSTWS_POPUP|WS_THICKFRAME。但我无法在任何地方画出任何东西。我在下面做了一个测试,只是把它全部填成红色,但没有任何改变。我做错了什么或错过了什么?

case WM_NCACTIVATE:
case WM_NCPAINT:
{
  // call default handler (I've tried it both ways, with and without DefWindowProc)
  ::DefWindowProc(hwnd, umsg, wparam, lparam);
  HDC hdc;
  if ((hdc=::GetWindowDC(hwnd))!=NULL) { 
    // Paint into this DC 
    RECT rcwin;
    if (::GetWindowRect(hwnd, &rcwin)) {
      HBRUSH hbrush=::CreateSolidBrush(RGB(255, 0, 0));
      if (hbrush) {
        rcwin.right-=rcwin.left;
        rcwin.bottom-=rcwin.top;
        rcwin.left=rcwin.top=0;
        ::FillRect(hdc, &rcwin, hbrush);
        ::DeleteObject(hbrush);
      }
    }
    ::ReleaseDC(hwnd, hdc);
  }
  return 0;
}

【问题讨论】:

标签: winapi gdi


【解决方案1】:

不要直接在WM_NCACTIVATE 中绘制。如果您需要触发重绘,您可以使用RedrawWindow()。在WM_PAINT/WM_NCPAINT 中进行所有实际绘图。

WM_NCPAINT 中绘图时,documentation 表示使用GetDCEx() 来获取HDC 进行绘图。 wParam 是您可以在其中绘制的HRGN。如果需要,您可以使用GetRgnBox() 获取HRGN 的边界矩形。

case WM_NCPAINT: {
    ::DefWindowProc(hwnd, umsg, wparam, lparam);
    HRGN hrgn = (HRGN)wParam;
    HDC hdc = ::GetDCEx(hwnd, hrgn, DCX_WINDOW | DCX_INTERSECTRGN);
    HBRUSH hbrush = ::CreateSolidBrush(RGB(255, 0, 0));
    ::FillRgn(hdc, hrgn, hbrush);
    ::DeleteObject(hbrush);
    ::ReleaseDC(hwnd, hdc);
    return 0;
}

【讨论】:

  • 问题是使用 MSDN 文档中的示例(与您在上面使用的相同),GetDCEx() 始终返回 NULL。这就是我发现其他人遇到同样问题并使用 GetWindowDC() 的地方。
  • @df234987 见this answerHow to correctly draw simple non-client area。显然我忘记了WM_NCPAINT
【解决方案2】:

基于来自 Remy 的关于邪恶 WM_NCPAINT 的链接,从下面的 pascal 版本转换为 C++ 版本。它与 stackoverflow 中的链接一样有效,但同样,只有在提供 WS_CAPTION 的情况下。我只是为了完整起见在这里发帖。

case WM_NCPAINT:
{
    #ifndef DCX_USESTYLE
      #define DCX_USESTYLE 0x00010000
    #endif

  HDC hdc=::GetDCEx(hwnd, 0, DCX_WINDOW|DCX_USESTYLE);
  if (hdc) {
    RECT rcclient;
    ::GetClientRect(hwnd, &rcclient);
    RECT rcwin;
    ::GetWindowRect(hwnd, &rcwin);
    POINT ptupleft;
    ptupleft.x=rcwin.left;
    ptupleft.y=rcwin.top;
    ::MapWindowPoints(0, hwnd, (LPPOINT) &rcwin, (sizeof(RECT)/sizeof(POINT)));
    ::OffsetRect(&rcclient, -rcwin.left, -rcwin.top);
    ::OffsetRect(&rcwin, -rcwin.left, -rcwin.top);

    HRGN rgntemp=NULL;
    if (wparam==NULLREGION || wparam==ERROR) {
      ::ExcludeClipRect(hdc, rcclient.left, rcclient.top, rcclient.right, rcclient.bottom);
    }
    else {
      rgntemp=::CreateRectRgn(rcclient.left+ptupleft.x, rcclient.top+ptupleft.y, rcclient.right+ptupleft.x, rcclient.bottom+ptupleft.y);
      if (::CombineRgn(rgntemp, (HRGN) wparam, rgntemp, RGN_DIFF)==NULLREGION) {
         // nothing to paint
      }
      ::OffsetRgn(rgntemp, -ptupleft.x, -ptupleft.y);
      ::ExtSelectClipRgn(hdc, rgntemp, RGN_AND);
    }

    HBRUSH hbrush = ::CreateSolidBrush(RGB(255, 0, 0));
    ::FillRect(hdc, &rcwin, hbrush);
    ::DeleteObject(hbrush);

    ::ReleaseDC(hwnd, hdc);
    if (rgntemp!=0) {
      ::DeleteObject(rgntemp);
    }
  }
  return 0;
}

【讨论】:

  • 你的意思是代码没有WS_CAPTION风格就不能工作吗?如果你remove WS_CAPTION,会发生什么?
  • 好吧,有一个例子,而不是这个最新的例子,客户区是一个渐变,所以红色闪烁,或者我可以在调试模式下暂停并看到那个区域的红色。使用WS_CAPTION,您会看到标题栏和它周围的红色框。但没有它,你只有客户区和一个白色的小标题栏,大约是普通标题栏的 1/4 左右,但我不能在上面写,它总是只是一条白色的超粗线。
  • 好的。不要忘记标记您自己的答案。这可能对阅读此主题的其他社区成员有所帮助。
猜你喜欢
  • 1970-01-01
  • 2011-05-13
  • 2016-09-13
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多