I am trying to draw an image using GDI+. When I do it inside WM_PAINT it works:
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
Gdiplus::Image gdiImage(L"unt.png");
graphics.DrawImage(&gdiImage, 40, 40);
EndPaint(hWnd, &ps);
break;
}
But when I do it on a button click or inside WM_CREATE it doesn't draw the image:
HDC hdc2 = GetDC(hWnd);
Gdiplus::Graphics graphics(hdc2);
Gdiplus::Image gdiImage(L"unt.png");
graphics.DrawImage(&gdiImage, 40, 40);
Even if I use BeginPaint() and EndPaint() it still fails. So, is there any way to draw the image outside of WM_PAINT?
In Win32, almost all drawing must happen in WM_PAINT handler.
Probably you don't see any drawing because as soon you finish to handle the button click, you receive a WM_PAINT.
If you draw outside WM_PAINT your drawing have short life because of invalidation of windows and then WM_PAINT message.
So the correct way to draw in Win32 is the WM_PAINT handler.
I edited the answer after a comment of author.
Suppose you need to change the image after a mouse click. You can do:
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
Gdiplus::Image gdiImage(clicked ? L"image_A.png" : L"image_B.png");
graphics.DrawImage(&gdiImage, 40, 40);
EndPaint(hWnd, &ps);
break;
}
case WM_LBUTTONDOWN: {
clicked = true;
InvalidateRect(hwnd, NULL, true);
break;
}
The InvalidateRect function is the answer. With that function you tell to Windows to redraw the window. This is the link to man page:InvalidateRect
If you need to update an image based on a button click (or any other Windows Event handler for that matter), you have to invalidate the area that the image occupies on the screen—usually through InvalidateRect()—and then have your WM_PAINT handler draw the image.
Related
I am trying to create a borderless window with the standard shadow. I read on the winapi documentation that setting pMarInset with negative values to the DwmExtendFrameIntoClientArea function, creates the "sheet of glass" effect where the client area is rendered without a window border. This gives me the result I wanted, which is a borderless window with the standard shadow. However, when I resize the window, I can see the standard frame behind the solid background color I gave in the WNDCLASS struct. I tried clearing the screen with a solid color but I get some weird results:
This is the window when active:
And when the window is inactive:
When I hover over the app icon in the taskbar, I see the solid color I tried clearing the screen with:
Here is my window procedure:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(handle, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_HIGHLIGHT+1));
EndPaint(handle, &ps);
return 0;
} break;
case WM_ACTIVATE: {
MARGINS margins = {-1};
DwmExtendFrameIntoClientArea(handle, &margins);
return 0;
} break;
case WM_NCCALCSIZE: {
if(wparam){ return 0; }
return DefWindowProcA(handle, message, wparam, lparam);
} break;
I am trying to render some text on screen. I am using GDI, C++ and trying to use DrawText and TextOut functions to render my text. My text only appears when program starts, and then text immediatily disappear. Am i able to use it with GDI and if i am, then how?
HDC hDC;
PAINTSTRUCT Ps;
HFONT font;
LOGFONT LogFont;
...
hDC = BeginPaint(hWnd, &Ps);
GDI render code
LogFont.lfStrikeOut = 0;
LogFont.lfUnderline = 0;
LogFont.lfHeight = 42;
LogFont.lfEscapement = 0;
LogFont.lfItalic = TRUE;
font = CreateFontIndirect(&LogFont);
SelectObject(hDC, font);
TextOut(hDC, 20, 18, "Some text", 14);
DeleteObject(font);
EndPaint(hWnd, &Ps);
Using code from this lesson.
My text only appears when program starts, and then text immediately disappear This usually happens when drawing is done not in WM_PAINT message handler.
INTRODUCTION:
I have decided to make a test project in MS Visual Studio 2008, in C++ to test a small program in pure WIN32, regarding painting a bitmap as window's background.
PROBLEM:
Window should have gray brush and a bitmap picture stretched over its client area.
In my code for WM_PAINT, if I try to paint gray brush for window, without bitmap, everything seems to work well.
Same goes if I just try to put a bitmap as a background.
Yet, when I combine these two, so I can get a bitmap picture stretched, and gray background behind bitmap, this is what happens:
Bitmap picture appears to "stands still", but gray brush appears over entire window for a second, then disappears entirely, so only stretched bitmap is seen, and then appears again, and so on.
It seems as if it is drawn from top going to the bottom, and it seems as if application is doing it all over again.
This is how I see it when I start my program.
RELEVANT INFORMATION:
The program was made by choosing option File->New, and then choosing Win32 project from the options.
Window class was set automatically, and the following members were set like this:
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
I have added static global variable to store bitmap handle:
static HBITMAP bmp;
In the window procedure, made by the wizard, I have initialized it with following code:
case WM_CREATE:
bmp = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP1) );
return 0;
break;
In the window procedure, made by the wizard, I have added WM_PAINT handler, with following code:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
RECT r;
GetClientRect( hWnd, &r );
// TODO: Add any drawing code here...
// fill client area with gray brush, just to test
FillRect( hdc, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) );
// memory DC for double buffering
HDC MemDC = CreateCompatibleDC( hdc );
// select our bitmap into memory DC
HBITMAP old = (HBITMAP)SelectObject( MemDC, bmp );
// get bitmap's width and height so we can stretch it
BITMAP b;
GetObject( bmp, sizeof(BITMAP), &b );
// stretch our bitmap
StretchBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top,
MemDC, 0, 0, b.bmWidth, b.bmHeight, SRCCOPY );
// perform proper cleanup
SelectObject( MemDC, old );
DeleteDC(MemDC);
EndPaint(hWnd, &ps);
}
return 0L;
break;
I have also invalidated client area when window is resized, or erasing of background happens, like this:
case WM_ERASEBKGND:
InvalidateRect( hWnd, NULL, TRUE );
return 1L;
break;
case WM_SIZE:
InvalidateRect( hWnd, NULL, TRUE );
return 0L;
Bitmap is destroyed like this:
case WM_DESTROY:
DeleteObject( bmp );
PostQuitMessage(0);
break;
IMPORTANT NOTE:
Even if I comment out handlers for WM_SIZE and WM_ERASEBKGND, the effect still occurs.
I do not have much experience with double buffering, but this is simple thing to do.
I just fail to see the mistake, so I ask more experienced and skillfull colleagues to help.
If additional source code is required, ask for it and I will post it, but until then I will omit it to keep the question brief.
You should not call InvalidateRect in WM_ERASEBKGND. That's just going to force an endless series of paint cycles.
The job of WM_ERASEBKGND is to paint the background, and that's all you should ever do. If your WM_PAINT is going to paint the entire window, then there's no need to paint any background. In which case, and I think this is your scenario, you should do nothing in WM_ERASEBKGND.
I am trying to implement double buffering but it doesn't seem to work i.e. the graphic still flickers.
The WM_PAINT gets called everytime when the mouse moves. (WM_MOUSEMOVE)
Pasted WM_PAINT below:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rect;
GetClientRect(hWnd, &rect);
int width=rect.right;
int height=rect.bottom;
HDC backbuffDC = CreateCompatibleDC(hdc);
HBITMAP backbuffer = CreateCompatibleBitmap( hdc, width, height);
int savedDC = SaveDC(backbuffDC);
SelectObject( backbuffDC, backbuffer );
HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
FillRect(backbuffDC,&rect,hBrush);
DeleteObject(hBrush);
if(fileImport)
{
importFile(backbuffDC);
}
if(renderWiredCube)
{
wireframeCube(backbuffDC);
}
if(renderColoredCube)
{
renderColorCube(backbuffDC);
}
BitBlt(hdc,0,0,width,height,backbuffDC,0,0,SRCCOPY);
RestoreDC(backbuffDC,savedDC);
DeleteObject(backbuffer);
DeleteDC(backbuffDC);
EndPaint(hWnd, &ps);
}
Add the following handler:
case WM_ERASEBKGND:
return 1;
The reason it works is because this message is sent before painting to ensure that painting is done on the window class's background. The flashing is going back and forth between the background and what's painted over it. Once the background has stopped being painted, it stops conflicting with what is painted over it, which includes filling the window with a solid colour, so there will still be a background anyway.
I'm trying to adapt a screensaver written in C++ with WinAPIs to work for multiple monitors. I found this article that suggests to rewrite this basic WM_PAINT handler:
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd, &ps );
DoDrawing(hdc, ps.rcPaint);
EndPaint(hWnd, &ps);
}
break;
void DoDrawing(HDC hDC, RECT rcDraw)
{
//Do actual drawing in 'hDC'
}
Into something like this to incorporate drawing for multiple screens:
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdcE = BeginPaint(hWnd, &ps );
EnumDisplayMonitors(hdcE,NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
}
break;
BOOL CALLBACK MyPaintEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdc1, // handle to monitor DC
LPRECT lprcMonitor, // monitor intersection rectangle
LPARAM data // data
)
{
RECT rc = *lprcMonitor;
// you have the rect which has coordinates of the monitor
DoDrawing(hdc1, rc);
// Draw here now
return 1;
}
But the question I have is what about special optimization/clipping that BeginPaint() sets up in the DC after processing WM_PAINT message? With this approach it will be lost. Any idea how to preserve it across the EnumDisplayMonitors() call?
The answer is actually described in the MSDN docs for EnumDisplayMonitors. When you pass an HDC parameter to EnumDisplayMonitors, then the DC it passes to your callback function is a subset of the DC that you originally passed in with these changes:
The clip has been further reduced to only cover the intersection of the original clip with the monitor's clip rectangle.
The color format of the DC is for the specific monitor rather than for the "primary" monitor of the window.
Note that in modern Windows (at least since Win8), you will never really see different color formats in practice for Window DCs since GDI always runs with 32-bit color.