Win32: Unable to set Bitmap to Button - winapi

I have this code:
case WM_CREATE:
{
HWND button = CreateWindowEx(NULL,
"BUTTON",
"Do!",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
580,
520,
100,
24,
hwnd,
(HMENU)IDC_MAIN_BUTTON,
GetModuleHandle(NULL),
NULL);
HBITMAP b = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
SendMessage(button, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)b);
I see the button normally, but not the bitmap I assigned.
What's wrong?

Don't forget to set the BS_BITMAP flag!

Related

Winapi, change the color of groupbox frame

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);
}
}

WINAPI Edit control with custom border

what is the proper way of implementing custom rounded border for EDIT control in pure WinAPI (no MFC)? I need an edit with border like this:
Should I subclass edit control and do custom painting in WM_NCPAINT or something like that?
I guess you have two options:
As you said, you could sub-class and override WM_NCPAINT, etc to provide your own non-client area
Alternatively, you could simply turn off the border styles on the edit control and make the parent window responsible for drawing the frame.
With option #1, you would need to override WM_NCCALCSIZE to make the non-client area of the edit control larger (i.e. make the client area smaller), and then WM_NCPAINT to render your custom frame. You may also need to handle WM_NCHITTEST. And of course you'd need to make the control itself physically larger to account for the extra frame thickness.
It depends on your application design and how many controls like this you wish to use, but if it were me I would go with option #2. Modifying the standard drawing behaviour of system controls, many of which have decades of accumulated kludges and compatibility fixes attached to them, is often not as easy as you might expect.
If you make sure the WS_BORDER and WS_EX_CLIENTEDGE styles aren't set on the edit control, it will have no visible border of its own. Then all you have to do is have the parent window, when processing WM_PAINT, draw the frame around it. Make sure you set the WS_CLIPCHILDREN style on the parent window so that your custom drawing doesn't overwrite the edit control.
Either path would probably work in the end though so it's up to you which way you go.
This is an implementation that works for me.
It subclass the "EDIT" class control and replaces the WM_NCPAINT handler to draw a rectangle with rounded corners for all edit boxes with the WS_BORDER or WS_EX_CLIENTEDGE style. It draws the border on the parent DC.
The diameter of the corner is now fixed (10), I guess that should depend on the font size ...
Thanks to Darren Sessions for the GDI+ example how to draw the rounded rect:
https://www.codeproject.com/Articles/27228/A-class-for-creating-round-rectangles-in-GDI-with
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
inline void GetRoundRectPath(GraphicsPath* pPath, Rect r, int dia)
{
// diameter can't exceed width or height
if (dia > r.Width) dia = r.Width;
if (dia > r.Height) dia = r.Height;
// define a corner
Rect Corner(r.X, r.Y, dia, dia);
// begin path
pPath->Reset();
// top left
pPath->AddArc(Corner, 180, 90);
// top right
Corner.X += (r.Width - dia - 1);
pPath->AddArc(Corner, 270, 90);
// bottom right
Corner.Y += (r.Height - dia - 1);
pPath->AddArc(Corner, 0, 90);
// bottom left
Corner.X -= (r.Width - dia - 1);
pPath->AddArc(Corner, 90, 90);
// end path
pPath->CloseFigure();
}
inline void GetChildRect(HWND hChild, LPRECT rc)
{
GetWindowRect(hChild,rc);
SIZE si = { rc->right - rc->left, rc->bottom - rc->top };
ScreenToClient(GetParent(hChild), (LPPOINT)rc);
rc->right = rc->left + si.cx;
rc->bottom = rc->top + si.cy;
}
inline void DrawRoundedBorder(HWND hWnd, COLORREF rgba = 0xFF0000FF, int radius = 5)
{
BYTE* c = (BYTE*)&rgba;
Pen pen(Color(c[0], c[1], c[2], c[3]));
if (pen.GetLastStatus() == GdiplusNotInitialized)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
pen.SetColor(Color(c[0], c[1], c[2], c[3]));
}
pen.SetAlignment(PenAlignmentCenter);
SolidBrush brush(Color(255, 255, 255, 255));
RECT rc = { 0 };
GetChildRect(hWnd, &rc);
// the normal EX_CLIENTEDGE is 2 pixels thick.
// up to a radius of 5, this just works out.
// for a larger radius, the rectangle must be inflated
if (radius > 5)
{
int s = radius / 2 - 2;
InflateRect(&rc, s, s);
}
GraphicsPath path;
GetRoundRectPath(&path, Rect(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top), radius * 2);
HWND hParent = GetParent(hWnd);
HDC hdc = GetDC(hParent);
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
graphics.FillPath(&brush, &path);
graphics.DrawPath(&pen, &path);
ReleaseDC(hParent, hdc);
}
static WNDPROC pfOldEditWndProc = NULL;
static LRESULT CALLBACK EditRounderBorderWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
{
DWORD style = GetWindowLong(hWnd, GWL_STYLE);
if (style & WS_BORDER)
{
// WS_EX_CLIENTEDGE style will make the border 2 pixels thick...
style = GetWindowLong(hWnd, GWL_EXSTYLE);
if (!(style & WS_EX_CLIENTEDGE))
{
style |= WS_EX_CLIENTEDGE;
SetWindowLong(hWnd, GWL_EXSTYLE, style);
}
}
// to draw on the parent DC, CLIPCHILDREN must be off
HWND hParent = GetParent(hWnd);
style = GetWindowLong(hParent, GWL_STYLE);
if (style & WS_CLIPCHILDREN)
{
style &= ~WS_CLIPCHILDREN;
SetWindowLong(hParent, GWL_STYLE, style);
}
}
break;
case WM_NCPAINT:
if (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
{
DrawRoundedBorder(hWnd);
return 0;
}
}
return CallWindowProc(pfOldEditWndProc, hWnd, uMsg, wParam, lParam);
}
class CRoundedEditBorder
{
public:
CRoundedEditBorder()
{
Subclass();
}
~CRoundedEditBorder()
{
Unsubclass();
}
private:
void Subclass()
{
HWND hEdit = CreateWindow(L"EDIT", L"", 0, 0, 0, 200, 20, NULL, NULL, GetModuleHandle(NULL), NULL);
pfOldEditWndProc = (WNDPROC)GetClassLongPtr(hEdit, GCLP_WNDPROC);
SetClassLongPtr(hEdit, GCLP_WNDPROC, (LONG_PTR)EditRounderBorderWndProc);
DestroyWindow(hEdit);
}
void Unsubclass()
{
HWND hEdit = CreateWindow(L"EDIT", L"", 0, 0, 0, 200, 20, NULL, NULL, GetModuleHandle(NULL), NULL);
SetClassLongPtr(hEdit, GCLP_WNDPROC, (LONG_PTR)pfOldEditWndProc);
DestroyWindow(hEdit);
}
};
CRoundedEditBorder g_RoundedEditBorder;
LRESULT CALLBACK ParentWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
#define WNDCLASSNAME L"RoundedEditBorderTestClass"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
WNDCLASSEXW wcex = { sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW,ParentWndProc,0,0,hInstance,NULL,NULL,CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW)),NULL,WNDCLASSNAME,NULL };
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(WNDCLASSNAME, L"Rounded Edit Border Test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
CreateWindowEx(0, L"EDIT", L"no border", WS_CHILD | WS_VISIBLE, 10, 10, 200, 24, hWnd, NULL, GetModuleHandle(NULL), NULL);
CreateWindowEx(0, L"EDIT", L"no ex style", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 50, 200, 24, hWnd, NULL, GetModuleHandle(NULL), NULL);
CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"Ex_ClientEdge", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 90, 200, 24, hWnd, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GdiplusShutdown(gdiplusToken);
return (int)msg.wParam;
}

Trackbar control disappearing on resize

I'm trying to write a WinAPI wrapper using C++11 but I've encountered a problem.
My tracksbars seem to go invisible whenever I resize the main window ( this problem does not occur with buttons/labels/progressbars/comboboxes/etc)
void Trackbar::Create(Window* parent)
{
this->handle = CreateWindowEx(
0,
TRACKBAR_CLASS
nullptr,
WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS | TBS_ENABLESELRANGE | TBS_TRANSPARENTBKGND,
170,
150,
100,
50,
parent->GetHandle(),
0,
GetModuleHandle(0),
nullptr);
if(!this->handle)
ERRORMB();
SendMessage(this->handle, TBM_SETRANGE, true, (LPARAM)MAKELONG(0, 100));
SendMessage(this->handle, TBM_SETPAGESIZE, 0, 4);
SendMessage(this->handle, TBM_SETSEL, false, (LPARAM)MAKELONG(0, 100));
SendMessage(this->handle, TBM_SETPOS, true, 0);
}
Why is this happening and how can I fix it?

SetLayeredWindowAttributes to make CEF transparent not working on Windows XP

I use SetLayeredWindowAttributes with LWA_COLORKEY to replace a color (#ff00ff) with transparency, in order to achieve transparent background in the CEF1 example.
It works well on 7/Vista.
On Windows XP, the function succeeds, but the color remains opaque.
My code to create the main window:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
HWND hWnd;
hInst = hInstance;
hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_COMPOSITED, szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE, CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
WndProc for the main window:
switch (message) {
case WM_CREATE: {
...
info.SetTransparentPainting(TRUE);
info.SetAsChild(hWnd, rect);
// Create the new child browser window
CefBrowser::CreateBrowser(info,
static_cast<CefRefPtr<CefClient> >(g_handler),
url_to_pageWithMagentaBackground, settings);
SetLayeredWindowAttributes(hWnd, RGB(0xff, 0x00, 0xff), 0xff, LWA_COLORKEY);
Html displayed:
<style type="text/css">html { background: #ff00ff; }</style>
Note these points:
It does work on XP with LWA_ALPHA (makes whole window semi-transparent),
but not with LWA_COLORKEY
When I connect to the XP through Remote Desktop, I do see the transparency as expected!
Can someone explain this please?

In c++ Windows API resize window during runtime?

How do you resize a global hwnd variable during runtime when a button is clicked?
Or just any way to resize the window during runtime.
i.e.
HWND hwnd; //global
int buttonid = 250; // an id for a button
//also global
int WINAPI wWinMain(/*blah blah blah */) {
//blah blah blah
hwnd = CreateWindowEx(
0,
L"WindowClass",
L"Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
300, 275,
NULL,
NULL,
hInstance,
NULL
);
HWND mybutton = CreateWindow(
L"BUTTON",
L"Button",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
14, 13,
250, 200,
hwnd,
(HMENU)buttonid,
hInstance,
NULL
);
//blah blah blah
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lparam) {
switch(uMsg) {
case WM_COMMAND:
if(buttonid==wParam) {
//this is where i want the code for resizing hwnd so when you click the
//button it resizes the window
}
}
}
MoveWindow or SetWindowPos (though the latter is more useful if you want to do more than just resize it).
In both cases, you can specify not only the position of the top-left corner, but also the position of the bottom-right corner, so if you leave the top-left corner as-is, and move the bottom-right, you resize the window without "moving" it.
SetWindowPos(yourhwnd,0,0,0,newWidth,newHeight,SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
Or if you want to move and resize you can use the older MoveWindow function

Resources