When creating a control (e.g. an edit control) on the fly using CreateWindow, it usually starts out with an ugly (boldish sans serif) font.
Usually I wok around that by grabbing the parent dialog's font, and setting it to the control - I can't even say if this is a good idea.
How do I "legally" fetch the right font?
The "correct" way to get the font used in dialog boxes like message boxes, etc. would be via the SystemParametersInfo() function:
// C++ example
NONCLIENTMETRICS metrics;
metrics.cbSize = sizeof(NONCLIENTMETRICS);
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS),
&metrics, 0);
HFONT font = ::CreateFontIndirect(&metrics.lfMessageFont);
::SendMessage(ctrlHWND, WM_SETFONT, (WPARAM)font, MAKELPARAM(TRUE, 0));
Don't forget to destroy the font when the controls are destroyed:
::DeleteObject(font);
You can look up the MSDN documentation for NONCLIENTMETRICS and SystemParametersInfo() to see what other system-wide parameters you can retrieve.
Related
I tried to make one of my TForms decendants transparent. The form is floating above (Z-wise) another application (not mine) and all that should be seen are a few controls "floating in the air". These controls provide some extra functionality and information that I'm missing. I have set
this->TransparentColorValue = this->Color;
this->TransparentColor = true;
This didn't have any visible effect, even if I set these at runtime to make sure that whatever Color the form has, the TransparentColor should be the same. I also tried explicitly setting the Color to clBlack first to make it as simple as possible.
My application also uses a Custom Style (High-DPI Optimized or "normal" doesn't seem to make a difference) and I'm now thinking that it's not possible to use one of these styles and make one of the forms transparent.
If I remove the custom style and use the default "Windows" style, the form becomes transparent and behaves just like I want. I can "click through" my transparent form to interact with the application behind it.
I tried SetWindowTheme(this->Handle, nullptr, L""); on the form in question but that didn't change its appearance either. I also wanted to try SetWindowTheme(this->Handle, nullptr, L"The CLSID for Windows"); but I couldn't find the CLSID for the "Windows" style.
Changing the style using the TStyleManager a'la std::make_unique<TStyleManager>()->SetStyle(L"Windows"); changes the style of the whole application.
I've enabled Runtime Themes and set DPI Awareness to Per Monitor v2.
Is my suspicion correct or is there a way to use a Custom Style and still have one form in an app transparent? If a moderate amount of WinAPI tinkering is required to do it, I'm happy with that too.
C++ Builder Community 10.4
My question arises from our program running on a Japanese OS. One of my co-workers had done something like embed a property sheet on a dialog and the property sheet has multiple pages. Everything works fine on other languages except Japanese.
On Japanese systems, some of the controls get cut off because there is not enough space. I determined that this was because on Japanese systems, the font used by the property sheet and hence the property pages was different than the font used by the parent dialog of the property sheet. For the record, the font used by the parent dialog and all the property pages was MS Shell Dlg, 8:
FONT 8, "MS Shell Dlg", 0, 0, 0x1
I wanted a simple fix to try and ameliorate the problem for Japanese and all potential system. I examined the fonts on Japanese Windows 7 property sheets/pages and they always SEEMED to be the Default GUI Font. So, when I was creating my first dialog, on the fly after loading the DIALOGTEMPLATE into memory with the MFC class CDialogTemplate, I would modify the font of the parent dialog to match the default GUI font, and everything would be fine on Japanese Windows--Window 7, that is.
A customer has now found that this is not a valid solution for Windows 8/8.1--it exhibits the original problem. After examining the fonts on a Windows 8.1 VM, I did determine that the property sheet on Windows 8.1 and the child property pages does not use the Default GUI Font.
That's lots of words to ask. is there a way to determine what the default font used by property sheets on a Windows system?
I figure my ugly workaround would be to create a property sheet with one property page, determine the font used by that property sheet and page, and then modify the dialog template of my parent dialog on the fly to use that font. Since property sheets have some quirks about activation (they get activated on creation even if invisible), I'd rather not, but it seems to be my only choice--besides re-engineering of my co-worker's dialogs.
Yes, apparently the font for Property Sheet and Wizard is different. There is a dialog template for each language. In WinAPI PropertySheet uses the font from that template.
To find this font use the following (I only tested this on Windows 10)
#define IDD_PROPSHEET 1006
#define IDD_WIZARD 1020
HMODULE hmod = LoadLibrary(L"comctl32.dll");
if (hmod)
{
HRSRC hres = FindResource(hmod, MAKEINTRESOURCEW(IDD_PROPSHEET), (LPWSTR)RT_DIALOG);
if (hres)
{
HGLOBAL hglob = LoadResource(hmod, hres);
if (hglob)
{
CString fontname;
WORD fontsize;
CDialogTemplate::GetFont((DLGTEMPLATE*)hglob, fontname, fontsize);
TRACE(L"%s %d\n", fontname, fontsize);
}
}
FreeLibrary(hmod);
}
This might be somewhat outdated. The font obtained from SystemParametersInfo(SPI_GETNONCLIENTMETRICS...) must already be compatible because it's the main display font.
For some reason, MFC takes things further and applies that template font to property pages as well. MFC does other things which I can't understand, for example VS2015 has this code in "dlgprop.cpp"
LANGID langid = GetUserDefaultUILanguage();
if ((PRIMARYLANGID(langid) == LANG_JAPANESE) && IsFontInstalled(_T("MS UI Gothic")))
{
WORD wLang = MAKELANGID(LANG_JAPANESE, 0x3f);
if (wLang != 0)
{
hResource = ::FindResourceExW(hInst, (LPWSTR) RT_DIALOG,
MAKEINTRESOURCEW(bWizard ? IDD_WIZARD : IDD_PROPSHEET), wLang);
}
...
}
I don't know why it forces a sub-language if a certain font is present. Also as #PRinCEKtd points out, this font might be outdated (but the font could be still be installed) You can step through CPropertySheet to find all the font manipulations.
see also codeguru link, Custom Font in Property Sheets
try use
NONCLIENTMETRICS ncm = { GetNONCLIENTMETRICSWSize() };
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)){..}
usual all system controls (status bar, list view, tree view,menu, message box..) use fonts from this struct
where GetNONCLIENTMETRICSWSize() can be like this (on XP/2003 (major < 6) struct size is less on 4 byte compare vista+)
ULONG GetNONCLIENTMETRICSWSize()
{
static ULONG m;
if (!m)
{
RtlGetNtVersionNumbers(&m, 0, 0);
}
return m < 6 ? sizeof(NONCLIENTMETRICS) - 4 : sizeof(NONCLIENTMETRICS);
}
From Windows Vista onwards, the Meiryo font family was used as default in the Windows user interface to support Japanese. I think before vista, it was MS Gothic...
How about manually setting a default windows font directly? There are many fonts that support Japanese as well as a wide variety of languages. Some of them have been present from XP upto 10, though they might have some updates.
The picture below (enlarged, so you better see the differences) shows Font differences between dynamically created Edit controls (the upper two examples) and Edit Controls created from the Dialog Editor (the lower example). How can I make the font of my dynamically created CEdit controls looking like the default (the lower example)?
I have created the CEdit Controls like following:
obj->CreateEx(WS_EX_CLIENTEDGE, _T("EDIT"), _T(""),
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
rect.left, rect.top, rect.Width(), rect.Height(),
GetSafeHwnd(), reinterpret_cast<HMENU>(mId));
obj->SetFont(&mFont); // mFont was created in the Dialog Constructor
// with mFont.CreatePointFont(80, _T("MS Shell Dlg"));
Thanks for your help!
The first example is using the System font (SYSTEM_FONT), as retrieved with the GetStockObject function, which is a bitmap font that has not been used since the days of Windows 3. More information is available on Raymond Chen's blog, and Michael Kaplan's blog.
The second example is using the "MS Shell Dlg" font, just like you asked it to. That actually maps to a font called "Microsoft Sans Serif" or "MS Sans Serif", the UI font back in the days of Windows 95 and 98. This is also known as DEFAULT_GUI_FONT, which indeed used to be an accurate name for it, but alas, it is accurate no longer.
Beginning with Windows 2000 (and continued in XP), Tahoma was used as the default UI font. This is what you are seeing in the third example: Tahoma 8 pt. Unfortunately, even on those operating systems, "MS Shell Dlg" does not return Tahoma--it still returns MS Sans Serif, which is why it looks wrong.
So, you could simply specify Tahoma as the GUI font, but that wouldn't really be correct, because it would break in older versions of the OS where Tahoma isn't installed or supported, or on foreign language versions of the operating system, where a different font is used out of necessity. Instead, you're supposed to specify the DS_SHELLFONT flag, which Raymond talks about here.
And all was fine and good until Windows Vista came out. And in Windows Vista, the powers that be at Microsoft decided that Tahoma was getting a little long-in-the-tooth and Windows was due for another UI font upgrade. They developed their own special font in-house called Segoe UI, supposedly designed for optimum on-screen readability. And in a special little twist, they decided that the default size should now be 9 pt, instead of 8 pt as used by every previous version of the OS, regardless of the font face. And you would probably think that either "MS Shell Dlg", "MS Shell Dlg2", or DS_SHELLFONT (or all three) would get you this new-fangled Segoe UI font, but you'd be wrong.
Uh oh. Now things get tricky... Not only does Vista use a different font than XP that is not easily accessible with a one-size-fits-all identifier, but it also uses a different size, changing the way your dialog will look on those systems, if you can get it to display at all. In many, many places, the Windows shell team appeared to simply punt the challenge--Tahoma 8 pt is used all over the place, even with the Aero theme enabled, when it's supposed to be using Segoe UI 9 pt. This kind of thing really makes the UI look unpolished, and it was the subject of lots of nitpicking back in the early days of Vista. Now, it seems most people have forgotten about it, but the UI hasn't started looking any less scattered and inconsistent.
And you're not the Windows shell team: you can't get away with this in your own app. The Top Rules for the Windows Vista User Experience even state explicitly that you should always:
Use Segoe UI, the new Windows Vista system font.
Respect the user's settings by always referencing the system font, sizes, and colors using the Windows Theme APIs. Don't use fixed values for fonts, sizes, or colors.
To be honest, I haven't really heard a good solution to this problem yet. And I suspect that by the time I ever do, no one will need to support Windows XP anymore (although most people aren't quite there yet). But here's what I do: I extract the default system font at runtime using the SystemParametersInfo function. Fortunately, the system message box font (lfMessageFont) is the correct font face and size, regardless of the current version of Windows and the user's chosen theme.
My code to initialize windows or dialogs generally looks something like this (SystemInfo::IsVistaOrLater is a helper function I've written; the implementation is the obvious):
// Get the system message box font
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
// If we're compiling with the Vista SDK or later, the NONCLIENTMETRICS struct
// will be the wrong size for previous versions, so we need to adjust it.
#if(_MSC_VER >= 1500 && WINVER >= 0x0600)
if (!SystemInfo::IsVistaOrLater())
{
// In versions of Windows prior to Vista, the iPaddedBorderWidth member
// is not present, so we need to subtract its size from cbSize.
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
}
#endif
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
HFONT hDlgFont = CreateFontIndirect(&(ncm.lfMessageFont));
// Set the dialog to use the system message box font
SetFont(m_DlgFont, TRUE);
SendMessage(hWnd, WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(FALSE, 0));
Or even easier in MFC, with the handy SendMessageToDescendants method
(m_DlgFont is a CFont object defined for the class):
// Get the system message box font
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
LOGFONT lfDlgFont = ncm.lfMessageFont;
m_DlgFont.CreateFontIndirect(&lfDlgFont);
// Set the dialog and all its controls to use the system message box font
SetFont(m_DlgFont, TRUE);
SendMessageToDescendants(WM_SETFONT, (WPARAM)m_DlgFont.m_hFont, MAKELPARAM(FALSE, 0), TRUE);
If you're not using MFC, I highly recommend implementing your own recursive version of SendMessageToDescendants. It makes the initialization code a lot simpler.
In my projects i copy the font from the main dialog. But the main dialog must be built by the Dialog Editor.
#include <windowsx.h> // for Get/SetWindowFont()
SetWindowFont(editHwnd, GetWindowFont(mainDlgHwnd), true);
I've got a dialog box where I need to display the standard Information icon. Here's my RC code:
ICON "",IDC_ICON_INFORMATION,18,70,21,20
I process the WM_INITDIALOG message as follows:
HICON aIcn = LoadIcon(NULL, IDI_INFORMATION);
SendDlgItemMessage(m_hWnd, IDC_ICON_INFORMATION, STM_SETICON, (WPARAM) aIcn, 0);
Everything works great under 96 DPI: the static control displays a 32x32-pixel icon.
However, when I switch to higher DPI's (through right-clicking on the Desktop, choosing Screen resolution, and clicking Make or other items larger or smaller) the icon does not scale! Since everything else scales nicely, the icon looks visually much smaller than the neighboring text. I would expect that on 144 DPI (150%) the icon dimensions will be 48x48 pixels. I did declare my application as DPI-aware through an XML manifest.
The damnedest thing is that when I use my own custom-made icon (also coming from the RC file), everything scales perfectly. Furthermore, the MessageBox function called with the MB_ICONINFORMATION flag does display a scaled version of the icon, as well.
Given these thoughts, I assume of the following:
The static control with the SS_ICON style can display scaled versions of icons.
The icon resource that contains the standard Information icon has a scaled version of the icon (48x48).
What am I doing wrong then?
Use LoadImage() instead of LoadIcon(), and specify the cxDesired and cyDesired params with the values you get from GetSystemMetrics(SM_CYICON) and GetSystemMetrics(SM_CXICON).
Or maybe just declaring your app as DPI aware could be enough? You can try that easily by simple creating a text file making it a manifest file.
See the example in the remarks section for the SetProcessDPIAware API
I'm writing an application for a Pocket PC 2003 device. In it there is a dialog where various text information is shown. The information is separated so that each piece resides inside its own label, defined as LTEXT in the resource file.
Now my problem is that, at the moment, all text lables have the same font and style (normal or simple, i.e. not bold or italic); I want want one to be set in bold. I know that I can set the font to bold in the resource file, but that sets the style of all labels.
How does one achieve this? I've seen it be used in the Windows 'About' screen so I know it's possible. I've written the program in C++ using the Win32 API directly (except for certain dialogs where I've used the resource file) so I would appreciate if the answer was given in the same language and approach.
Thanks.
In the resource editor, edit the static text item, and change its control ID to something unique: IDC_BOLD for example.
In the DialogProc for the dialog boxes that is hosting the control, add a WM_CTLCOLORSTATIC handler:
case WM_CTLCOLORSTATIC:
HDC hdc;
HWND hwndCtl;
hwndCtl = (HWND) lParam;
hdc = (HDC) wParam;
if( GetWindowLong(hwndClt, GWL_ID ) == IDC_BOLD )
{
SetBkMode(hdc,TRANSPARENT);
SetTextColor(hdc,RGB(0xff,0,0)); // turn the text red for fun :)
SelectObject(hdc,hBoldFont); // but you want this...
return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE);
//return 0L; // if visual themes are enabled (common controls 6) then 0 is better.
}
// default processing
return 0;
You are developing for Pocket PC 2003, I don't know what buttons styles are available. This Page refers of course to desktop XP. But, if the buttons in the dialogs are not flat grey 95esq buttons, then it might be more appropriate to return 0 as that will paint the text background correctly if the dialogs background is not plain grey.
Pre-visual styles a return of 0 causes the system to reset the DC so its important to know which return is appropriate.