Is it possible to make a Static control transparent? - c++

I am trying to implement a static control which refreshes(change text) in response to some event, which occurs once every second. Since I didn't want to paint the entire client area every second and so I decided to use a static control, now the problem is the parent window is skinned, meaning it has custom bitmap as its background, and the static control doesn't fit in, so am looking for ways to make the static control's background transparent.
This is what I have now:
hHandle = CreateWindowEx( WS_EX_TRANSPARENT, "STATIC", "", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 60, 212, 477, 20, hwnd, 0, hInstance, 0) ;
case WM_CTLCOLORSTATIC:
{
HDC hdC = (HDC)wParam;
SetTextColor( hdC, RGB(31,122,179) );
SetBkMode( hdC, TRANSPARENT );
return 0;//(HRESULT)GetStockObject(NULL_BRUSH);
}
break;
I tried returning NULL brush to paint the background, hoping it would make its background transparent but it didn't what more it forced the static control to not repaint properly, what I mean is that the text gets painted on top of the old text so its all messy.
Is subclassing is the only option ?

The only way I've found to do this reliably is to sub-class the static control and paint the background manually.
WS_EX_TRANSPARENT does not make a control truly transparent to underlying pixels (although it may appear like that) and WS_EX_COMPOSITED can not be used for child windows.
Instead, sub-class the static, and catch the WM_ERASEBKGND message. You can then paint the appropriate portion of the underlying bitmap.

The way to get a WC_STATIC control to show only text in the color you choose (over an image or other surface) is the return, as I understand this. This is what worked for me from this link.
case WM_CTLCOLORSTATIC:
SetTextColor((HDC)wParam, RGB(255, 0, 0));
SetBkMode((HDC)wParam, TRANSPARENT);
// the correct return needs HOLLOW_BRUSH
return (LRESULT)GetStockObject(HOLLOW_BRUSH);
break;

You don't mention the window styles on the static control, specifically the extended styles WS_EX_TRANSPARENT seems intended to solve the problem you describe with the repaint. Also as I understand it WS_EX_COMPOSITED might be useful in your context.
You also might consider whether the background window should have WS_CLIPCHILDREN set as that might affect the repaint.

Related

Transparency in C++

I'm trying to create transparency in my application.
For instance the window of my app is square and i want to make it round, by hiding parts of the window.
My code looks something like this:
HDC hdcMask = nullptr;
HBITMAP hBMP = (HBITMAP)LoadImageW(nullptr, L"C:\\mask.bmp", IMAGE_BITMAP, 150, 160, LR_LOADFROMFILE);
SelectObject(hdcMask, hBMP);
HWND hWnd = GetActiveWindow();
HDC hdcWindow = GetDC(hWnd);
TransparentBlt(hdcWindow, 0, 0, 150, 160, hdcWindow, 0, 0, 150, 160, RGB(0,0,0));
where mask.bmp is a bitmap where white is what i want to be transparent and black is what I want to be visible.
After applying this code, nothings happens. What am I doing wrong ? Is there another method to obtain the desired result?
Note: I need this code to work on Windows XP OS or later.
There are several ways of making a window transparent and/or translucent.
SetWindowRgn will make parts of a window transparent.
SetLayeredWindowAttributes can make parts of a window transparent, and can also apply translucency to the whole of the rest of the window.
UpdateLayeredWindow can give individual windows different amounts of translucency.
Why don't you use a different format than bmp where you can also include alpha data?
You can use a PixelFormat32bppPARGB Bitmap, use Bitmap::LockBits an Bitmap::Unlockbits
to keep the file's format and avoid having the alpha setting overwritten.

Applying Windows theme to custom control with WS_BORDER style

I have a custom control created using CreateWindowEx with the WS_BORDER style. Everything works fine apart from the border appearing in a different colour to other controls in the dialog box. The border in my control is black, the other controls have a blue border. I've tried calling EnableThemeDialogTexture(_dialogHandle, ETDT_ENABLE) after creating the control, as well as the logic from http://www.patchou.com/projects/richedit/ but to no avail. I'm using C++ and the Winapi. ie. no MFC, no .Net. Any guidance very much appreciated.
EDIT: Here's the logic that worked for me:
HDC hdc = GetWindowDC(hwnd);
HTHEME themeHandle = OpenThemeData(hwnd, L"Edit");
if(themeHandle)
{
int cxBorder = GetSystemMetrics(SM_CXBORDER);
int cyBorder = GetSystemMetrics(SM_CYBORDER);
RECT rc;
GetClientRect(hwnd, &rc);
OffsetRect(&rc, cxBorder, cyBorder);
ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
InflateRect(&rc, cxBorder, cyBorder);
DrawThemeBackground(themeHandle, hdc, 0, 0, &rc, NULL);
CloseThemeData(themeHandle);
}
ReleaseDC(hwnd, hdc);
You have to draw the border yourself, using the theme from another control (for example, the listview or treeview control). For a custom child control, drawing the border is quite easy - simply handle the WM_NCPAINT message. The part ID and state ID when you draw the border should both be 0.

Troubles with ::MoveWindow - leaving blank space

I am quite desperate to resolve this very annoying issue :(
I am trying to display a child window on parent window. Some time the window need to be resized. But for some reason, when I using MoveWindow function it leaves blank space on the top of the parent window. I would like to present a picture here but I can not post a picture.
Here is the code example:
HWND hwnd // Comes from external function. Was defined as WS_CHILD previously
HWND hwndParent = ::GetParent(hwnd);
RECT parentRect = {0,0,0,0};
RECT childRect = {0,0,0,0};
::GetClientRect(hwndParent, &parentRect); // Suppose it returns {0,0,600,300}
BOOL ok = ::MoveWindow(hwnd, 0, 0, 600, 300, true);
::GetClientRect(hwnd, &childRect); // Will return {0,0,584,297}
WHY ?????
What am I doing wrong? Did I forgot some flags with window initialization?!
Rather than use GetClientRect, use GetWindowRect and MapWindowPoints(NULL,hwndParent,&parentRect,2) to adjust it to the parent window coordinates. GetWindowRect will include the non-client area that MoveWindow requires.
Edit: If you want a window that doesn't have a non-client area so the window rect and the client rect are the same size, you need to trim the window styles that you apply to the window. Avoid the WS_BORDER, WS_CAPTION, WS_DLGFRAME, WS_OVERLAPPED, WS_SIZEBOX, and WS_THICKFRAME styles.
MoveWindow updates window position, while GetClientRect gets a client-area part of the window, which does not have to be the same. If your window has non-client area, then everything is fine and works as expected.
If you are still under impression that child window does not fully cover parent's client area, then the spacing belongs to the child control/window, and you need to look for ways to remove it there (control flags, parameters etc).
MoveWindow operates on window coordinates -- including non-client area (borders, title bar, etc).
GetClientRect gets the area of the client portion of the window, ignoring borders, title bar, etc.
This is where the mismatch is. If you want to MoveWindow to a desired client size, you need to just AdjustWindowRect to try and predict what to pass into MoveWindow. Note that it's not always possible, and not always accurate. For example minimum / maximum sizes of windows, menus (which can wrap to multiple lines), etc.
The problem was WS_POPUP flag to the parent window.
Very strange. As far as I know it was not suppose to have such an effect.
Thanks for everyone!

Static Control Background Color with C++

I am creating a basic GUI with the Windows API and I have run into an issue. It starts with a main window that opens with a custom background color I set (RGB(230,230,230)). It then displays text in the upper left corner with the static control.
settingstext = CreateWindow("STATIC",
"SETTINGS",
SS_LEFT | WS_CHILD,
12,
20,
100,
20,
hwnd,
NULL,
proginstance,
NULL);
ShowWindow(settingstext, 1);
This works, but when the text is displayed I need a way to change the background of it to match the main window or else it just looks like it doesn't blend in.
My question is, how do I do this? I currently use the method below and it works, but I wanted to know, is there a way to permanently set the background color somehow, right after the CreateWindow function for the static control without changing system colors, and just have it apply to that one control and not anything that sends the WM_CTLCOLORSTATIC message. I have experimented around with using the GetDC function and SetBkColor function outside of the message loop but nothing works.
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230));
return (INT_PTR)CreateSolidBrush(RGB(230,230,230));
}
I want to do this because...
I don't want to fill up my message loop with functions that need to be called every time the window repaints.
Have the changes apply to only this static control.
I would be very thankful for any help that could be provided, at least pointing me in the right direction, thanks.
For static text controls there's no permanent way to set the text color or their background. Even if you want to apply the changes to a single static control; you would still have to handle WM_CTLCOLORSTATIC notification message in parent dlgproc just when the control is about to be drawn.
This is due to the DefWindowProc overwriting your changes to the device context each time it handles WM_CTLCOLORSTATIC as stated in the MSDN:
By default, the DefWindowProc function selects the default system colors for the static control.
static HBRUSH hBrush = CreateSolidBrush(RGB(230,230,230));
case WM_CTLCOLORSTATIC:
{
if (settingstext == (HWND)lParam)
//OR if the handle is unavailable to you, get ctrl ID
DWORD CtrlID = GetDlgCtrlID((HWND)lParam); //Window Control ID
if (CtrlID == IDC_STATIC1) //If desired control
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230));
return (INT_PTR)hBrush;
}
}
If you're looking to make the control's background transparent over a parent dialog you could use SetBkMode(hdcStatic, TRANSPARENT).
I think there is a permanent way to do it.
Just after you create the label,use GetDC() function to get the Device Context.
Then use:
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230)); // Code Copied from the above answer by cpx.
And it should do .
Have you considered subclassing the static window and doing owner draw?

How to draw in the nonclient area?

I'd like to be able to do some drawing to the right of the menu bar, in the nonclient area of a window.
Is this possible, using C++ / MFC?
Charlie hit on the answer with WM_NCPAINT. If you're using MFC, the code would look something like this:
// in the message map
ON_WM_NCPAINT()
// ...
void CMainFrame::OnNcPaint()
{
// still want the menu to be drawn, so trigger default handler first
Default();
// get menu bar bounds
MENUBARINFO menuInfo = {sizeof(MENUBARINFO)};
if ( GetMenuBarInfo(OBJID_MENU, 0, &menuInfo) )
{
CRect windowBounds;
GetWindowRect(&windowBounds);
CRect menuBounds(menuInfo.rcBar);
menuBounds.OffsetRect(-windowBounds.TopLeft());
// horrible, horrible icon-drawing code. Don't use this. Seriously.
CWindowDC dc(this);
HICON appIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
::DrawIconEx(dc, menuBounds.right-18, menuBounds.top+2, appIcon, 0,0, 0, NULL, DI_NORMAL);
::DestroyIcon(appIcon);
}
}
In order to draw in the non-client area, you need to get the "window" DC (rather than "client" DC), and draw in the "window" DC.
You should try handling WM_NCPAINT. This is similar to a normal WM_PAINT message, but deals with the entire window, rather than just the client area. The MSDN documents on WM_NCPAINT provide the following sample code:
case WM_NCPAINT:
{
HDC hdc;
hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
// Paint into this DC
ReleaseDC(hwnd, hdc);
}
This code is intended to be used in the message loop of your applicaton, which is canonically organized using a large 'switch' statement.
As noted in the MFC example from Shog, make sure to call the default version, which in this example would mean a call to DefWindowProc.
If you just want something in the menu bar, maybe it is easier/cleaner to add it as a right-aligned menu item. This way it'll also work with different Windows themes, etc.