0

I'm trying to draw something in the title bar area to represent an X since there is no WS_CAPTION, it just uses WS_EX_TOOLWINDOW | WS_EX_TOPMOST and WS_POPUP|WS_THICKFRAME. But I can't get anything to draw anywhere. I did a test below to just fill it all in red, but nothing changed. What am I doing wrong or missing?

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;
}
df234987
  • 513
  • 2
  • 13
  • Related, possible duplicate: [How to correctly draw simple non-client area (4 px red border)?](https://stackoverflow.com/questions/50132757/) – Remy Lebeau May 19 '20 at 19:09

2 Answers2

2

Based on a Link from Remy about the evil WM_NCPAINT, converted from the pascal version to the C++ version below. It works as well as the link in stackoverflow but again, only if WS_CAPTION is provided. I'm just posting here for completeness.

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;
}
df234987
  • 513
  • 2
  • 13
  • Do you mean the code does not work without `WS_CAPTION` style? If you `remove WS_CAPTION`, what will happen? – Strive Sun May 20 '20 at 07:00
  • Well with one of the examples, not this latest one, the client area is a gradient so the red flashed or I could pause in debug mode and see the red in that area. With the `WS_CAPTION` you see the title bar and frame around it red. But without that, you just have the client area and a small white titlebar that is about 1/4 or so of a normal titlebar but I can't write on that, it's always just a white extra thick line. – df234987 May 20 '20 at 23:36
  • All right. Do not forget to mark your own answer. This can be beneficial to other community members reading this thread. – Strive Sun May 27 '20 at 06:31
1

Do not draw directly in WM_NCACTIVATE. If you need to trigger a repaint, you can use RedrawWindow() for that. Do all of the actual drawing in WM_PAINT/WM_NCPAINT.

When drawing in WM_NCPAINT, the documentation says to use GetDCEx() to get the HDC to draw on. The wParam is an HRGN that you can draw within. You can use GetRgnBox() to get the bounding rectangle of the HRGN, if needed.

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;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    The problem is that using the example in the MSDN docs (same as what you are using above), the GetDCEx() always returns NULL. That's where I found others had the same problem and used GetWindowDC() instead. – df234987 May 19 '20 at 18:51
  • 1
    @df234987 see [this answer](https://stackoverflow.com/a/50135792/65863) to [How to correctly draw simple non-client area](https://stackoverflow.com/questions/50132757/). Apparently I forgot that there are undocumented gotchas with `WM_NCPAINT` – Remy Lebeau May 19 '20 at 19:05
  • **Do not draw directly in WM_NCACTIVATE**. @RemyLebeau you are wrong. Look at the Windows classic theme: system buttons and caption are drawn **exactly** in `WM_NCACTIVATE` when main window becomes active or inactive. Not in `WM_NCPAINT`! Also if you click on HELP system button and then click anywhere in the window then close button and help button will be redrawn not on `WM_NCACTIVATE`, not on `WM_NCPAINT` and even not in `WM_PAINT`. They will be redrawn somewhere else. This is according to my non-client drawings research. – Ig_M May 22 '22 at 22:52