Trackbar control disappearing on resize - winapi

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?

Related

WIN32 - How can I query for an image's metadata?

I want to know how can I query for metada of an image that I loaded with LoadImage. For example, how do I go about querying its demensions?
You can use GetObject with BITMAP.
I create a sample with LoadImage:
HWND background = CreateWindow("STATIC", "background", SS_BITMAP | WS_CHILD | WS_VISIBLE, 200, 200, 300, 300, hwnd, NULL, NULL, NULL);
HBITMAP hBmp = (HBITMAP)LoadImage(NULL, "test.bmp", IMAGE_BITMAP, 100, 100, LR_LOADFROMFILE);
SendMessage(background, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBmp);
This sample shows an image:
Then I get the image information through GetObject:
BITMAP bitmapInfo;
GetObject(hBmp, sizeof bitmapInfo, &bitmapInfo);
Then you can view the specific information of the image through the returned BITMAPstructure.
It works for me:

Button scaling in winapi

I am creating a simple calculator and I have a problem with scaling all elements. They have to scale with main window but I have no idea how to perform such a thing
Here is part of code:
#define ID_BUTTON0 0
#define ID_BUTTON1 1
#define ID_BUTTON2 2
#define ID_BUTTON3 3
#define ID_BUTTON4 4
...
case WM_CREATE:
{
CreateWindow(L"STATIC", L"", WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_TEXTFIELD), 0, NULL);
CreateWindow(L"BUTTON", L"CE",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTONCE), 0, NULL);
CreateWindow(L"BUTTON", L"C",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTONC), 0, NULL);
CreateWindow(L"BUTTON", L"/",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTONDIV), 0, NULL);
CreateWindow(L"BUTTON", L"X",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTONMUL), 0, NULL);
CreateWindow(L"BUTTON", L"7",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTON7), 0, NULL);
CreateWindow(L"BUTTON", L"8",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTON8), 0, NULL);
CreateWindow(L"BUTTON", L"9",WS_VISIBLE | WS_CHILD,0, 0, 0, 0,hWnd, HMENU(ID_BUTTON9), 0, NULL);
...
resize_window(hWnd);
}
break;
case WM_SIZE:
{
resize_window(hWnd);
UpdateWindow(hWnd);
return 0;
}
void resize_window(HWND hWnd) {
int buttonxsize = WINDOWSIZEW / 4;
int buttonysize = WINDOWSIZEH / 6;
int posx = WINDOWSIZEW / 4;
int posy = WINDOWSIZEH / 6;
RECT rc;
GetClientRect(hWnd, &rc);
SetWindowPos(GetDlgItem(hWnd, ID_TEXTFIELD), 0, 0, 0, WINDOWSIZEW, buttonysize, SWP_NOZORDER);
SetWindowPos(GetDlgItem(hWnd, ID_BUTTONC), 0, 0, posy, buttonxsize, buttonysize, SWP_NOZORDER);
SetWindowPos(GetDlgItem(hWnd, ID_BUTTONCE), 0, posx, posy, buttonxsize, buttonysize, SWP_NOZORDER);
SetWindowPos(GetDlgItem(hWnd, ID_BUTTONDIV), 0, 2 * posx, posy, buttonxsize, buttonysize, SWP_NOZORDER);
SetWindowPos(GetDlgItem(hWnd, ID_BUTTONMUL), 0, 3 * posx, posy, buttonxsize, buttonysize, SWP_NOZORDER);
...
}
and so on...
After compiling I obtain such a thing :
How do I make it work?
HWND hwndTextField = CreateWindow(L"Static", L"",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP,
0, 0, WINDOWSIZEW, WINDOWSIZEH / 6,
hWnd, NULL, GetModuleHandle(NULL), NULL);
hwndTextField is declared on stack, it will be forgotten as soon as it goes out of scope, you won't be able find the control later (not easily anyway). hwndTextField should be declared as static or global. Better yet, assign an ID for each control using HMENU. This ID can be used to find the control. Also, when you click the button it sends WM_COMMAND message with that ID.
The style for this control should be WS_VISIBLE | WS_CHILD | WS_TABSTOP. However WS_TABSTOP is ignored unless this is created in a dialog.
And resize the controls in a separate function, so that it can be done from both WM_CREATE and WM_SIZE
For example
#define ID_TEXT 100
...
case WM_CREATE:
{
CreateWindow(L"STATIC", L"", WS_VISIBLE | WS_CHILD,
0, 0, 0, 0, hWnd, HMENU(ID_TEXT), 0, NULL);
...
resize_window(hWnd);
}
case WM_SIZE:
{
resize_window(hWnd);
return 0;
}
void resize_window(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
//update ****
WINDOWSIZEW = rc.right;
WINDOWSIZEH = rc.bottom;
...
SetWindowPos(GetDlgItem(hWnd, ID_TEXT), 0, x, y, w, h, SWP_NOZORDER);
...
}

OpenGL (Windows) not drawing to screen

I'm trying to start experimenting with OpenGL and Windows GUI stuff. I'm running Windows 7 on my dev box and running Visual Studio 2013. When I run the below code, I don't receive any errors and all the of window information seems correct but nothing is drawn to the screen from OpenGL.
For a first program I was looking to put an overlay on top of Notepad, then draw a line/plus sign somewhere in the screen.
First I create a window:
myWindow = CreateWindowEx(
WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED,
WINDOW_NAME,
WINDOW_NAME,
WS_POPUP | WS_VISIBLE | WS_MAXIMIZE,
g_NotepadCoords.left,
g_NotepadCoords.top,
g_width,
g_height,
NULL, NULL, g_me, NULL);
The globals and coordinates for the window are calculated using FindWindowA and GetClientRect. Both of these function calls succeed and return reasonable values. After CreateWindowEx, I use the following function calls to set properties and check support, etc:
SetLayeredWindowAttributes(myWindow, RGB(0, 0, 0), 255, LWA_ALPHA | LWA_COLORKEY);
if (S_OK != DwmIsCompositionEnabled(&bIsEnabled)){...}
Right now HandleWindowMessage has WM_CREATE, WM_CLOSE, WM_DESTROY and WM_PAINT enabled. SetupOpenGL is called in WM_CREATE, and the drawing happens in WM_PAINT.
WM_CREATE and WM_PAINT:
case WM_CREATE:
{
printf("WM_CREATE\n");
g_Hdc = GetDC(hWnd);
SetupGL();
return 0;
}
break;
case WM_PAINT:
{
GLenum err;
const GLubyte *errString;
RGB colorToDraw = { 0 };
drawPlus(g_.middleX, g_.middleY, 12, 12, 4, &colorToDraw);
err = glGetError();
errString = gluErrorString(err);
printf("ERROR DRAWING? %s\n", errString);
}
break;
As far as I can tell I set up OpenGL correctly; no errors are printed:
void SetupOpenGL()
{
__try{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | // Format Must Support Window, OPENGL, Composition
PFD_SUPPORT_OPENGL |
PFD_SUPPORT_COMPOSITION |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, // RGBA
32,
0, 0, 0, 0, 0, 0,
8, // alpha
0, 0, 0, 0, 0, 0, 0,
8, // stencil Buffer
0,
PFD_MAIN_PLANE, // Main Drawing Layer
0, 0, 0, 0
};
PixelFormat = ChoosePixelFormat(g_Hdc, &pfd);
if (0 == PixelFormat)
{
printf("ChoosePixelFormat failed %d\n", GetLastError());
__leave;
}
if (!SetPixelFormat(g_Hdc, PixelFormat, &pfd))
{
printf("SetPixelFormat failed %d\n", GetLastError());
__leave;
}
if (!DescribePixelFormat(g_Hdc, PixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
{
printf("DescribePixelFormat failed %d\n", GetLastError());
__leave;
}
g_MainHGLRC = wglCreateContext(g_Hdc);
if (!g_MainHGLRC)
{
printf("wglCreateContext failed %d\n", GetLastError());
__leave;
}
if (!wglMakeCurrent(g_Hdc, g_MainHGLRC))
{
printf("wglMakeCurrent failed %d\n", GetLastError());
__leave;
}
glMatrixMode(GL_PROJECTION);
glClearColor(0.f, 0.f, 0.f, 0.f);
}
__finally{}
}
And then drawPlus is super simple:
void drawPlus(float x, float y, float width, float height, float lineWidth, RGB* RGB)
{
printf("drawPlus(%f,%f,%f,%f,%f,[%d,%d,%d])", x, y, width, height, lineWidth, RGB->red, RGB->green, RGB->blue);
glLineWidth(lineWidth);
glBegin(GL_LINES);
glColor4f(RGB->red, RGB->green, RGB->blue, 1);
glVertex2f(x, y + height/2);
glVertex2f(x, y - height/2);
glVertex2f(x + width/2, y);
glVertex2f(x - width/2, y);
glEnd();
}
When I run it, I don't receive any errors, I can see the executable creating another window and I've confirmed that drawPlus is getting called correctly and with good parameters. However, there's nothing actually drawn to the screen so I'm not sure what I'm missing. I just ran a quick check on the coordinates and for the Notepad windows elements I got:
top: 86
bottom: 916
left: 170
right: 609
And then for where to draw the plus I got
x = 219
y = 412
Which both seem reasonable so it's not my windows being mismatched or etc.

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

Win32: Unable to set Bitmap to Button

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!

Resources