Winapi, change the color of groupbox frame - user-interface

I want to make dark semitransparent GUI using WinAPI. I made a custom button, but I cant make my own groupbox. In WM_DRAWITEM case of message handler, the callback drawing my groupbox is not even called. This callback contains MessageBox call that is not even once appeared.
How to draw groupbox with colored frame and colored text? Also I want to make my groupboxes lying under the all other controls. How to do that?
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Main logic goes here...
ShowMainWindow();
}
void RegisterWndClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = Proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(0x14, 0x14, 0x14));
wc.lpszMenuName = NULL;
wc.lpszClassName = szName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT("Cannot register class"), TEXT("Error"), MB_OK);
}
}
// Draw custom button
void DrawButton(DRAWITEMSTRUCT *Item, HWND hwnd)
{
SelectObject(Item->hDC, CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial Black"));
FillRect(Item->hDC, &Item->rcItem, CreateSolidBrush(RGB(0x14, 0x14, 0x14)));
SelectObject(Item->hDC, CreateSolidBrush(0));
if (Item->itemState & ODS_SELECTED)
{
SetTextColor(Item->hDC, 0);
SelectObject(Item->hDC, CreateSolidBrush(0xFF00));
SelectObject(Item->hDC, CreatePen(PS_SOLID, 2, 0xFF00));
}
else
{
SetTextColor(Item->hDC, 0x00FF00);
SelectObject(Item->hDC, CreatePen(PS_SOLID, 2, 0x00FF00));
}
SetBkMode(Item->hDC, TRANSPARENT);
RoundRect(Item->hDC, Item->rcItem.left, Item->rcItem.top, Item->rcItem.right, Item->rcItem.bottom, 20, 20);
int len = GetWindowTextLength(Item->hwndItem);
LPSTR lpBuff[255];
GetWindowTextA(Item->hwndItem, lpBuff, len + 1);
DrawTextA(Item->hDC, lpBuff, len, &Item->rcItem, DT_CENTER);
}
// Trying to draw customgroupbox
void DrawKeyGroup(DRAWITEMSTRUCT *Item, HWND hwnd)
{
SelectObject(Item->hDC, CreateSolidBrush(RGB(0, 255, 0)));
SelectObject(Item->hDC, CreateSolidBrush(0));
SetTextColor(Item->hDC, 0x00FF00);
SelectObject(Item->hDC, CreatePen(PS_SOLID, 2, 0x00FF00));
MessageBox(NULL, "", "", MB_OK);
//SetBkMode(Item->hDC, TRANSPARENT);
RoundRect(Item->hDC, Item->rcItem.left, Item->rcItem.top, Item->rcItem.right, Item->rcItem.bottom, 200, 200);
}
//Main windows proc
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
RECT rect;
switch (uMsg)
{
case WM_CREATE: // Init interface
// Text field
CreateWindow(TEXT("EDIT"),
NULL,
WS_VISIBLE | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL,
10, 280, 350, 20,
hWnd,
(HMENU)ID_KEY_FIELD,
NULL,
NULL);
// Button to enter the text
CreateWindowEx(0, TEXT("BUTTON"),
TEXT("Unlock"),
WS_VISIBLE | WS_CHILD | BS_OWNERDRAW,
370, 280, 150, 20,
hWnd,
(HMENU)ID_KEY_ENTER,
GetModuleHandle(NULL),
NULL);
// Groupbox
CreateWindowEx(0, TEXT("BUTTON"),
TEXT("Key"),
WS_VISIBLE | WS_CHILD | BS_GROUPBOX | BS_OWNERDRAW,
5, 262, 523, 47,
hWnd,
(HMENU)ID_KEY_GROUP,
GetModuleHandle(NULL),
NULL);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
EndPaint(hWnd, &ps);
break;
// Color text field
case WM_CTLCOLOREDIT:
{
HDC hdc = (HDC)wParam;
HWND hwnd = (HWND)lParam;
if (GetDlgCtrlID(hwnd) == ID_KEY_FIELD)
{
SetTextColor(hdc, 0xFF00);
SetBkColor(hdc, RGB(0x29, 0x29, 0x29));
return (LRESULT)CreateSolidBrush(RGB(0x29, 0x29, 0x29));
}
break;
}
// Color statie elements (group box caption)
/*case WM_CTLCOLORSTATIC:
SetTextColor((HDC)wParam, RGB(0x00, 0x8C, 0xBA));
SetBkMode((HDC)wParam, TRANSPARENT);
SetBkColor((HDC)wParam, RGB(0x14, 0x14, 0x14));
return (LRESULT)CreateSolidBrush(GetBkColor((HDC)wParam));*/
// Draw custom controls
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
switch (dis->CtlID)
{
case ID_KEY_ENTER:
DrawButton(dis, GetDlgItem(hWnd, dis->CtlID));
break;
case ID_KEY_GROUP:
DrawKeyGroup(dis, GetDlgItem(hWnd, dis->CtlID));
break;
}
}
case WM_SIZE:
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void ShowMainWindow(void)
{
HWND hMainWnd;
MSG msg;
RegisterWndClass(WndProc, TEXT("Info"), COLOR_WINDOW);
hMainWnd = CreateWindowEx(WS_EX_LAYERED, TEXT("Info"), TEXT("Info"),
WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, 0, 550, 360, (HWND)NULL, (HMENU)NULL,
GetModuleHandle(NULL), NULL);
SetWindowLong(hMainWnd, GWL_EXSTYLE, GetWindowLong(hMainWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hMainWnd, 0, (255 * 70) / 100, LWA_ALPHA);
if (!hMainWnd)
{
MessageBox(NULL, TEXT("Can\'t create main window."), TEXT("Error"), MB_OK);
return;
}
ShowWindow(hMainWnd, SW_SHOW);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

Related

How do I make a static control with custom width and color?

To change the static's control color and width, I did process the WM_NCPAINT message, but it's not working properly:
the text isn't properly displayed. Should I draw it in this message? When I call SetWindowText(), the new text is properly set, but I still have the issue #2.
after a call to SetWindowText(), the text is set, but the border differs, it's smaller than the one I've draw in the WM_NCPAINT message.
Here's how I'm handling WM_NCPAINT:
case WM_NCPAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rt = {0};
GetClientRect(hwnd, &rt);
HPEN pen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
HPEN holdPen = SelectObject(dc, pen);
DrawBorder(dc, &rt);
SelectObject(dc, holdPen);
EndPaint(hwnd, &ps);
DeleteObject(pen);
pen = NULL;
return 0;
}
When the application starts, the static control border looks like the one I've draw at WM_NCPAINT:
But once I call SetWindowText(), for example, from a button click, it looks like this:
What am I missing?
Here's full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void EnableVisualStyles2(void);
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void SetDefaultFont(HWND hwnd);
void DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2);
void DrawBorder(HDC hdc, RECT *rect);
HFONT getSystemDefaultFont(void);
WNDPROC oldButtonProc;
HINSTANCE ghInstance;
HFONT hDefaultFont;
HWND btn, btn2;
enum
{
BTN_ID = 10,
BTN2_ID,
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow)
{
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
EnableVisualStyles2();
if(!RegisterClass(&wc)) {
return -1;
}
int width = 540;
int height = 460;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int cx = (screenWidth - width) / 2;
int cy = (screenHeight - height) / 2;
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
cx, cy, width, height, NULL, NULL,
hInstance, NULL);
ghInstance = hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DeleteObject(hDefaultFont);
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
btn =
CreateWindow(L"static", L"+",
WS_VISIBLE | WS_CHILD | WS_TABSTOP |
SS_NOTIFY | SS_CENTER | SS_CENTERIMAGE,
15, 25, 50, 50,
hwnd, (HMENU) BTN_ID, NULL, NULL);
SetDefaultFont(btn);
oldButtonProc = (WNDPROC) SetWindowLongPtr(btn,
GWLP_WNDPROC,
(LONG_PTR) ButtonProc);
btn2 =
CreateWindow(L"Button", L"Click me!",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
15, 100, 70, 25,
hwnd, (HMENU) BTN2_ID,
NULL, NULL);
SetDefaultFont(btn2);
}
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case BTN_ID:
{
//InvalidateRect(btn, NULL, TRUE);
if(IsWindowVisible(btn2))
{
SetWindowText(btn, L"+");
ShowWindow(btn2, SW_HIDE);
}
else
{
SetWindowText(btn, L"-");
ShowWindow(btn2, SW_SHOW);
}
}
break;
case BTN2_ID:
{
InvalidateRect(btn, NULL, TRUE);
//SetWindowText(btn, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0");
}
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_NCPAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rt = {0};
GetClientRect(hwnd, &rt);
HPEN pen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
HPEN holdPen = SelectObject(dc, pen);
DrawBorder(dc, &rt);
SelectObject(dc, holdPen);
EndPaint(hwnd, &ps);
DeleteObject(pen);
pen = NULL;
return 0;
}
}
return CallWindowProc(oldButtonProc, hwnd, msg, wParam, lParam);
}
void EnableVisualStyles2(void)
{
TCHAR dir[MAX_PATH] = {0};
GetSystemDirectory(dir, sizeof(dir) / sizeof(*dir));
ACTCTX actCtx = {0};
actCtx.cbSize = sizeof(ACTCTX);
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID |
ACTCTX_FLAG_SET_PROCESS_DEFAULT |
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
actCtx.lpSource = L"shell32.dll";
actCtx.lpAssemblyDirectory = dir;
actCtx.lpResourceName = (LPCTSTR) 124;
ULONG_PTR cookie = FALSE;
HANDLE h = CreateActCtx(&actCtx);
assert(h != INVALID_HANDLE_VALUE);
assert(ActivateActCtx(h, &cookie));
}
HFONT getSystemDefaultFont(void)
{
if(!hDefaultFont)
{
// get system default font
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
hDefaultFont = CreateFontIndirect(&ncm.lfMessageFont);
}
return hDefaultFont;
}
void SetDefaultFont(HWND hwnd)
{
SendMessage(hwnd, WM_SETFONT, (WPARAM)getSystemDefaultFont(), FALSE);
}
void DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
{
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
}
void DrawBorder(HDC hdc, RECT *rect)
{
DrawLine(hdc, rect->left, rect->top, rect->left, rect->bottom);
DrawLine(hdc, rect->left, rect->top, rect->right, rect->top);
DrawLine(hdc, rect->right, rect->top, rect->right, rect->bottom);
DrawLine(hdc, rect->left, rect->bottom, rect->right, rect->bottom);
}
Your static control does not have a transparent background, so when you call the SetWindowText function, the static control partially covers the external lines you draw.
You can add the following code in WndProc:
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC)wParam;
SetBkMode(hdcStatic, TRANSPARENT);
return (LRESULT)GetStockObject(NULL_BRUSH);
}
But this code tells the static control to draw the text without a background color and not to repaint the background. So the new text is drawn on top of the old text instead of on a fresh background.You can refer to this thread to try to solve it.
Of course, you can also extend the boundary of the straight line when drawing a straight line (you do not need to process the WM_CTLCOLORSTATIC message), as shown in the following code:
void DrawBorder(HDC hdc, RECT* rect)
{
DrawLine(hdc, rect->left - 3, rect->top - 3, rect->left - 3, rect->bottom + 3) ;
DrawLine(hdc, rect->left - 3, rect->top - 3, rect->right + 3, rect->top - 3);
DrawLine(hdc, rect->right + 3, rect->top - 3, rect->right + 3, rect->bottom + 3);
DrawLine(hdc, rect->left - 3, rect->bottom + 3, rect->right + 3, rect->bottom + 3);
}
This is the modified effect:

Creating a filled rectangle with WINAPI

I have created a window using the Win32 API, with a painted Text and a Button. If the button is pressed, it will be deleted, and the text should be overlayed by a filled rectangle.
The button disappearing works fine, but the position of the rectangle is the position of the button. No matter if I change the values of:
RECT rect = { 50, 120, 450, 15 };
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
static HWND hWndButton;
static HWND hWndEditBox;
switch (uiMessage)
{
case WM_CREATE:
hWndButton = CreateWindow(TEXT("BUTTON"), TEXT("Los geht's !"),
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
240, 50, 120, 40, hWnd,
reinterpret_cast<HMENU>(BTN_MAIN_BUTTON_ID),
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hWnd, GWL_HINSTANCE)),
NULL);
return (0);
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0, 0, 0));
SetTextAlign(hdc, TA_CENTER);
TextOut(hdc, 300, 10, TEXT("Bitte legen Sie ihr Mitarbeiterprofil an."), lstrlen(TEXT("Bitte legen Sie ihr Mitarbeiterprofil an.")));
EndPaint(hWnd, &ps);
return (0); }
case WM_COMMAND:
if (wParam == BTN_MAIN_BUTTON_ID)
{
DestroyWindow(hWndButton);
PAINTSTRUCT ps2;
HDC hdc = BeginPaint(hWnd, &ps2);
RECT rect = { 50, 120, 450, 15 };
HBRUSH brush = CreateSolidBrush(RGB(50, 151, 151));
GetClientRect(hWnd, &rect);
FillRect(hdc, &rect, brush);
DeleteObject(brush);
EndPaint(hWnd, &ps2);
int i;
char string[20];
for (i = 0; i< profilEinheiten; i++)
{
hWndEditBox = CreateWindow(
TEXT("EDIT"), TEXT("Edit Boxes"),
WS_VISIBLE | WS_CHILD,
100, 40+(20*i), 100, 15, hWnd,
(HMENU)IDC_EDITBOX_TEXT,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
}
return (0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return (0);
}
return (DefWindowProc(hWnd, uiMessage, wParam, lParam));
}

How do I set the GroupBox's Caption color?

I'm handling WM_CTLCOLORSTATIC like this:
case WM_CTLCOLORSTATIC:
{
HWND fromHwnd = (HWND) lParam;
if(fromHwnd == hGroupBox)
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(255, 255, 0));
SetBkMode(hdcStatic, TRANSPARENT);
return (LRESULT) bckBrush;
}
}
break;
It uses the returned brush as background color but the SetTextColor() has no effect at all. What am I missing?
Here's all my code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hGroupBox;
HBRUSH bckBrush;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow)
{
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
if(!RegisterClass(&wc)) {
assert(!"register class error");
}
bckBrush = CreateSolidBrush(RGB(0, 128, 0));
int width = 500;
int height = 350;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int cx = (screenWidth - width) / 2;
int cy = (screenHeight - height) / 2;
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
cx, cy, width, height, NULL, NULL,
hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
hGroupBox =
CreateWindowW(L"button", L"Pick a city",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_GROUPBOX,
50, 75, 200, 150, hwnd,
(HMENU) 20, NULL, NULL);
break;
case WM_CTLCOLORSTATIC:
{
HWND fromHwnd = (HWND) lParam;
if(fromHwnd == hGroupBox)
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(255, 255, 0));
SetBkMode(hdcStatic, TRANSPARENT);
return (LRESULT) bckBrush;
}
}
break;
case WM_DESTROY:
DeleteObject(bckBrush);
bckBrush = NULL;
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

How to paint over white line between menu bar and client area of window?

I tried to color customize menu items (pure WinAPI). But there is a line in the menu bar which does not draw with MenuInfo.hbrBack color. If the mouse cursor hover above items a part of this line is redrawn. But if I resize the window the line will return. And in the area of menu bar where no items the line drawn constantly. How can I draw over this annoying line?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
struct
{
COLORREF text = RGB(200, 200, 250);
COLORREF clientBorder = RGB(120, 0, 0);
COLORREF clientBackground = RGB(100, 100, 100);
COLORREF itemBorder = RGB(0, 0, 255);
COLORREF itemBackground = RGB(0, 120, 0);
COLORREF pink = RGB(255, 0, 255);
} colorTheme;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWindow";
wc.cbWndExtra = NULL;
wc.cbClsExtra = NULL;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(colorTheme.clientBackground);
wc.hInstance = hInst;
RegisterClassEx(&wc);
HWND hMainWnd = CreateWindow(
"MainWindow",
"MainWindow",
WS_OVERLAPPEDWINDOW,
100, 100, 450, 120,
(HWND)NULL, NULL, HINSTANCE(hInst), NULL);
HMENU hMenu = CreateMenu();
HMENU hMenuSub1 = CreatePopupMenu();
HMENU hMenuSub2 = CreatePopupMenu();
HMENU hMenuSub3 = CreatePopupMenu();
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub1, "SubMenu1");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item01");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item02");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item03");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item04");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item05");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub2, "SubMenu2");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub3, "SubMenu3");
MENUINFO menuInfo;
menuInfo.cbSize = sizeof(menuInfo);
menuInfo.fMask = MIM_BACKGROUND;
menuInfo.hbrBack = CreateSolidBrush(colorTheme.pink);
SetMenuInfo(hMenu, &menuInfo);
SetMenu(hMainWnd, hMenu);
ShowWindow(hMainWnd, nCmdShow);
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
HFONT hApplicationFont;
LOGFONT applicationFont;
applicationFont.lfHeight = 16;
applicationFont.lfWidth = 6;
applicationFont.lfEscapement = 0;
applicationFont.lfOrientation = 0;
applicationFont.lfWeight = FW_NORMAL;
applicationFont.lfItalic = FALSE;
applicationFont.lfUnderline = FALSE;
applicationFont.lfStrikeOut = FALSE;
applicationFont.lfCharSet = DEFAULT_CHARSET;
applicationFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
applicationFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
applicationFont.lfQuality = ANTIALIASED_QUALITY;
applicationFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy_s(applicationFont.lfFaceName, "Arial");
hApplicationFont = CreateFontIndirectA(&applicationFont);
SelectObject(hDC, hApplicationFont);
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.clientBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.clientBackground);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
Rectangle(hDC, 0, 0, clientRect.right, clientRect.bottom);
EndPaint(hWnd, &ps);
break;
}
case WM_MEASUREITEM:
{
LPMEASUREITEMSTRUCT itemStruct = (LPMEASUREITEMSTRUCT)lParam;
const char* str = (const char*)(itemStruct->itemData);
SIZE strSize;
HDC hDC = GetDC(hWnd);
GetTextExtentPoint32(hDC, str, lstrlen(str), &strSize);
itemStruct->itemWidth = strSize.cx;
itemStruct->itemHeight = 30;
ReleaseDC(hWnd, hDC);
return TRUE;
break;
}
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT itemStruct = (LPDRAWITEMSTRUCT)lParam;
HDC hDC = itemStruct->hDC;
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.itemBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.itemBackground);
SetTextColor(hDC, colorTheme.text);
SetBkMode(hDC, TRANSPARENT);
Rectangle(hDC, itemStruct->rcItem.left,
itemStruct->rcItem.top,
itemStruct->rcItem.right,
itemStruct->rcItem.bottom + 1);
DrawText(hDC, (const char*)(itemStruct->itemData), -1, &(itemStruct->rcItem), DT_SINGLELINE | DT_CENTER | DT_VCENTER);
break;
}
case WM_DESTROY:
{
PostQuitMessage(NULL);
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return NULL;
}
It seems to be part of the non-client area of the window. If that's the case then to paint there you need to handle WM_NCPAINT.
It is a single pixel line above the window's client area, so for example if I add the following code to your program I can paint it in red.
// ... in the WNDPROC
case WM_NCPAINT:
{
auto result = DefWindowProc(hWnd, WM_NCPAINT, wParam, lParam);
HDC hdc = GetWindowDC(hWnd);
RECT r = GetNonclientMenuBorderRect(hWnd);
HBRUSH red = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &r, red);
DeleteObject(red);
ReleaseDC(hWnd, hdc);
return result;
}
// ... elsewhere
RECT MapRectFromClientToWndCoords(HWND hwnd, const RECT& r)
{
RECT wnd_coords = r;
// map to screen
MapWindowPoints(hwnd, NULL, reinterpret_cast<POINT*>(&wnd_coords), 2);
RECT scr_coords;
GetWindowRect(hwnd, &scr_coords);
// map to window coords by substracting the window coord origin in
// screen coords.
OffsetRect(&wnd_coords, -scr_coords.left, -scr_coords.top);
return wnd_coords;
}
RECT GetNonclientMenuBorderRect(HWND hwnd)
{
RECT r;
GetClientRect(hwnd, &r);
r = MapRectFromClientToWndCoords(hwnd, r);
int y = r.top - 1;
return {
r.left,
y,
r.right,
y+1
};
}
Now an issue with the above code is that it is over-painting the rectangle after the default non-client painting is done. In theory this could flicker; in practice I don't notice a flicker. If it did flicker, however, a safer way to do this would be to modify the WPARAM you pass to DefWindowProc(hWnd, WM_NCPAINT, ... ) such that it is the handle to a region that is the region passed to WM_NCPAINT minus the rectangle you want to paint. This doesnt seem necessary to me, for whatever reason.
If you are using themes / visual styles, which pretty much everything is nowadays, you can't override a lot of the menu styling without using a workaround like https://github.com/adzm/win32-custom-menubar-aero-theme which also uses the same approach to get rid of the white line. Note that you will need to handle this in WM_NCPAINT and WM_NCACTIVATE.

How to draw image on a window?

I have created a window with createwindow() api using VS2005 in C++ on Windows Vista
My requirement is to draw an image (of any format) on that window. I am not using any MFC in this application.
not exactly sure what is your problem: draw a bitmap on the form, or you would like know how to work with various image formats, or both. Anyways below is an example of how you could load a bitmap and draw it on the form:
HBITMAP hBitmap = NULL;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
<...>
case WM_CREATE:
hBitmap = (HBITMAP)LoadImage(hInst, L"c:\\test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
break;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
hdc = BeginPaint(hWnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
oldBitmap = SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bitmap), &bitmap);
BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DeleteObject(hBitmap);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LoadImage loads an icon, cursor, animated cursor, or bitmap. Details here
For working with various images formats you can use Windows Imaging Component (see IWICBitmapDecoder) or code from here Loading JPEG and GIF pictures or 3rd party tools like FreeImage or LeadTools
hope this helps, regards
void LoadScreen(HWND hWnd) {
RECT rect;
HDC hdc = GetDC(hWnd);
HBRUSH brush = CreatePatternBrush((HBITMAP)LoadImage(NULL, L"file.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
GetWindowRect(hWnd, &rect);
FillRect(hdc, &rect, brush);
DeleteObject(brush);
ReleaseDC(hWnd, hdc);
}
#include <windows.h>
#include <string.h>
HBITMAP hBitmap, hOldBitmap;
HDC hdc, hdcMem;
BITMAP bm;
HINSTANCE hI;
PAINTSTRUCT ps;
RECT rect;
RECT rc;
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
hBitmap = (HBITMAP)LoadImage(hI, "1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
GetObject(hBitmap, sizeof(BITMAP), &bm);
hdc = GetDC(hWnd);
hdcMem = CreateCompatibleDC(hdc);
hOldBitmap = SelectBitmap(hdcMem, hBitmap);
ReleaseDC(hWnd, hdc);
return 0;
case WM_LBUTTONDOWN:
//for dragging not only by the title, but also by any part of the window
ReleaseCapture();
SendMessage(hWnd, 0xA1, 2, 0);
break;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
//overlay image with stretching to fit the window
GetClientRect(hWnd,&rect);
SetStretchBltMode(hdc, STRETCH_HALFTONE);
StretchBlt(hdc,0,0,rect.right,rect.bottom,
hdcMem,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
DeleteObject(hOldBitmap);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPInst, LPSTR lpCmdLine, int nCmdShow)
{
//copying a pointer to a running application instance (module)
hI=hInstance;
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProcedure;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
wc.lpszClassName = "test_class";
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClass(&wc);
HWND hWnd = CreateWindow(wc.lpszClassName, "Image Window",
//window with title (overlapping window)
WS_OVERLAPPEDWINDOW,
//window without title
//WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage (&msg, NULL, 0, 0))
{
DispatchMessage (&msg);
TranslateMessage (&msg);
}
UnregisterClass(wc.lpszClassName, hInstance);
return (int) msg.wParam;
}

Resources