Draw a line next to word in WinAPI - c

How do I draw a line like this that is right next to a word like "Counts", in WinAPI with C?

Using Dialog Resources
Create a static text control with no text that is 1 or 2 pixels in height, turn on the border (WS_BORDER), and set its style to Static Edge (WS_EX_STATICEDGE). Then create a static text control with the word "Counts" in it on top of that. Then use CreateDialog() or DialogBox() to show the dialog box.
IDD_DIALOG1 DIALOGEX 0, 0, 172, 63
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "",IDC_STATIC,6,12,156,1,WS_BORDER,WS_EX_STATICEDGE
LTEXT "Counts ",IDC_STATIC,6,8,26,8
END
Note: This is verbatim what Visual Studio generated using the dialog designer.
Creating Static Controls Using CreateWindow() (as suggested by Jonathan Potter)
LRESULT OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct )
{
// Get default gui font
NONCLIENTMETRICS metrics;
metrics.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &metrics, NULL);
HFONT hFont = CreateFontIndirect(&metrics.lfMessageFont);
// Create the line
CreateWindowEx(WS_EX_STATICEDGE, _T("STATIC"), NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,
10, 17, 280, 1, hWnd, NULL, lpCreateStruct->hInstance, NULL);
// Create the Counts label
HWND hwndCounts = CreateWindow(_T("STATIC"), _T("Counts "), WS_CHILD|WS_VISIBLE,
10, 10, 50, 26, hWnd, NULL, lpCreateStruct->hInstance, NULL);
// Apply the default gui font
SendMessage(hwndCounts, WM_SETFONT, (WPARAM)hFont, TRUE);
// Cleanup the font object
DeleteObject(hFont);
}
Drawing manually on the WM_PAINT event
void OnPaint( HWND hWnd )
{
// Get the default font
NONCLIENTMETRICS metrics;
metrics.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &metrics, NULL);
HFONT hFont = CreateFontIndirect(&metrics.lfMessageFont);
// Setup HDC
RECT rect;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Select the default font
SelectObject(hdc, hFont);
// Draw the line using the button shadow
SelectObject(hdc, GetStockObject(DC_PEN));
SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
MoveToEx(hdc, 10, 17, NULL);
LineTo(hdc, 280, 17);
// Draw the word Counts overtop of the line
SetRect(&rect, 10, 10, 280, 22);
SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
DrawText(hdc, TEXT("Counts "), -1, &rect, DT_NOCLIP);
// Cleanup the font object
DeleteObject(hFont);
// Quit painting
EndPaint(hWnd, &ps);
}
Note: Something I did not account for in this example is the height of the default font. You will want to adjust the code for that.
Here is a screenshot of the output of this method.
In your example, it looked like a single one pixel line, so that's what I drew, but if you'd like to make the line look more like a 'Fixed 3D' or 'lowered bevel line' (which is what the group box tends to draw for it's border line), then you can draw another line below it with the button highlight color.
SetDCPenColor(hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
MoveToEx(hdc, 10, 18, NULL);
LineTo(hdc, 280, 18);
As pointed out by Ben Voigt, it might be better to do this with DrawEdge though.
RECT line;
SetRect(&line, 10, 17, 280,17);
DrawEdge(hdc, &line, EDGE_ETCHED, BF_TOP );
Creating a Group Box Control (suggested by Hans Passant)
Hans Passant's suggestion of doing this with a Group Box did work when I tested it. It still drew a rectangle, and when you enabled visual styles it was very difficult to see. Nevertheless, this should get you started if you want to give it a go.
HWND hwndGroup = CreateWindow(_T("Button"), _T("Counts "),
WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 10, 10, 280, 2, hWnd, NULL,
lpCreateStruct->hInstance, NULL);
SendMessage(hwndGroup, WM_SETFONT, (WPARAM)hFont, TRUE);
Additional Note
Something else I would like to suggest is that you use can use Spy++ which comes with Visual Studio to analyze the window you are looking at. This will tell you at the very least if it's a child control, or whether they are painting it manually. If it's a child control you will also be able to see the rectangle and styles that are applied to it, as well lots of additional information.

Related

What means by 'clear the update region' when painting the client area on Windows?

Here the Painting the Window mentioned that:
After you finish painting the client area, you clear the update region, which tells the operating system that it does not need to send another WM_PAINT message until something changes.
One may write these code:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
HBRUSH brush = CreateSolidBrush(RGB(127, 127, 127));
FillRect(hdc, &rect, brush);
const wchar_t * lstr = L"here is information";
TextOut(hdc,
5, 5,
lstr, _tcslen(lstr));
DrawText(hdc, TEXT("Singleline in center~"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
MoveToEx(hdc, 50, 100, NULL);
LineTo(hdc, 44, 10);
LineTo(hdc, 78, 40);
Rectangle(hdc, 16, 36, 72, 70);
Rectangle(hdc, 34, 50, 54, 70);
DeleteObject(brush);
EndPaint(hWnd, &ps);
}
What means by clear here? We do not want what we have drawn to be cleared.
WM_PAINT messages are generated on demand (see Paint messages will come in as fast as you let them). For each window, the system maintains an update region. Clients can mark parts or all of a window's client area as "invalid", calling InvalidateRect or InvalidateRgn. Either one adds to the update area, but doesn't immediately trigger a WM_PAINT message.
When the system determines that it's time to send a WM_PAINT message, it is the client's responsibility to empty the update region when it's done painting so that no additional WM_PAINT messages are generated until the update region is non-empty again.
The call to BeginPaint does that for you, so you don't have to worry about this so long as you use standard WM_PAINT handling. If you do have more specific requirements (e.g. when using a Direct2D render target) you would have to manually clear the update region with a call to ValidateRect or ValidateRgn.

WINAPI: Window client area not lining up with the window

The client area of my window is not aligning properly with the outer non-client area. Take a look, this is when I am not touching the window, just after I launch it:
It's not aligned, although all dimensions across all functions are ok, 500x500 to be exact.
Now, when I resize it, it kind of aligns correctly:
HWND Window = CreateWindowEx(
0,
WindowClass.lpszClassName,
"Handmade Hero",
WS_OVERLAPPED | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
0,
0,
Instance,
0);
RECT rect;
GetClientRect(Window, &rect);
Win32AllocateMemoryBuffer(&GlobalBackBuffer, rect.right, rect.bottom);
Message handling:
case WM_PAINT:
{
PAINTSTRUCT Paint;
HDC DeviceContext = BeginPaint(Window, &Paint);
Dimension rect;
GetClientRect(Window, &rect);
Win32DisplayBufferInWindow(DeviceContext, &GlobalBackBuffer, Dimension.right, Dimension.bottom);
EndPaint(Window, &Paint);
} break;
The stretchdbits:
StretchDIBits(DeviceContext,
0, 0, WindowWidth, WindowHeight,
0, 0, Buffer->BitmapWidth, Buffer->BitmapHeight,
Buffer->BitmapMemory,
&Buffer->BitmapInfo,
DIB_RGB_COLORS, SRCCOPY);
I've been tracking the dimensions across all the program in VS debugger, but couldn't track the problem. Maybe your intuition would spot something?

plain winapi c GUI changing static text background

I am using plain winapi c to create a GUI, I am new to this language and am struggling with something many might think is basic. Could someone please explain to me how I change the background colour for static text because currently is transparent. The code I am using for the text is:
hwndStatic = CreateWindow(TEXT("static"), TEXT(""),
WS_CHILD | WS_VISIBLE,
10, 70, 90, 25, hwnd, NULL, g_hinst, NULL);
In general, you change the drawing of static text controls by handling WM_GETCTLCOLORSTATIC.
In that handler, you can change things about the DC, like the text color, background mode, background color, even the font that's selected.
You can also return a handle to a GDI brush (using a cast to get it by the type system). The control will erase itself first with the brush and then draw the text.
The callback will happen for all static controls that are children of the current window, so you first test to see if it's the child you care about.
For example:
case WM_CTLCOLORSTATIC:
HWND hwnd = (HWND) lParam;
if (hwnd == hwndStatic) {
HDC hdc = (HDC) wParam;
::SetTextColor(hdc, RGB(0xFF, 0, 0)); // set the text to red
::SetBkMode(hdc, OPAQUE);
::SetBkColor(hdc, RGB(0x00, 0xFF, 0x00)); // set background to green
HBRUSH hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
return (INT_PTR) hbrBackground;
}
return 0;
This shows several things you can do. You probably don't want to do all of them, but it can be educational to see them all in action.
Note that if you create a brush to return, you have to keep track of it and delete it later. I've avoided this issue by relying on GetSysColorBrush. The system owns those, so you shouldn't delete them. You can also use GetStockObject for system GDI objects that you don't have to manage. But if you need a custom color, you'll have to use CreateSolidBrush and then clean it up.
Respond to the WM_CTLCOLORSTATIC message in your program and have it return a brush object of the proper color.
I've slightly modified the example from the link:
case WM_CTLCOLORSTATIC:
{
HWND hWnd = (HWND) lParam;
if (hWnd == hMyStatic)
{
HBRUSH hbrBkgnd = CreateSolidBrush(RGB(0,0,0));
return (INT_PTR)hbrBkgnd;
}
return 0;
}

W32 textbox automatic newline

So I have a textbox and I want to make it go to a new line when it reaches the border. Also, I am new to this w32 API. Here is my code:
hEdit=CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD|WS_VISIBLE|
WS_BORDER, 10,
10, 160, 420, hwnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL),
NULL);
HGDIOBJ hfDefault=GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault,
MAKELPARAM(FALSE,0));
SendMessage(hEdit, WM_SETTEXT, NULL,
(LPARAM)"Insert text here...");
You need to include the ES_MULTILINE style when creating the control.
http://msdn.microsoft.com/en-us/library/windows/desktop/bb775464(v=vs.85).aspx

Color of the lines are not changing?

I want to draw a white line in my window:
case WM_PAINT:
{
hdc=GetDC(hWnd);
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
MoveToEx(hdc, 0, 0, 0);
LineTo(hdc, 100, 100);
ReleaseDC(hWnd, hdc);
}
but the color is still black. What's Wrong?
You are trying to set a brush for your line when you should be using a pen. A brush is used to fill the interior of a shape while a pen is used to draw the lines.
MSDN says this about pens:
A pen is a graphics tool that an application can use to draw lines and
curves. Drawing applications use pens to draw freehand lines, straight
lines, and curves.
And this about brushes:
A brush is a graphics tool that applications use to paint the interior
of polygons, ellipses, and paths.
Your code would need to be something more like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
hdc=BeginPaint(hWnd, &ps); // Used instead of GetDC in WM_PAINT
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
HPEN hOldPen = SelectObject(hdc, hPen);
MoveToEx(hdc, 0, 0, 0);
LineTo(hdc, 100, 100);
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
EndPaint(hWnd, &ps); // Used instead of ReleaseDC in WM_PAINT
}

Resources