I'm working on a windows desktop application and I noticed to my dismay that my text was flickering so upon searching I found two methods - return true for erasebkgnd and double buffering. The first method didn't do anything and the second made my background all black for some reason and it still flickers.
Below is where I call paint and the buffering:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
putImage(screen, hWnd);
RECT rc;
GetClientRect(hWnd, &rc);
HDC memdc;
auto hbuff = BeginBufferedPaint(screen, &rc, BPBF_COMPATIBLEBITMAP, NULL, &memdc);
EndBufferedPaint(hbuff, TRUE);
EndPaint(hWnd, &ps); } break;
and this next part is the putImage function
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
graphic.DrawImage(Image::FromFile(filePath), 10, 10);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
I was wondering what the problem was and if there is a fix for this.
my background all black
This issue is due to not filling the your background. You can fill your background before draw the image like this:
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
flickering
This issue is due to not using BeginBufferedPaint in correct manner. Call BeginBufferedPaint before drawing the image like this:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
EndPaint(hWnd, &ps); } break;
And don't forget to call BufferedPaintInit and BufferedPaintUnInit. For example like this:
case WM_CREATE:
{
hr = BufferedPaintInit();
}
break;
//...
case WM_DESTROY:
{
BufferedPaintUnInit();
PostQuitMessage(0);
}
break;
Update: The complete code.
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
Image* image = Image::FromFile(L"path-to\\test.png");
Status status = graphic.DrawImage(image, 10, 10);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DWORD err;
HRESULT hr;
static HDC memdc;
switch (message)
{
case WM_CREATE:
{
hr = BufferedPaintInit();
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
EndPaint(hWnd, &ps); } break;
case WM_DESTROY:
{
BufferedPaintUnInit();
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Related
I am writing the WinAPI Application, and I tried to write a function, that would automate for me creating Rectangles for text input (with Font, BkMode, TextColor), that then is called by DrawContent function called by WM_PAINT. The text does not appear, after I call ELW::AddTextControl.
LRESULT CALLBACK ELW::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
ELW::DrawContent(hWnd, hInst, hdc);
}
break;
case WM_CLOSE:
{
DestroyWindow(hWnd);
UnregisterClass(CLASS_NAME, hInst);
}
break;
case WM_SYSCOMMAND:
{
if (wParam == SC_CLOSE);
break;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Content drawing
void ELW::DrawContent(HWND hWnd, HINSTANCE hInst, HDC hdc)
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
RECT testowyRectangle = { 0, 1 , 20 , 30 };
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_GRAYTEXT + 1));
ELW::AddTextControl("Testowy test", 80, 80, 100, 100, CUSTOM_COLOR_YELLOW_COLORREF, 10, "Euroscope", hdc);
EndPaint(hWnd, &ps);
};
void ELW::AddTextControl(const char* text, int xPos, int yPos, int xSize, int ySize, COLORREF rgb, int fontSize, const char* fontName, HDC hdc)
{
RECT TextRectangle = { xPos, yPos, ( xPos + xSize ) , ( yPos + ySize ) }; // Left, Top, Right, Bottom
if (rgb == NULL)
rgb = RGB(0, 0, 0);
if (fontSize == NULL)
fontSize = 10;
if (fontName == NULL)
fontName = "Euroscope";
HFONT hFont = CreateFont(fontSize, 8, 0, 0, 600, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT(fontName));
SelectObject(hdc, hFont);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, rgb);
DrawText(hdc, TEXT(text), strlen(text), &TextRectangle, DT_CENTER | DT_NOCLIP);
}
Found an error:
strlen(text)
should be
strlen(TEXT(text))
I am a bit new to c++ and I am trying to create a gui application to tell me whether my caps lock is active or not. I have already set up the basic UI and it starts up according to plan (by showing me my lock state via colors) but I cannot manage to change the window color at runtime.
here's my code :
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
bool state = false;
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(0, 255, 0)));
}
else {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(255, 0, 0)));
}
EndPaint(hwnd, &ps);
}
case WM_KEYUP:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(0, 255, 0)));
}
else {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(255, 0, 0)));
}
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
and thanks in advance.
Don't call (Begin/End)Paint() in your WM_KEYUP handler, and don't call GetKeyState() in your WM_PAINT handler. Have WM_KEY(DOWN|UP) save the desired color to a variable and then call InvalidateRect() whenver that variable changes value to trigger a repaint of your window. Let WM_PAINT draw the window using the current value of that variable as needed.
Also, your case blocks are missing break statements. And you are leaking the HBRUSH returned by CreateSolidBrush().
Try something more like this:
COLORREF color;
void UpdateColorForCapsLock()
{
if (GetKeyState(VK_CAPITAL) & 0x0001) {
color = RGB(0, 255, 0);
}
else {
color = RGB(255, 0, 0);
}
}
RESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
{
UpdateColorForCapsLock();
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(hdc, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
return 0;
}
case WM_KEYDOWN:
case WM_KEYUP:
{
if (wParam == VK_CAPITAL)
{
UpdateColorForCapsLock();
InvalidateRect(hwnd, NULL, TRUE);
}
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Capture the WM_KEYDOWN message. If i's a Caps Lock key press (check the parameters), make your window repaint itself by calling InvalidateRect.
I have created a custom window frame using DWM. The frame extends successfully but whenever I try to draw onto the frame, the extended frame coveres whatever I am trying to draw. I have seen other people try to input a top left within negative bounds, but even when I try to do that, the title bar still overlaps the main window's painting. Here is my code (note: i don't have any code for hit testing):
#include <Windows.h>
#include <numeric>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
const auto s_brush = CreateSolidBrush(RGB(0, 0, 255));
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
LRESULT res;
if (DwmDefWindowProc(hwnd, msg, wparam, lparam, &res))
return res;
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
{
RECT r;
GetWindowRect(hwnd, &r);
SetWindowPos(hwnd, 0, 0, 0, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED);
}
break;
case WM_ACTIVATE:
{
int metrics[4];
const auto window_dpi_ = GetDpiForWindow(hwnd);
metrics[0] = GetSystemMetricsForDpi(SM_CYCAPTION, window_dpi_);
metrics[1] = GetSystemMetricsForDpi(SM_CXFIXEDFRAME, window_dpi_);
metrics[2] = GetSystemMetricsForDpi(SM_CYSIZEFRAME, window_dpi_);
metrics[3] = GetSystemMetricsForDpi(SM_CYBORDER, window_dpi_);
const auto cy_titlebar_ = std::accumulate(metrics, metrics + sizeof metrics / sizeof(int), 0);
MARGINS margins{ 0, 0, cy_titlebar_, 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
const auto hdc = BeginPaint(hwnd, &ps);
const auto old = SelectObject(hdc, s_brush);
Rectangle(hdc, 0, 0, 50, 75);
SelectObject(hdc, old);
EndPaint(hwnd, &ps);
}
break;
case WM_NCCALCSIZE:
if (wparam == TRUE)
{
RECT& client_rect = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lparam)->rgrc[0];
const auto window_dpi_ = GetDpiForWindow(hwnd);
const auto frame_width{ GetSystemMetricsForDpi(SM_CXFRAME, window_dpi_) };
const auto border_width{ GetSystemMetricsForDpi(SM_CXPADDEDBORDER, window_dpi_) };
const auto frame_height{ GetSystemMetricsForDpi(SM_CYFRAME, window_dpi_) };
client_rect.bottom -= frame_height + border_width;
client_rect.left += frame_width + border_width;
client_rect.right -= frame_width + border_width;
break;
}
default:
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE, LPWSTR lpcmdline, int cmd_show)
{
WNDCLASS wc{ CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hinstance,
0,0, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), 0, L"CustWnd" };
const auto hwnd = CreateWindow(MAKEINTATOM(RegisterClass(&wc)), L"Custom Window Frame", WS_OVERLAPPEDWINDOW,
0, 0, 500, 700, 0, 0, hinstance, 0);
ShowWindow(hwnd, cmd_show);
UpdateWindow(hwnd);
MSG msg;
while (GetMessageW(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
The window class doesn't have a default cursor, it's going to show the wrong cursors as you move the mouse. Change wc to
WNDCLASS wc{ CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hinstance, 0,
LoadCursor(NULL, IDC_ARROW),
reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), 0, L"CustWnd" };
WM_NCHITTEST should also be handled, otherwise title-bar will not grip. It is better to calculate the border thickness based on Windows style, or keep it as static value because it will be needed throughout the procedure, as well as title bar height.
Note that this code will look very different in Windows 10 versus Window 7 which has the weird transparent title-bar, you'll need 32-bit bitmap with alpha channel to draw on title-bar. Or use buffered paint with BufferedPaintSetAlpha as shown below
#include <Windows.h>
#include <Windowsx.h> //for `GET_X_LPARAM` etc.
...
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static int cy_titlebar_ = 100;
static RECT border_thickness;
LRESULT result;
if(DwmDefWindowProc(hWnd, msg, wParam, lParam, &result))
return result;
switch(msg)
{
case WM_CREATE:
{
//find border thickness
border_thickness = { 0 };
if(GetWindowLongPtr(hWnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness,
GetWindowLongPtr(hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if(GetWindowLongPtr(hWnd, GWL_STYLE) & WS_BORDER)
{
border_thickness = { 1,1,1,1 };
}
MARGINS margins = { 0, 0, cy_titlebar_, 0 };
DwmExtendFrameIntoClientArea(hWnd, &margins);
SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
return 0;
}
case WM_NCCALCSIZE:
{
if(wParam)
{
RECT& r = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam)->rgrc[0];
r.left += border_thickness.left;
r.right -= border_thickness.right;
r.bottom -= border_thickness.bottom;
return 0;
}
break;
}
case WM_NCHITTEST:
{
result = DefWindowProc(hWnd, msg, wParam, lParam);
if(result == HTCLIENT)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hWnd, &pt);
if(pt.y < border_thickness.top) return HTTOP;
if(pt.y < cy_titlebar_) return HTCAPTION;
}
return result;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
auto hdc = BeginPaint(hWnd, &ps);
//paint opaque:
RECT rc{ 0, 0, 100, cy_titlebar_ };
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
HDC memdc;
HPAINTBUFFER hbuffer = BeginBufferedPaint(
hdc, &rc, BPBF_TOPDOWNDIB, ¶ms, &memdc);
auto brush = CreateSolidBrush(RGB(255, 0, 0));
FillRect(memdc, &rc, brush);
DeleteObject(brush);
SetBkMode(memdc, TRANSPARENT);
DrawText(memdc, L"Opaque", -1, &rc, 0);
BufferedPaintSetAlpha(hbuffer, &rc, 255);
EndBufferedPaint(hbuffer, TRUE);
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
So I'm working on an emulator, and got stuck on trying to paint a GDI window. Basically I have this code:
switch(uMsg)
{
case WM_SIZE:
break;
case WM_PAINT:
{
PAINTSTRUCT Ps;
HDC const PaintDC = BeginPaint(hwnd, &Ps);
RECT ClientRect;
GetClientRect(hwnd, &ClientRect);
int const WindowWidth = ClientRect.right - ClientRect.left;
int const WindowHeight = ClientRect.bottom - ClientRect.top;
StretchDIBits(PaintDC,
0, 0,
WindowWidth,
WindowHeight,
0, 0,
DecodedScreenMemory.Info.bmiHeader.biWidth,
DecodedScreenMemory.Info.bmiHeader.biHeight,
DecodedScreenMemory.Memory,
&DecodedScreenMemory.Info,
DIB_RGB_COLORS,
SRCCOPY);
EndPaint(hwnd, &Ps);
break;
}
case WM_CLOSE:
{
Running = false;
Result = 0;
break;
}
default:
{
Result = DefWindowProc(hwnd,
uMsg,
wParam,
lParam);
}
}
In another place of the program I have a piece of code that, for now, fills an 8 pixels wide border with white leaving the rest black. So, from what I understand, I have the window buffer, then I call StretchDIBits with appropriate parameters, then at every frame I call UpdateWindow and it should render the buffer on screen. Why does it turn black instead?
I'm trying to make a window with a glass background, but it's not working. See my code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_ERASEBKGND: {
RECT rect;
GetClientRect(hWnd, &rect);
FillRect(GetDC(hWnd), &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
return(0);
} case WM_PAINT: {
RECT rect;
GetClientRect(hWnd, &rect);
rect.bottom = 262;
FillRect(GetDC(hWnd), &rect, (HBRUSH)COLOR_WINDOW);
return(0);
} case WM_CREATE: {
if (IsWindowsVistaOrGreater()) {
BOOL IsCompositionEnabled = FALSE;
DwmIsCompositionEnabled(&IsCompositionEnabled);
if (IsCompositionEnabled) {
MARGINS margins = {0, 0, 0, 0};
margins.cyBottomHeight = 100;
HRESULT hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (SUCCEEDED(hr)) {
}
}
}
return(0);
} case WM_CLOSE: {
DestroyWindow(hWnd);
return(0);
} case WM_DESTROY: {
PostQuitMessage(0);
return(0);
} default: {
return(DefWindowProc(hWnd, message, wParam, lParam));
}
}
}
This code makes the following window: See the image
This window is what I'm trying to do but there's a problem. When I move the window it blinks. Why does it blink?
There are a few things you should change:
When you handle WM_PAINT, you should call BeginPaint to get the DC and other info for your painting, then call EndPaint when you are done. This gives you a DC that restricts your painting to the clipping region needed, and prevents the flicker.
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
rect.bottom = 262;
FillRect(hdc, &rect, (HBRUSH)COLOR_WINDOW);
EndPaint(hWnd, &ps);
return(0);
}
You should also return 1 from WM_ERASEBKGND as you have done the erasing.
The HDC to use when erasing is passed in wParam so use that rather than the window DC.
case WM_ERASEBKGND: {
RECT rect;
GetClientRect(hWnd, &rect);
FillRect((HDC)(wParam), &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
return(1);
}