CTreeCtrl with Explorer theme not DPI aware - c++

I have an MFC app that is high dpi aware. The app displays a CTreeCtrl, which properly draws the expand/collapse (e.g. +/-) glyphs properly at different dpi settings. Here is a snippet at 200%.
In order to present a more modern appearance, I've set the tree control's theme to that of Windows Explorer by adding this to the tree control's PreSubclassWindow overide:
SetWindowTheme(m_hWnd, L"Explorer", NULL);
The tree control now draws the expand/collapse glyphs just like Windows Explorer, which is cool. But, the glyphs do not scale at high dpi settings. Here is another snippet at 200%
The theme part size at 200%, - GetThemePartSize(td, NULL, TVP_GLYPH, GLPS_OPENED, NULL, TS_DRAW, &size) - is 32 pixels. Clearly the Explorer themed glyphs are not growing in size as the dpi increases.
Has anyone else run int to this and, if so, did you find a resolution (other than owner/custom drawing the tree control?
Visual C++ 2015.
Thanks in advance...

I figured out that the high dpi issue has nothing to do with setting the Windows theme. CTreeCtrl has a high dpi bug in that the expand/collapse (e.g. +/-) glyphs are not being properly scaled with or without setting a Windows them.
If you call CTreeCtrl::GetItemPartRect at different dpi scales, you will see the returned rectangle's height is scaled (due to the scaled font), but the width isn't. Thus, what I thought was an issue with the theme was only an illusion, because the themed expand/collapse glyphs have more transparent pixels.
Sorry for wasting everyone's time...

Related

DPI Scaling with windows-generated dialogues in C++?

I'm trying to properly DPI scale an application in C++ and I'm having trouble getting this to work with the File Picker window created from calling OPENFILENAMEW from commdlg.h.
I'm using three monitors: two with 1.0 dpi and one with 2.5 dpi. For me, the file picker only opens with 1.0 DPI regardless of what window my application is in. So when I drag the file picker to the 2.5 dpi monitor, the window is so small is hard to read. I can only get it to scale with 2.5 dpi when I disconnect the other monitors. I looked at the documentation for OPENFILENAMEW and there is a flag to allow the dialogue to resize manually but that's about it.
It has to register the dpi at some point to scale but I just can't find it.
Does anyone know how to do this?
Enabling per monitor DPI awareness in Manifest settings didn't solve this completely but it did lead me to the answer I was looking for! So the issue that persisted was that once the file-picker window was created, it kept it's DPI scale from its original window even after moving it to a window with different DPI.
Apparently the options in Manifest don't support this and neither does the SetProcessDpiAwareness function in the shellscaling api, which can be used to set that Manifest setting programmatically.
However, the SetProcessDpiAwarenessContext from winuser.h has one more option that the others don't: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. This can only be used on windows machines with the Creators update (named Redstone 2) and you can check that to do DPI scaling right when you can, and wrong but as good as possible when you can't:
if (IsWindowsVersionOrGreater(HIBYTE(NTDDI_WIN10_RS2), LOBYTE(NTDDI_WIN10_RS2), 0)) {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
else {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
And this works!

MFC picture control changes size when DPI awareness disabled or running on Win7

I made an MFC app for my friend using VS2015 in Win10. It looks like this, which is exactly the same as in resource editor.
.
But when he ran the app on his computer in Win7, the Bitmap image in Picture Control enlarges and covers up some text boxes below, which looks like this.
.
After I searched and realized it may be related with DPI awareness. I disabled DPI-Awareness in property page of Manifest Tool and rebuilt. The same thing happened even when it runs in Win10.
Could someone help me explain the cause of this and find a solution to fix the size of the image control? Thanks.
The main problem is that a dialog from a resource is always measured in DLUs.
And DLUs are calculated from the size of the font, that is used for the dialog.
See this article how dialog base units are calculated.
Now you have a static picture control that is sized in DLUs. The bitmap is just scaled in pixels and is never resized, when you assign it to a static dialog control. And because the real size of the static control depends on the used font, you get different layouts for your dialog and your bitmap.
And because just the font changes when you choose no DPI awareness and because the font changes from windows version to windows version your dialog always look different.
Advice: Paint you picture your own and stretch it accordingly.
Also this stackoverflow question is nice documents and shows the effect of DLUs.
And here some code for auto sizeing picture controls.
An auto-sizing bitmap picture control
A simple image preview class using GDI+
CxImage
Normally, I prefer to keep control in my hand by using SetWindowPos() to set the size of image I want in different situations. You can use below two lines to control/set position and size of your image.
Assume ID of the Picture Control is IDC_STATIC2 then you can use like:
CStatic * pStatic = (CStatic *) GetDlgItem(IDC_STATIC2);
pStatic->SetWindowPos(NULL,20,20,50,50,0);

MFC Picture Control is not scaled automatically according to Windows display scale

I have a Picture Control in my application which is not scaled properly according to the Windows zoom - 100, 125, 150 % etc.
I have done a research but only found a solution for C#, which is handled by a property AutoScaleMode = AutoScaleMode.Dpi;
Can anyone tell me what is the alternative in MFC?
MFC applications automatically default themselves to be DPI aware, this means that it assumes any resizing for bitmaps etc will be handled by the app (e.g. the app may have multiple versions of the same bitmap depending on DPI settings). It makes the app look much neater on scaled machines because the alternative is to automatically scale the whole app which can make it look 'fuzzy'.
You can switch OFF DPI awareness, have a look at this article:
MFC applications now default to being DPI-aware

How do you scale the title bar on a DPI aware win application?

I am making my app dpi-aware per monitor by setting <dpiAware>True/PM</dpiAware> in the manifest file. I can verify with process explorer that this is indeed working or by calling GetProcessDpiAwareness.
This is all working fine and I can scale anything in the client area fine in my code. However, my only problem is that if I drag my app from a system-dpi monitor to a non-system dpi monitor, the title bar and any system menu would either become too big or too small. This isn't the case for most built-in apps (e.g. calc, edge browser, etc..) so there must be away to scale it properly. Does anyone how the devs at MS did this?
The screenshot below should explain my problem better. Also notice, that the padding between the close, min, and max button is different when it's scaled (96dpi).
Sample app I'm attaching a very simple app that is per-monitor dpi aware.
The Windows 10 Anniversary Update (v1607) has added a new API you must call to enable DPI scaling of the non-client areas: EnableNonClientDpiScaling. This function should be called, when WM_NCCREATE is received. The message is sent to the window's procedure callback during window creation.
Example:
case WM_NCCREATE:
{
if (!EnableNonClientDpiScaling(hWnd))
{
// Error handling
return FALSE;
}
return DefWindowProcW(...);
}
If the application's DPI-awareness context is DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, then calling EnableNonClientDpiScaling should be omitted, as it won't have any effect, although the function will still return successfully.
From the documentation:
Non-client scaling for top-level windows is not enabled by default. You must call this API to enable it for each individual top-level window for which you wish to have the non-client area scale automatically. Once you do, there is no way to disable it. Enabling non-client scaling means that all the areas drawn by the system for the window will automatically scale in response to DPI changes on the window. That includes areas like the caption bar, the scrollbars, and the menu bar. You want to call EnableNonClientDpiScaling when you want the operating system to be responsible for rendering these areas automatically at the correct size based on the API of the monitor.
See this blog post for additional information about DPI scaling changes in Windows 10 AU.
Does anyone how the devs at MS did this?
This has a pretty disappointing answer. Using Alin Constantin's WinCheat and inspecting the top-level window of Calculator, I see a window size of 320x576, and a client size that is also 320x576.
In other words, Microsoft entirely avoids the problem by suppressing the non-client area of the window, putting everything in the client area instead. Making this work well for you may involve custom drawing of the title bar.
Something worth noting is that Calculator and e.g. Windows Explorer don't use the same colour for the title bars. Calculator doing custom drawing of the title bar would explain that perfectly.
UPDATE:
It is enough to add new <dpiAwarness> declaration to manifest to solve all this mess. Example is here.
Remnants of former investigations (obsolete):
More investigations on the subject.
System setup: two monitors, one is 96 dpi another one is 267 dpi (Microsoft Surface 4).
Testing window is moved to secondary 96 dpi monitor:
Here is rendering (wrong, IMO) with <dpiAware>true/pm</dpiAware> in manifest:
Note huge size of caption bar and wrong sizes of window icons.
And here is correct rendering using <dpiAware>true</dpiAware>
And I suspect that MSDN documentation is plainly misleading about values of PROCESS_DPI_AWARENESS. I do not see any differences in messages and styles between <dpiAware>true</dpiAware> and <dpiAware>true/pm</dpiAware>. The later one just makes caption larger. In both case application receives WM_DPICHANGED message while moving between monitors with different DPI.
Sigh.
The documentation says:
Note that the non-client area of a per monitor–DPI aware application is not scaled by Windows, and will appear proportionately smaller on a high DPI display.
The Microsoft apps that you link to deal with this by removing the non-client area and making the client area cover the entire window.

How to scale font sizes based on current DPI settings in VC++/MFC applications?

How to scale font sizes based on current DPI settings in VC++/MFC applications ?
As of now when I change the DPI from 100% yo 150% the font sizes remain the same, although the icons will scale down based on the current dpi ..
Please suggest the best way for above problem.
In Windows Vista and 7, the OS tries to hide the DPI from your program and does adjustments behind the scenes. If you want your program to react properly to DPI changes you must follow the guidelines from Microsoft titled Creating a DPI-Aware Application.
By specifying the text and control sizes in DLU's. That happens by default though, so I assume you are generating dialogs dynamically or from a memory-based DLGTEMPLATE. If you, you're (pardon my French) screwed, because you'll have to muck about with converting DLU's to pixels, a very painful and tedious process. Read the following KB articles:
http://support.microsoft.com/default.aspx?scid=kb;en-us;125681
http://support.microsoft.com/default.aspx?scid=kb;en-us;145994
Don't use DPI for font scaling. Instead, use the settings the user has configured in the "Appearance" section of Control Panel.
You might also want to consider making the font size configurable for just your application.