Win32 dialog based borderless window with bitmap background and visual styles - winapi

i'm new to StackOverflow so forgive me for any stupid mistakes I make on the post.
I'm having an issue to create a borderless window with a bitmap background using the Win32 API (no MFC) and a dialog based application. I'm using visual studio 2010.
The issue only happens if I use the windows XP Visual Styles, by linking the common controls lib with the following snippet:
#pragma comment(linker, \
"\"/manifestdependency:type='Win32' "\
"name='Microsoft.Windows.Common-Controls' "\
"version='6.0.0.0' "\
"processorArchitecture='*' "\
"publicKeyToken='6595b64144ccf1df' "\
"language='*'\"")
I have a simple dialog and inside it a PictureControl with a bitmap image that will be the background of the window. I want this image to fill the entire Window, no resize or drag and drop will be needed, so i just put it centered on screen and defined both the dialog and the Picture Control dimensions to be the same in my RC file as shown below (both with dimensions 356, 210):
//Main Dialog
IDD_DIALOG_MAIN DIALOGEX 0, 0, 356, 210
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
//Picture Control
CONTROL 103,IDC_PB_SPLASH,"Static",SS_BITMAP | SS_CENTERIMAGE | SS_REALSIZEIMAGE,0,0,356,210
DEFPUSHBUTTON "OK",IDOK,152,169,50,14
END
Now the issue is, when the I'm using the visual styles, the image doesn't fill the entire area of the dialog Window. A small white space can be seen on both sides of the dialog(if a remove the SS_CENTERIMAGE it's a white space on right side). This doesn't happen without the Visual Styles. The pictures below show the problem (The image used there is just an example, the actual background image is more complex, so just painting with a brush is not an option)
Without Visual Styles
http://postimg.org/image/hji8sa6j1/
With Visual Styles
http://postimg.org/image/b733ig3gt/
Sorry for the links, still don't have enough reputation to post images.
Any suggestions on how to make the bitmap fill the entire window with visual styles enabled?
I found this nice answer https://stackoverflow.com/a/17713810/3022281 by melak47 to making a borderless window but it's not dialog based. If nothing else works I guess I'll have to go with that solution.

Dont use a picture control. Draw directly on the dialog:
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){
HDC hdc;
RECT rt;
switch (message){
case WM_PAINT:
GetWindowRect(hDlg, &rt);
hdc = GetWindowDC(hDlg);
StretchBlt(hdc, 0, 0, rt.right, rt.bottom, hdcMemBitmap, 0, 0, width, height, SRCCOPY);
ReleaseDc(hDlg, hdc);
break;
default: //for messages that we don't deal with
return false;
}
return false;
}
width and height are for the bitmaps width and height.
valter

After a lot of fussing around, I got it to work by simply defining the image dimensions in the .RC code a little less than what the Visual Studio Dialog editor allowed me to.
By using the editor, if I pulled the Window Edges all the way down to the image width, or if I set the "Real Size Image" property to true, the editor came up with the dimensions (356,210), which was leaving the white space when Visual Styles were enabled. I just manually tuned the width down a little from 356 to 353 and the white space disappeared! So it was just Visual Studio Editor calculating the dimensions wrong for when the visual styles were enabled.

Related

Control on non-client area incorrectly painted as transparent

I customised the non-client area of window frame following the example in https://learn.microsoft.com/en-gb/windows/win32/dwm/customframe?redirectedfrom=MSDN. On this non-client area, I want to add a container dialog and a few custom buttons. I cannot get the color I want to be painted on my container dialog. Instead, it is always painted transparent in that area. The issue is demonstrated in the 2 figures below (the background there is for demonstrating the transparency).
The effect I want is:
But what has been painted is:
As you can see, the container dialog is wanted to be painted as purple but is painted as transparent and so is the customer picture button (gray cross inside circle).
The code I used to handle the WM_PAINT for the container dialog is:
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
Gdiplus::Graphics g(hDC);
Gdiplus::Color clr(255, 0, 0, 0);
clr.SetFromCOLORREF(RGB( 85, 107, 47));
Gdiplus::SolidBrush b(clr);
g.FillRectangle(&b, rc.left, rc.top, RECT_WIDTH(rc), RECT_HEIGHT(rc));
EndPaint(hwnd, &ps);
I googled but could not find topic of similar issue (may be I didn't use the right keywords). I am not familiar in using winapi to draw non-client area. Any help pointing out what I have missed or direction to solution is much appreciated.
A VS project that demonstrate the problem is linked below:
CustomCaption.zip

Text drawn with DrawText() on certain background colors (when SetBkMode is TRANSPARENT) is unacceptably blotchy: How to overcome?

In a legacy MFC application, I am attempting to draw deep red (255, 26, 26) on a 'Carbon' theme background (97, 107, 136) in a dialog. Although I am using BCGSoft's MFC library to wrap the code that draws the text, the essential code that performs the drawing of the text is the following:
// CODE THAT DRAWS THE TEXT
// Copied from BCGSoft's 'BCGPStatic.cpp' text-drawing routine
// clrText is RGB(255, 26, 26)
COLORREF clrTextOld = pDC->SetTextColor (clrText);
// background is RGB(97, 107, 136)
pDC->SetBkMode (TRANSPARENT);
pDC->DrawText (strText, rectClient, uiDTFlags);
Problem is: The text displayed is unacceptably blotchy:
BLOTCHY TEXT
If I change the color (and nothing else) to a lighter red (255, 164, 164), the text displays just fine:
GOOD TEXT
Because the font is likely of relevance, here is the code that sets the FONT:
// SETTING THE FONT
CFont * currentFont = staticCtl.GetFont();
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
currentFont->GetLogFont(&lf);
lf.lfHeight = 18;
lf.lfWeight = FW_BOLD;
// I have ALSO TRIED:
// CLEARTYPE_QUALITY
// ANTIALIASED_QUALITY
// NONANTIALIASED_QUALITY
// PROOF_QUALITY
// ...and just leaving it unset
lf.lfQuality = CLEARTYPE_NATURAL_QUALITY;
m_staticFontRed.CreateFontIndirect(&lf);
staticCtl.SetFont(&m_staticFontRed);
// The following are specific to BCGSoft
staticCtl.m_hFont = (HFONT)(m_staticFontRed.GetSafeHandle());
staticCtl.m_clrText = RGB(255, 164, 164);
I have researched this extensively and attempted as many 'workarounds' as I can think of. Specifically:
Use TextOut instead of DrawText
Set SetBkMode(OPAQUE) and simply fill the background with FillRect before drawing the text
Remove FW_BOLD from the font
As noted in the code snippet, attempt different 'qualities' in the LOGFONT (NONANTIALIASED_QUALITY, etc.)
What can I do, using MFC / WinAPI (in C++, obviously) to get my choice of color for both text and background to work without the text being unacceptably blotchy?
ADDENDUM
Based on #BarmakShemirani's comment, I zoomed in on the pixels and found that they are RED (after taking a screenshot as a bitmap). Therefore, for certain it would seem, those pixels are being generated as RED on the monitor and simply not being perceived by the eye that way).
Here is a picture of the zoomed-in version:
...The red pixels are present (blotchiness gone).
I now suspect that this is an issue with the MONITOR, and not with the way the pixels are being drawn on-screen or with the human eye.
Thanks to #BarmakShemirani!
ADDENDUM 2
I use my monitor in portrait mode, which is atypical. I just rotated the monitor to landscape and the very same screenshot in my question is significantly less blotchy. This leads me to believe this is mostly a MONITOR issue and not an OS or 'human eye' issue.
...I wonder if some monitors handle this situation better than others, or is this 'built in' to most monitors for some (good) reason?

Borderless window. How to add a shadow and remove 1px border?

I need to create a borderless window with specified background color. I know how to remove a non client area and get something like this:
It's cool but not truly what I want. If you take a closer look at any aero window - there's a shadow around it (actually this is not a shadow but some glow). I found somewhere that I can use this code to add a shadow:
const MARGINS shadow_on = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(hwnd, &shadow_on);
It's almost do it's job (thought this is absolutely not clear to me -
documentation says nothing about relationship of shadow and this function). Almost. There's a thin border appeared around the window. It looks like it's semitransparent and it breaks the look and feel of the window:
I know that it's possible - the visual studio even change the color of this border somehow!
Update: as IInspectable noticed in comments I can use negative margins in DwmExtendFrameIntoClientArea(). I set -1 value and got this result:
As you can see - it's even weirder. I tried to fill a background with color, but without luck.
To remove one pixel border after calling this function:
const MARGINS shadow_on = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(hwnd, &shadow_on);
You need to override WndProc WM_NCCALCSIZE message, and return 0 as the result.
Also you need to create window using WS_CAPTION style. (On Windows XP this code won't produce rectangular window, but there is no shadow on WinXP, so on Windows XP you should fallback to WS_POPUP window style)
By the way, to add shadow it is enough to use this margins:
const MARGINS shadow_on = { 1, 0, 0, 0 };
Here is clean windows API code example how to create such window, it is written on Delphi: https://stackoverflow.com/a/44489430/877099

How do I extend nonclient area into a window thinner than 16 pixels?

I'm working on a project that requires me to draw a thin line horizontally across the screen and drag it vertically. To add a bit of flair I'm trying to add the DWM glass effect to the window so that it will match window boders in Windows 7 and 8 (though I know I won't get transparency in Windows 8).
I've got the line drawn by creating a child window with the non client area extended into the client area using the sheet of glass trick with DwmExtendFrameIntoClientArea like this:
MARGINS margins = { -1, -1, -1, -1 };
DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
DwmExtendFrameIntoClientArea(DIV_HWND, &margins);
And I create the window like this:
HWND DIV_HWND = CreateWindow(DIV_NAME,
NULL,
WS_VISIBLE,
0, 0, mon_info.rcWork.right, mon_info.rcWork.top + 3,
top->hwnd, NULL,
hInstance, NULL);
And the window class like this:
const wchar_t DIV_NAME[] = L"DIV";
WNDCLASS DIV = {};
DIV.lpfnWndProc = DIV_PROC;
DIV.hInstance = hInstance;
DIV.lpszClassName = DIV_NAME;
DIV.hCursor = LoadCursor(NULL, IDC_SIZEALL);
RegisterClass(&DIV);
I've gotten the window down to a width of 16 pixels with all the nice borders and drop shadow effects I expect to see around a windows border by handling the WM_GETMINMAXINFO message and returning 2 as the ptMinTrackSize.x and y but I can't seem to get it smaller than that.
I can get a "glassy" window by using DwmEnableBlurBehindWindow on a region I set with SetWindowRgn but the color is that of a background window even when it is in focus. Additionally I miss the slight drop shadow and border.
It seems like I might be hitting the size restriction due to border sizes since each border is 8 pixels wide.
Any help is greatly appreciated!
I've found my answer so I'll leave it here on the off chance someone else needs it. All I had to do was call DwmSetWindowAttribute(hwmd, DWMWA_NCRENDERING_POLICY, DWMNCRP_ENABLED, sizeof(int)); to ensure the DWM rendering policy was not based on the window style. This allowed me to use a WS_POPUP window to get the size I wanted with a nicely glass filled interior.
There are a few bugs here:
There is a strange shadow along the top and bottom if the Y dimension of the window is smaller than 16px.
On Vista\7 the rounded window edges give the window a strange oval-like shape.

Win32: How to make drop shadow honor non-rectangular Layered window?

i've created a layered window by adding the the WS_EX_LAYERED extended style:
wndClass.ExStyle = wndClass.ExStyle | WS_EX_LAYERED;
Windows will use black as the chroma key color value. i'm going to leave a large border of black to make the problem obvious:
After the window is constructed, i tell it to use black as a chroma-key color:
SetLayeredWindowAttributes(hwnd, 0x00000000, 255, LWA_COLORKEY);
Now the popup layered window appears partially transparent:
The problem is the final step. i want to use CS_DROPSHADOW class style, available since Windows XP, to create a drop-shadow:
wndClass.Style = wndClass.Style | CS_DROPSHADOW;
The drop shadow appears, but the shadow surrounds the original rectangular window, and doesn't take into account the window's transparency provided by the layered window:
Does anyone know what magical option i've missed somewhere that will make the drop shadow honor the non-rectangular layered window?
Another example of where this issue appears is when you don't include the 6px padding/margin. The hint window as drawn by Windows® themes is non-rectangular. This leaves a small visible gap where the window is transparent, but the drop shadow does not appear:
Microsoft has managed to make it work, as you can see from this hint from Internet Explorer:
Looking closer at a Windows tooltips class hint window. Using SpyXX - i can get its window rect, and class styles:
SpyXX says:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
So everything points to the window itself being 104x20 pixels, with the drop shadow outside the window itself. (Which is consistent with CS_DROPSHADOW.)
Next i can look at the styles of the tooltips window class:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
Interestingly, it doesn't use CS_SAVEBITS (0x800); which is useful for small, short-lived, windows.
Nor does it use CS_DROPSHADOW (0x20000). So now i wonder how is it drawing outside its own window?
Note: Transparent layered windows is documented as the preferred technique over regions.
Edit: Layered Windows have been around with Windows 2000. CS_DropShadow was added with XP.
Transparent layered windows is documented as the preferred technique over regions.
However, CS_DROPSHADOW does pay attention to regions. If you crop or otherwise shape your window using a region, the drop-shadow will follow the new outline.
Fortunately, you can use regions with layered windows, and by combining the two get the effect you're looking for.
BTW: tooltips_class32 does use CS_DROPSHADOW - you won't see it in the window styles because it's a class style, not a window style.
Why don't you use LWA_ALPHA and build the shadow into the image?
Edit in reponse to your comment:
A) Doesn't stop you using an alpha channeled PNG for a shadow only. Blt the 2 images together and use as one single image.
B) Its not hard to generate a drop shadow. In the image you posted its black with 3 different alpha values.
C) But it doesn't work does it? ie Time to get creative.
D) Then don't try and get windows to do something it won't do for you.
E) Is entirely irrelevant. Layered windows handle that for you.
I strongly recommend you learn more about layered windows because they CAN help you to your goal.
Edit2: You have the bitmap. Its fairly easy to scan over the image and find which bits will be colour keyed (by identifying the black yourself) and hen modify that to have an alpha of 0 where everything else will have an alpha of 255 (Not: You may have to convert the image to a 32-bit image from a lower colour format, you can do this by creating a new DC and copying the image). This will give you the same effect with LWA_ALPHA as with LWA_COLORKEY. From there its fairly easy to identify the pixel at the edge, where the color changes to (R = 0, G = 0, B = 0, A = 0). You then change that first pixel to have a n alpha of 192, the one blow it to 128 and the one below to 64. You now have an alpha'd gradation below the image that will look like the shadow. You can adjust the alpha to get the effect you want.
CS_DROPSHADOW only works with standard rectangular windows. WS_EX_LAYERED windows don't support everything. They are more of a low-level, self-service method to draw exactly what you want.
To get a drop shadow, you'll have to generate the drop-shadow yourself from the alpha channel in the image.

Resources