On this page : https://learn.microsoft.com/en-us/windows/win32/gdi/wm-ncpaint it is explained how to draw in the nonclient area with GDI.
How can I draw in the nonclient area of my window with Direct2D without having to deal with GDI or GDI+ ?
First of all, WM_NCPAINT is old. Using it will disable the DWM theme-ing for the window, giving a windows classic/7 basic look. So don't do it.
But to use any rendering API do draw in the client area, remove the standard window frame from the window by returning 0 when wParam is true in your WM_NCCALCSIZE message.
case WM_NCCALCSIZE:
if (static_cast<bool>(wParam))
return 0;
return DefWindowProc(hwnd, msg, wParam, lParam);
If you want to keep the standard borders, recalculate the window bounds in WM_NCCALCSIZE.
Then to get a "client area" title bar, use DwmExtendFrameIntoClientArea and extend it from the TOP.
Make sure to handle WM_NCHITTEST so that dragging your window will work too.
Make sure to premultiply your ALPHA in direct2d.
Drawing a rectangle at (0,0) will draw a rectangle in the titlebar of your new custom window.
SEE: https://github.com/oberth/custom-chrome
Related
How Do I Set the Background Color of buttons including a Checkbox button?
I struggled to find the answer to this today - thinking it should be simple to answer this, but the information I stumbled on was less than helpful, so at the risk of duplicating stuff that's out there but I couldn't find, I'll make this quick'n'dirty how-to...
All 'Button' class windows send WM_CTLCOLORSTATIC to their parent window, which can then call ::SetBkColor((HDC)wParam, rgbBkColor), and return a brush for that color.
If this is all using system colors, then the brush handle doesn't need to be managed, you can simply ask for the ::GetSysColor(sysIndex), and return the ::GetSysColorBrush(sysIndex) for the returned brush.
If you're using a custom color, then you'll need to create your own brush and manage the handle for that.
I needed this code for a Message Box replacement, which has the upper part using a white background, and the lower part using a gray background, per the Windows standard message box. So my static control (icon) needed to be white, while my other buttons (including a "Don't ask again" checkbox) needed to have a gray background (checkboxes normally have a white background).
So, I handle WM_ERASEBKGND to paint the two portions of the background correctly, and then I handle WM_CLTLCOLORSTATIC to ensure that all buttons are properly "transparent" for the background that they appear on. In my case, the I used a "Static" control for the icon, which draws its background in gray, and a couple of push-buttons plus a checkbox button - which a checkbox button always paints its background in white, so both required a fix.
My example is using MFC, but hopefully you can translate that trivially enough for your purposes:
// add to the message map:
ON_MESSAGE(WM_CTLCOLORSTATIC, OnCtlColorStatic)
// create the implementation:
LRESULT CRTFMessageBox::OnCtlColorStatic(WPARAM wParam, LPARAM lParam)
{
// buttons and static controls (icon) send WM_CTLCOLORSTATIC, so we can force them to use the correct background color here...
const HDC hdc = (HDC)wParam;
const int idc = ::GetDlgCtrlID((HWND)lParam);
// choose a system color or brush based on if this is icon (static) or another control (a button)
const int idx = idc == IDC_STATIC ? COLOR_WINDOW : COLOR_3DFACE;
// select system color
::SetBkColor(hdc, GetSysColor(idx));
// return system brush (which we don't need to delete!)
return (LRESULT)GetSysColorBrush(idx);
}
I want to draw a rectangle around my window but I don't want to override the title bar.
what I wrote so far in the window callback function is:
case WM_NCPAINT:
{
HDC hdc;
RECT rect;
HPEN pen;
hdc=GetDCEx(hWnd,(HRGN)wParam,DCX_WINDOW|DCX_CACHE|DCX_INTERSECTRGN|DCX_LOCKWINDOWUPDATE);
GetWindowRect(hWnd,&rect);
pen=CreatePen(PS_SOLID, 10, RGB(255, 0, 0));//red pen 10 pixels in size
SelectObject(hdc,pen);
Rectangle(hdc,0,0,(rect.right-rect.left),(rect.bottom-rect.top));
DeleteObject(pen);
ReleaseDC(hWnd,hdc);
}
break;
However, this draws over the window title bar with white brush.
How can I make it not to paint over the title bar? I'm loosing the title bar text and the menu...
I have tried using HOLLOW_BRUSH before creating the pen as follows:
HBRUSH b=CreateSolidBrush(HOLLOW_BRUSH);
SelectObject(hdc,b);
But that only caused the title bar to not be drawn at all (being black).
By handling the WM_NCPAINT message, you are telling the window manager that you are taking responsibility for painting the entire non-client area, and so the window manager will not draw any of it for you.
If you want the original title bar to be drawn then you need to call DefWindowProc() first, then do your own drawing "on top" of what it draws.
You may also need to use ExcludeClipRect() to prevent the client area from being drawn over if you wish to draw the entire non-client area at once with a single rectangle.
I have created a Dialog Box in a c++ windows application (using ATL lib) and I have set on it three buttons. The button lie as usual at the bottom of the Dialog Box. I want by the resize of the Dialog Box, the button to retain their position at the bottom of it. In other words, they should keep a constant (low) distance from the bottom margin and the on side (right or left) of the Dialog Box. To bring this into effect, I try to move the buttons accordingly while the Dialog Box size is changed. I use the following code (as example only with the OK button), but the only result is the button to disappear during the resize. How should I modify the code in order for the button to retain its distance from the bottom and the right side of the Dialog Box?
LRESULT RenameFolderDlg::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT r;
GetWindowRect(&r);
CWindow okB(GetDlgItem(IDOK));
RECT okR;
okB.GetWindowRect(&okR);
okB.MoveWindow( r.right - 80 , r.bottom - 40, okR.right - okR.left, okR.bottom - okR.top, 1);
return 0;
}
I use winapi. In that I use GetWindowRect to fetch cordinates of control and then use MapWindowPoints to map those rectangular coordinates on to screen. Then use SetWindowPos to position windows according to mapped rectangular coordinates.
On Google, I found MapWindowPoints for ATL. I think what you are legging in your code is mapwindowpoints. Try using that.
Hope it work....
Instead of
GetWindowRect(&r);
you shall use
GetClientRect(&r);
because the coordinates returned by GetWindowRect are relative to the upper-left corner of the screen, while MoveWindow called for a child control expects they are relative to the upper-left corner of the parent window's client area.
Is there any function (C++, MFC) to obtain window's background color?
There's a default background color for windows, use GetSysColor(COLOR_WINDOW). But ultimately a window decides itself what is 'background' and what is 'foreground'. Affected by WNDCLASS.hbrBackground and the window's WM_ERASEBKGND and WM_PAINT message handlers. Anything is possible.
CDC* pDc= GetDC();
COLORREF crBkgnd = pDc->GetBkColor();
I am not using a dialog, I'm using my own custom class which I have registered and then used the CreateWindow call to create it, I have preset the background color to red when registering:
WNDCLASSEX wc;
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
But now I want to change the background color at runtime, by e.g. clicking a button to change it to blue.
I have tried to use SetBkColor() call in the WM_PAINT, and tried returning a brush from the WM_CTLCOLORDLG message, they don't work.
Any help?
From Window Background comes:
...The system paints the background for a
window or gives the window the
opportunity to do so by sending it a
WM_ERASEBKGND message when the
application calls BeginPaint. If an
application does not process the
message but passes it to
DefWindowProc, the system erases the
background by filling it with the
pattern in the background brush
specified by the window's class.....
...... An application can process the
WM_ERASEBKGND message even though a
class background brush is defined.
This is typical in applications that
enable the user to change the window
background color or pattern for a
specified window without affecting
other windows in the class. In such
cases, the application must not pass
the message to DefWindowProc. .....
So, use the WM_ERASEBKGND message's wParam to get the DC and paint the background.
You may try the following:
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
Short answer: Handle WM_ERASEBKGND.
Longer answer:
When you register the WNDCLASS, you're providing information about all windows of that class. So if you want to change the color of just one instance of the window, you'll need to handle it yourself.
When it's time to repaint your window, the system will send your wndproc a WM_ERASEBKGND message. If you don't handle it, the DefWindowProc will erase the client area with the color from the window class. But you can handle the message directly, painting whatever color (or background pattern) you like.