SetBkMode(hdc, TRANSPARENT) doesn't work - c

When I use SetBkMode(hdc, TRANSPARENT); in the code below, I got the following effect when I resize the main window (and hence when the child receives the WM_PAINT message):
The problem is : When I resize the main window, The old area of "Find:" shoule be erased, I guess. But it just remains there.
If I don't use SetBkMode(hdc, TRANSPARENT);, I don't have this problem. It looks like:
, i.e it has white background. Furthermore, if I use SetBkMode(hdc, TRANSPARENT);, it looks like the same as above, before I resize the main window. So I don't think SetBkMode(hdc, TRANSPARENT); works here.
the hwnd is a static child with style SS_BITMAP.
Do you know why this issue occurs?
switch (message) {
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, gDefaultGuiFont);
SetBkMode(hdc, TRANSPARENT);
RECT rc;
GetClientRect(hwnd, &rc);
DrawText(hdc, _TR("Find:"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
.............
}

Try to use "fixed" rectangles. For example
RECT rc;
GetClientRect(hwnd, &rc);
rc.left += ...; rc.top += ...; // shift up-left point
DrawText(hdc, _TR("Find:"), -1, &rc, DT_SINGLELINE | DT_LEFT | DT_TOP);
The idea is you draw text in wrong position (once) and in right position (twice) while backgound updated only once. Can't say more on part of code.

The problem is that windows is not updating the control (in time) that's behind your static control, you are now responsible for it's contents. So you want to use the background provided by the parent. Well just ask the parent to draw it for you in the child window:
RECT rc;
GetClientRectRelative(m_hWnd, GetParent(m_hWnd), &rc);
SetWindowOrgEx(m_mdc, rc.left, rc.top, NULL);
SendMessage(GetParent(m_hWnd), WM_PAINT, (WPARAM)(HDC)m_mdc);
SetWindowOrgEx(m_mdc, 0, 0, NULL);
In which
bool GetClientRectRelative(HWND hWnd, HWND hWndRelativeTo, RECT *pRect)
{
RECT rcWnd, rcRelativeTo;
if (!GetClientRect(hWnd, &rcWnd) ||
!ClientToScreen(hWnd, (POINT*)&rcWnd) ||
!ClientToScreen(hWnd, (POINT*)&rcWnd + 1) ||
!GetClientRect(hWndRelativeTo, &rcRelativeTo) ||
!ClientToScreen(hWndRelativeTo, (POINT*)&rcRelativeTo) ||
!ClientToScreen(hWndRelativeTo, (POINT*)&rcRelativeTo + 1))
return false;
pRect->top = rcWnd.top - rcRelativeTo.top;
pRect->left = rcWnd.left - rcRelativeTo.left;
pRect->right = rcWnd.right - rcRelativeTo.left;
pRect->bottom = rcWnd.bottom - rcRelativeTo.top;
return true;
}
Now draw anything you like, I suggest you'd use the TRANSPARENT background mode.
Please create all your child windows with the styles WS_CLIPCHILDREN and WS_CLIPSIBLINGS, then these problems will become apparent immediately and you avoid flicker.

Related

Making some parts of the window transparent in WinAPI

I want to make only certain(rectangular) parts of the window transparent.
I have set the window as WS_EX_LAYERED and the WM_PAINT function is as follows:
case WM_PAINT:;
RECT rect;
GetWindowRect(hwnd, &rect);
HDC hdc = GetDC(hwnd);
HDC hdc1 = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc, hBitmap);
SIZE size = {rect.right - rect.left, rect.bottom - rect.top};
BLENDFUNCTION blendFunc = {AC_SRC_OVER, 0, 0, AC_SRC_ALPHA};
UpdateLayeredWindow(hwnd, hdc1, NULL, &size, hdc, NULL, RGB(0, 0, 0), &blendFunc, ULW_ALPHA);
DeleteObject(hBitmap);
DeleteDC(hdc);
ReleaseDC(hwnd, hdc1);
break;
I tried creating a child window, but that doesn't seem to work if I change it's opacity through SetLayeredWindowAttributes, possibly because it mimics the parent windows opacity. I am currently trying to use UpdateLayeredWindow to make certain parts transparent, but I can't even make the entire part transparent using UpdateLayeredWindow.
What is the general way to make certain parts of the window transparent?
I suggest you could refer to the Doc:Using Layered Windows
To have a dialog box come up as a translucent window, first create the
dialog as usual. Then, on WM_INITDIALOG, set the layered bit of the
window's extended style and call SetLayeredWindowAttributes with the
desired alpha value.
The third parameter of SetLayeredWindowAttributes is a value that ranges from 0 to 255, with 0 making the window completely transparent and 255 making it completely opaque.
In order to use layered child windows, the application has to declare itself Windows 8-aware in the manifest. Refer to the thread: How to use WS_EX_LAYERED on child controls

Strange coordinates in `WM_MOUSEMOVE`

MSDN describes that lParam of WM_MOUSEMOVE is 2 shorts due to it needing to be compatible with virtual coordinates because it acts as a redirected event if capture is set, which is clear. However, negative coordinates are still received under normal circumstances when moving the mouse slightly outside of the window, to be exact a bonus 5 pixels in all directions except up (which is where the caption is, and it'd go to WM_NCMOUSEMOVE instead).
I initially suspected this was to do with the drop shadow technically being part of the window (like with the output of AdjustWindowRectEx's rectangle including it) since you can receive events in some of the shadow, but the values of that don't match up, and clamping the values to the client area's size doesn't feel intended. Where do the bonus 5 pixels (on my system, Windows 10 Education 2004) come from, especially considering that shouldn't even be a part of the client area to my knowledge, and is there a clean/intended way to dodge unwanted values?
I've seen some discussion about the area you can grab to resize the window potentially being related, but my window isn't resizable (doesn't have a thick frame).
Edit: After tinkering, it seems this is related to the window style. Here's a reproducible sample (hopefully on other machines, too):
#include <Windows.h>
#include <stdio.h>
// Remove `^ WS_THICKFRAME` and the bug vanishes!
#define WINDOW_STYLE ((WS_OVERLAPPEDWINDOW | WS_VISIBLE) ^ WS_THICKFRAME)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_MOUSEMOVE) {
POINTS mouse = MAKEPOINTS(lParam);
printf("WM_MOUSEMOVE # x=%hd, y=%hd\n", mouse.x, mouse.y);
} else if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProcW(hWnd, message, wParam, lParam);
}
int main(void) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"SO_Example";
wcex.hIconSm = NULL;
RECT windowRect = { 0, 0, 600, 600 };
AdjustWindowRectEx(&windowRect, WINDOW_STYLE, FALSE, 0);
ATOM windowClass = RegisterClassExW(&wcex);
HWND hWnd = CreateWindowExW(
0,
(LPCWSTR)windowClass,
L"hello stackoverflow!",
WINDOW_STYLE,
CW_USEDEFAULT, CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
Here's that sample in action: https://i.imgur.com/LfCe9od.mp4
It appears 5 is the border size (1) and the border padding (4) added up (aka the area the user can use to resize the window), so it's safe to discard values outside of the client rectangle, even if it's a little strange that Win32 is reporting events in that area.

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

WM_PAINT doesn't show up after painting

So i'm painting a bitmap, heres my code:
hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, g_hBitmap);
GetObject(g_hBitmap, sizeof(bm), &bm);
BitBlt(hdc, 196 - (bm.bmWidth/2), 90, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
Sometimes, when I paint it with this code, the bitmap is not displayed. Although if i minimize/unminimize the window, the bitmap is displayed. I'm pretty sure there's no problems with my code so is there something else I should be doing?
EDIT:
Turns out it's not just bitmaps, if I draw text with TextOut sometimes it's not displayed until its minimized/unminimized. I don't think minimizing/unminimizing sends another WM_PAINT message, so I don't think that when i do that it's causing it to be repainted correctly.
Oh and the rest of the controls get painted normally, just the stuff inside WM_PAINT isn't painted.
UPDATEHere's the code thats causing the problems, it works 98% of the time too.
// This is a global variable
bool GlobalVar = false;
// This is a different thread started with _beginthread
void ThreadExample()
{
GlobalVar = true;
InvalidateRect(hMainWnd, NULL, TRUE);
_endthread();
}
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
if (GlobalVar == true)
{
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, 0x0000ff);
OrigFont = SelectObject(hdc, g_hLargeFont);
GetTextExtentPoint32(hdc, ErrorMsg, lstrlen(ErrorMsg), &sz);
TextOut(hdc, 196 - (sz.cx/2), 100, ErrorMsg, lstrlen(ErrorMsg));
SelectObject(hdc, OrigFont);
}
EndPaint(hWnd, &ps);
break;
EDIT2:Another important detail could be, in my actual application, this code is inside a if statement that checks a global variable, and paints if its true. And this variable is set from a different thread, and after the variable is set I call InvalidateRect(hMainWnd, NULL, TRUE);
Updated my example code to represent this.
What is immediately not good with this code snippet (you actually should rather have posted more details) is that you delete temporary DC with your global bitmap handle still selected into it. You need to do SelectObject once again to unselect your bitmap.
You normally do it like this:
HGDIOBJ hPreviousBitmap = SelectObject(hdcMem, g_hBitmap);
// ...
SelectObject(hdcMem, hPreviousBitmap);
Also, error checking never hurts. Possibly one of the API calls fail and it's important which one exactly as it sheds more light on the issue.

Creating and using fonts / avoiding memory leaks in windows GDI

I'm trying to get to the bottom of a memory leak in an application written in C and running on Windows CE 6.0. I suspect that the issue MAY be related to the handling of the paint event of the window. In pseudo code it looks like this.
LRESULT CALLBACK HandlePaint(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint (hWnd, &ps);
HFONT logfont;
FONTINFO font1, font2;
memset(&logfont, 0, sizeof(LOGFONT));
//set font options for font1.
font1 = CreateFontIndirect(&logfont);
memset(&logfont, 0, sizeof(LOGFONT));
//set font options for font2.
font2 = CreateFontIndirect(&logfont);
for(int i = 0; i <= SOME_NUMBER; i++)
{
DrawStuff(hdc, font1);
DrawStuff(hdc, font2);
}
EndPaint (hWnd, &ps);
}
INT DrawStuff(HDC hdc, HFONT font)
{
HPEN pen = CreatePen(PS_SOLID, borderWidth, bordercolor);
HBRUSH brush = CreateSolidBrush(backcolor);
SelectObject (hdc, pen);
SelectObject (hdc, brush);
SelectObject(hdc, font);
SetTextColor (hdc, forecolor);
SetBkColor (hdc, backcolor);
DrawText (hdc, pChar, wcslen(pChar), prect, DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
DeleteObject(font);
DeleteObject(brush);
DeleteObject(pen);
}
I've noticed in the examples I've seen for windows graphics that there seems to be a pattern for most grapics objects of:
HBRUSH brush = CreateBrush();
SelectObject(hdc, brush);
// use brush
DeleteObject(brush);
However, as you can see in the example above with the fonts, each font is being created once, and then Selected/Deleted multiple times. I'm not sure what the implications are of doing that. Would there be a reason to suspect a memory leak here?
Thanks!
I agree with #pmg's comment that the creator of the Form should be the destroyer of the font, not the DrawStuff callee.
Also bear in mind that SelectObject returns the original item in the DC and you should always return that object when you're done, e.g.:
HPEN newPen = CreatePen(...);
HPEN oldPen = SelectObject(hdc, newPen);
// do stuff
// clean up
SelectObject(hdc, oldPen); // <-- note this line
DeleteObject(newPen);

Resources