Resize window to fit content after WM_PAINT - c++

is there a way to resize after all WM_PAINT calls are done? so that i can automatically resize my window to fit whatever i have painted, since initially my window size is defined at a certain size, there may be leftover spaces that are not painted, how can i remove these leftover spaces?
As you can see from the screenshot below the white spaces are the leftover spaces.
#include <windows.h>
#include "resource.h"
#include "loadFromTextFile.h"
const char g_szClassName[] = "myWindowClass";
const int recLength = 3;
const int wndHeight = 700;
const int wndWidth = 700;
PAINTSTRUCT ps;
HDC hdc;
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
HPEN myPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
//load from text
string FILE_PATH = ("10x10.txt");
loadFromTextFile loaded = loadFromTextFile(FILE_PATH);
int mazeWidth = loaded.getColumns() * recLength;
int mazeHeight = loaded.getRows() * recLength;
void paintMaze(HWND hwnd, vector<vector<string> > grid){
hdc = ::BeginPaint(hwnd, &ps);
SelectObject(hdc, myPen);
for (int r = 0; r < loaded.getRows(); r++){
for (int c = 0; c < loaded.getColumns(); c++){
RECT rect;
rect.left = c*recLength;//x upper left
rect.top = r*recLength;//y upper left
rect.right = (c + 1)*recLength;//x lower right corner
rect.bottom = (r + 1)*recLength;//y lower right corner
if (grid[r][c] == "1"){//1 path
FillRect(hdc, &rect, blackBrush);
}
else if (grid[r][c] == "0"){//wall
FillRect(hdc, &rect, whiteBrush);
}
if (r == 50 && c == 50){
FillRect(hdc, &rect, redBrush);
}
}
}
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(redBrush);
DeleteObject(myPen);
EndPaint(hwnd, &ps);
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_ABOUT:
SetWindowPos(hwnd, 0, 0, 0, mazeWidth, mazeHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
break;
case WM_PAINT:
{
paintMaze(hwnd, loaded.getNodeGrid());
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);;
wc.lpszClassName = g_szClassName;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 32, 32, 0);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
" Maze",
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,//prevent resize
CW_USEDEFAULT, CW_USEDEFAULT, wndWidth, wndHeight,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}

Paint in an offscreen buffer.
This allows you to resize the window.
Paint the window by copying the buffer.

Related

Drawing a semi-transparent rectangle in a transparent window

I found this code in stackoverflow and it really draws a rectangle on a transparent window using a bitmap. But somehow I cannot change the transparency of the rectangle. More precisely, I can, but it gets darker. As if the bitmap itself has a black background. How do I make the rectangle transparent?
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
Full code
#include <Windows.h>
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
WNDCLASS windowClass {};
windowClass.lpfnWndProc = windowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = L"Keystrokes";
windowClass.style = CS_NOCLOSE;
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&windowClass))
{
return 0;
}
HWND hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, L"Keystrokes", L"Keystrokes", WS_POPUP, 0, 0, 148, 140, 0, 0, hInstance, 0);
if (!hwnd)
{
return 0;
}
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
ShowWindow(hwnd, nCmdShow);
MSG msg {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
paintRect(hDC, { 0, 0, 48, 48 }, RGB(255, 0, 0), RGB(255, 255, 255), 255);
EndPaint(hwnd, &ps);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
“As if the bitmap itself has a black background.”
Because your background is set to:
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
Of course you can set it to NULL_BRUSH to make it look transparent:
windowClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
But when you move it, you will find that this is not to make the window transparent, but to stop drawing the background:
As #Ben answers here:
You haven't made the window transparent, you have just stopped drawing the background. What you see as the background is just whatever happened to be underneath the window when it was first drawn.
And you need to refer to Layered Windows
You can't add rectangles directly on the window, you should use a layered window, and then add what you need.
Here is a sample that you can refer to it:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("test window");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, szAppName,
TEXT("the hello program"),
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
148,
148,
NULL,
NULL,
hInstance,
NULL);
SetLayeredWindowAttributes(hwnd, 0, 1, LWA_ALPHA);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
VOID FadeRect(RECT* prc, HDC hdc)
{
BOOL fFade = FALSE;
static HWND hwnd;
SIZE size;
POINT ptSrc = { 0, 0 };
BLENDFUNCTION blend;
SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0);
if (!fFade)
return;
if (!hwnd) hwnd = CreateWindowEx(WS_EX_LAYERED |
WS_EX_TRANSPARENT |
WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
L"static", L"static", WS_POPUP | WS_VISIBLE, prc->left,
prc->top, prc->right, prc->bottom, NULL, (HMENU)0, NULL, NULL);
else MoveWindow(hwnd, prc->left, prc->top, prc->right, prc->bottom, TRUE);
RECT rect{ prc->left,prc->top,prc->right,prc->bottom };
size.cx = prc->right - prc->left;
size.cy = prc->bottom - prc->top;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 150;
UpdateLayeredWindow(hwnd, NULL, NULL, &size, hdc, &ptSrc, 0,
&blend, ULW_ALPHA);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rect;
GetClientRect(hwnd, &rect);
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rect, 2);
HDC hDC = BeginPaint(hwnd, &ps);
RECT rc{ rect.left,rect.top,rect.left + 48,rect.top + 48 };
FadeRect(&rc, hDC);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

Child window with OpenGL not responding to gluOrtho2D()

I created a child window using WinAPI, now I'm trying to draw a triangle there. It is drawn, but its scale is not correct.
I think this is due to the fact that I have not properly installed orthogonal system. It is set in the init() function, I call it when the child window is created in the WM_CREATE message, it is triggered, but the orthographic projection is still not set to the desired size. So I only see the bottom of the triangle.
#include <Windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")
//opengl values
int windowWidth = 600, windowHeight = 800, windowDepth = 600;
void init();
HWND childOpenGLWindowHWND = NULL;
HWND CreateOpenGLChildWindow(wchar_t* title, int x, int y, int width, int height,
BYTE type, DWORD flags, HWND hWndParent, HINSTANCE hInstance, WNDPROC wndProc)
{
int pf;
HDC hDC;
HWND hWnd;
PIXELFORMATDESCRIPTOR pfd;
WNDCLASS wc;
wc.style = CS_OWNDC;
wc.lpfnWndProc = wndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: "
L"Cannot register window class.", L"Error", MB_OK);
return NULL;
}
hWnd = CreateWindow(
L"OpenGL",
title,
WS_CHILD,
x,
y,
width,
height,
hWndParent,
NULL,
NULL,
NULL
);
if (!hWnd) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_TYPE_RGBA | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, L"ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.", L"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, L"SetPixelFormat() failed: "
"Cannot set format specified.", L"Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
wchar_t WinName[] = L"MainFrame";
int WINAPI WinMain(
HINSTANCE This,
HINSTANCE Prev,
LPSTR cmd,
int mode
)
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd;
MSG msg;
WNDCLASS wc;
wc.hInstance = This;
wc.lpszClassName = WinName;
wc.lpfnWndProc = WndProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
if (!RegisterClass(&wc)) return NULL;
int windowWidth = 800;
int windowHeight = 800;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
hWnd = CreateWindow(
WinName,
L"Title",
WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX,
(screenWidth - windowWidth) / 2,
(screenHeight - windowHeight) / 2,
windowWidth,
windowHeight,
HWND_DESKTOP,
NULL,
This,
NULL
);
if (!hWnd)
{
MessageBox(NULL, L"MAIN HWND ERROR!!!",
L"Error", MB_OK);
exit(1);
}
childOpenGLWindowHWND = CreateOpenGLChildWindow(
WinName,
0,
0,
600,
800,
NULL,
NULL,
hWnd,
This,
WndProc
);
if (!childOpenGLWindowHWND)
{
MessageBox(NULL, L"Child window init error", L"Error", MB_OK);
return 0;
}
hDC = GetDC(childOpenGLWindowHWND);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
SendMessage(hWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), NULL);
ShowWindow(hWnd, mode);
ShowWindow(childOpenGLWindowHWND, mode);
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(childOpenGLWindowHWND, hDC);
wglDeleteContext(hRC);
DestroyWindow(childOpenGLWindowHWND);
return msg.wParam;
//return NULL;
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 1, 0);
glVertex3f(-50, 0, 0);
glVertex3f(50, 0, 0);
glVertex3f(0, 50, 0);
glEnd();
glFlush();
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
init();
case WM_PAINT:
display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return NULL;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return NULL;
case WM_CHAR:
switch (wParam) {
case 27:
PostQuitMessage(0);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return NULL;
}
The WM_CREATE message is triggered by CreateWindow. This is before the OpenGL Context is created by wglCreateContext and made current by wglMakeCurrent.
Thus not any OpenGL instruction takes effect at this point.
I recommend to implement the WM_SHOWWINDOW message instead:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
switch (message)
{
// [...]
case WM_SHOWWINDOW:
BeginPaint(hWnd, &ps);
init();
EndPaint(hWnd, &ps);
return NULL;
case WM_PAINT:
BeginPaint(hWnd, &ps);
display();
EndPaint(hWnd, &ps);
return NULL;
// [...]
}
return NULL;
}

How to correctly draw simple non-client area (4 px red border)?

I'm trying to draw a custom painted non-client area, instead of default theme border (Windows 10).
I handled WM_NCCALCSIZE to resize the non-client area to 4 pixels on each side and then handled WM_NCPAINT to draw the red border.
My custom painting succeeds when application is first displayed, but fails to redraw either when application is resized, or when minimized and restored, despite the fact that both WM_NCCALCSIZE and WM_NCPAINT are called during resizing or when window is restored.
#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "window";
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindowEx(
WS_EX_COMPOSITED,
"window",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
SetWindowTheme(hWnd, L"", L"");
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCCALCSIZE:
{
RECT rect;
GetWindowRect(hWnd, &rect);
LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
ncParams->rgrc[0].top = rect.top + 4;
ncParams->rgrc[0].left = rect.left + 4;
ncParams->rgrc[0].bottom = rect.bottom - 4;
ncParams->rgrc[0].right = rect.right - 4;
return 0;
}
case WM_NCPAINT:
{
RECT rect;
GetWindowRect(hWnd, &rect);
HDC dc = GetDCEx(hWnd, (HRGN) wParam, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
HGDIOBJ old = SelectObject(dc, pen);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Rectangle(dc, 0, 0, width, height);
SelectObject(dc, old);
DeleteObject(pen);
ReleaseDC(hWnd, dc);
return 0;
}
case WM_NCACTIVATE:
RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
return 0;
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The wParam of WM_NCPAINT message sometimes returns 1 instead of a handle to a region (HRGN). In that case HRGN must be created using CreateRectRgn function.
#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "window";
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindowEx(
NULL,
"window",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
SetWindowTheme(hWnd, L"", L"");
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCCALCSIZE:
{
LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
ncParams->rgrc[0].top += 4;
ncParams->rgrc[0].left += 4;
ncParams->rgrc[0].bottom -= 4;
ncParams->rgrc[0].right -= 4;
return 0;
}
case WM_NCPAINT:
{
RECT rect;
GetWindowRect(hWnd, &rect);
HRGN region = NULL;
if (wParam == NULLREGION) {
region = CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);
} else {
HRGN copy = CreateRectRgn(0, 0, 0, 0);
if (CombineRgn(copy, (HRGN) wParam, NULL, RGN_COPY)) {
region = copy;
} else {
DeleteObject(copy);
}
}
HDC dc = GetDCEx(hWnd, region, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
if (!dc && region) {
DeleteObject(region);
}
HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
HGDIOBJ old = SelectObject(dc, pen);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Rectangle(dc, 0, 0, width, height);
SelectObject(dc, old);
ReleaseDC(hWnd, dc);
DeleteObject(pen);
return 0;
}
case WM_NCACTIVATE:
RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
return 0;
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

Transparency in GDI DCs

I have the "simple" goal of drawing a bitmap with some transparency around it on the screen. That bit wasn't so hard:
#include <windows.h>
#include "BBKG.h"
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int wH = 156;
static int wW = 166;
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC mem0, mem1;
HBITMAP hbmMask;
BITMAP bm;
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
mem0 = CreateCompatibleDC(0);
mem1 = CreateCompatibleDC(0);
SelectObject(mem0, hbmColour);
SelectObject(mem1, hbmMask);
SetBkColor(mem0, crTransparent);
BitBlt(mem1, 0, 0, bm.bmWidth, bm.bmHeight, mem0, 0, 0, SRCCOPY);
BitBlt(mem0, 0, 0, bm.bmWidth, bm.bmHeight, mem1, 0, 0, SRCINVERT);
DeleteDC(mem0);
DeleteDC(mem1);
return hbmMask;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
MSG msg;
HWND hwnd;
WNDCLASSW wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP| WS_EX_TRANSPARENT,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
//Workaround for focusables stealing my Esc key
if (msg.message == WM_KEYDOWN){
if (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
static int px;
static int py;
static HBITMAP bhbm;
static RECT nRect = { 0, 0, wW, wH };
switch (msg)
{
case WM_CREATE:
{
HWND bb = CreateWindowW(L"STATIC", L"",
WS_VISIBLE | WS_CHILD ,
0, 0, wW, wH,
hwnd, (HMENU)11, hInst, NULL);
//SetTimer(hwnd, 1, 80, NULL);
return 0;
}
case WM_PAINT: {
//Vars
RECT wRect;
if (GetUpdateRect(hwnd, &wRect, FALSE) == 0) {
return 0; //Nothing to paint
}
PAINTSTRUCT gps;
PAINTSTRUCT ps;
BeginPaint(hwnd, &gps);
HWND bb = GetDlgItem(hwnd, 11);
HDC bbhdc = BeginPaint(bb, &ps);
HDC mdc = CreateCompatibleDC(bbhdc);
//Load Image
BITMAP pBM;
HBITMAP pHBM = (HBITMAP)LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HBITMAP pMBM = CreateBitmapMask((HBITMAP)pHBM, 0x00000000);
GetObject(pHBM, sizeof(pBM), &pBM);
//Paint
HBITMAP oldBM = (HBITMAP)SelectObject(mdc, pMBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCAND);
SelectObject(mdc, pHBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCPAINT);
//Cleanup
SelectObject(mdc, oldBM);
DeleteObject(pHBM);
DeleteObject(pMBM);
DeleteDC(mdc);
EndPaint(bb, &ps);
EndPaint(hwnd, &gps);
return 1;
}
case WM_ERASEBKGND: {
return 0;
}
case WM_DESTROY:
{
DeleteObject(bhbm);
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
SetCapture(hwnd);
px = LOWORD(lParam);
py = HIWORD(lParam);
return 1;
case WM_LBUTTONUP:
{
ReleaseCapture();
return 1;
}
case WM_MOUSEMOVE:
{
if (GetCapture() == hwnd)
{
RECT rcWindow;
GetWindowRect(hwnd, &rcWindow);
SetWindowPos(hwnd, NULL, rcWindow.left + LOWORD(lParam) - px, rcWindow.top + HIWORD(lParam) - py, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Use any generic bmp with a black border will do, I used this:
Now the question is, how can I make it so that when I move the window (click/drag) the background updates? I was hoping for something like putting the bitmap into a transparent window so that it's overlayed on top of things but it seems to just grab the pixels of what ever is behind it.
I'm attempting to do this without GDI+ or other libraries, if possible.
CreateWindow() does not accept extended window styles, such as WS_EX_TRANSPARENT (which is why it has EX in its name). You have to use CreateWindowEx() instead:
hwnd = CreateWindowExW(WS_EX_TRANSPARENT,
wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
A better option is to create a layered window (see also this) by using the WS_EX_LAYERED extended style). Then you can use the UpdateLayeredWindow() function to provide the window with the bitmap and the transparent color (you can also specify alpha as well). Let the window manage all of the hard work of drawing the bitmap transparently for you.
Your WndProc() can also respond to the WM_NCHITTEST message to tell the OS that all clicks on the window should be treated as if the user were clicking on the window's titlebar. Let the window handle the mouse tracking and auto-positioning for you.
Try something more like this:
#include <windows.h>
HINSTANCE hInst;
static int wH = 156;
static int wW = 166;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
WNDCLASSW wc = {0};
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
HWND hwnd = CreateWindowEx(WS_EX_LAYERED,
wc.lpszClassName, L"",
WS_POPUP, 100, 100, wW, wH, NULL, NULL,
hInst, NULL);
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HDC hdcScreen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(hdcScreen);
HBITMAP oldBM = (HBITMAP) SelectObject(hdcBmp, hBmp);
POINT pt = {0};
UpdateLayeredWindow(hwnd,
hdcScreen,
NULL, NULL,
hdcBmp, &pt,
RGB(0, 0, 0), // black
NULL, ULW_COLORKEY
);
SelectObject(hdcBmp, oldBM);
DeleteDC(hdcBmp);
ReleaseDC(0, hdcScreen);
DeleteObject(hBmp);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
//Workaround for focusables stealing my Esc key
if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_NCHITTEST:
{
return HTCAPTION;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}

How to change an image in a window MFC application

Let me start off by saying that I am very new to MFC applications, so please bear with me...
I would like to create a window with an image (lets say a green light). When necessary, I want the application to change the image (to a yellow or red light). I have code that will create the window and display the image (green light), but it will not update the image (when i == 15 or i == 50)
#include "resource.h"
using namespace std;
const char g_szClassName[] = "myWindowClass";
HBITMAP Light = NULL;
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
{
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, Light);
GetObject(Light, sizeof(bm), &bm);
BitBlt(hdc, 20, 180, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
case WM_CREATE:
{
Light = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP1));
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
{
DeleteObject(Light);
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
int eventCode;
int i = 0;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = (LPCSTR)g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, (LPCSTR)"Window Registration Failed!", (LPCSTR)"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
(LPCSTR)g_szClassName,
(LPCSTR)"Test Picture",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 420, 280,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, (LPCSTR)"Window Creation Failed!", (LPCSTR)"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
if (i == 15)
{
Light = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP3));
UpdateWindow(hwnd);
}
else if (i == 50)
{
Light = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP2));
UpdateWindow(hwnd);
}
i++;
}
return Msg.wParam;
}
I thought that I could do
Light = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP3));
UpdateWindow(hwnd);
to update the window picture, but I was wrong :( ...what could I use to change the image?
You need to call InvalidateRect to cause a repaint. UpdateWindow doesn't do anything if the window's invalid region is empty.