MFC SetFont for specific dialog control not working - c++

I have a standard MFC SDI app. In the About Dialog (created with the MFC Wizard), I am trying to change some static text format (labels). This is my code:
BOOL CAboutDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
auto font_programname = std::make_shared<CFont>();
font_programname->CreateFontW(25, 0, 0, 0, FW_BLACK, 0, 1, 0, 0, 0, 0, 0, 0, _T("Arial"));
_label_programname.SetFont(font_programname.get());
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
However, this results only in the font becoming bold, but the size does not change. What am I doing wrong?

The font is becoming bolder, so the CreateFont and SetFont are doing something. This leaves the Font Mapper as the next suspect. The "Arial" is a suggestion, but the font mapper will look at other characteristics first. The font you are seeing is most likely the largest raster (non True Type) font that is available in an FW_BLACK weight. Getting the exact font you want will require filling in other parameters of CreateFont so the desired font is located.
For debugging, follow the CreateFont call with a call to GetLogFont and view the data structure to see what font was actually mapped.
The comment above concerning the lifetime of the font is correct. That may not be the first issue you are facing but it is an issue. The CFont needs to be a dialog-class variable, not a local variable in the function, so the font object lasts as long as the control on the dialog.

It does look like you do not need a CFont object for your purposes, as you let it go out of scope.
Also see WM_SETFONT.
const int iFontSize = 25;
const CString sFont = L"Arial";
HFONT hFont = CreateFontW(iFontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, sFont);
yourControl.SendMessageW(WM_SETFONT, reinterpret_cast<WPARAM>(hFont), TRUE);
The low-order word of lParam specifies whether the control should be redrawn immediately upon setting the font. If this parameter is TRUE, the control redraws itself.
Though you should keep the object(s) in a container too, (E.g. std::vector<HFONT>) and delete the/all object(s) later (destructor).
The application should call the DeleteObject function to delete the font when it is no longer needed; for example, after it destroys the control.

Related

Set the console window size to match screen buffer on Windows

Playing around with the console functions to control the layout of a console program, and I cannot change its size.
Currently, what I can do is disable resizing, remove buttons and change the buffer size, but if I try to resize the window itself, all attempts fail; albeit, some functions do resize the window, but in pixels, so scale is drastically off.
What I've done so far (not a C++ person, just fiddling around, go easy on syntax-isms):
#include <iostream>
#include <Windows.h>
using namespace std;
COORD conBufferSize = { 150, 40 };
SMALL_RECT conScreen = { 0, 0, 150, 40 };
CONSOLE_SCREEN_BUFFER_INFOEX csbie;
int main()
{
// console opened for application
HWND hwConsole = GetConsoleWindow();
// hide it
ShowWindow(hwConsole, SW_HIDE);
// get the style for it
DWORD style = GetWindowLong(hwConsole, GWL_STYLE);
// disable maximizing and minimizing and resizing
style &= ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX);
SetWindowLong(hwConsole, GWL_STYLE, style);
HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleScreenBufferSize(hConsole, conBufferSize);
// this does nothing to the window itself as best I can tell
// if by "window" it means what portion of the display window you view "into"
// commented out here for functionality
// SetConsoleWindowInfo(hConsole, TRUE, &conScreen);
SetConsoleActiveScreenBuffer(hConsole);
// this sequence works, but seems by accident
// surely there is another method?
csbie.cbSize = sizeof(csbie);
GetConsoleScreenBufferInfoEx(hConsole, &csbie);
csbie.srWindow = conScreen;
SetConsoleScreenBufferInfoEx(hConsole, &csbie);
// required to update styles
// using the cx/cy parameters sets size in pixels
// that is much smaller than buffer size which accounts for font size
// therefore this "doesn't" work
SetWindowPos(hwConsole, HWND_TOP, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE| SWP_SHOWWINDOW);
// to look at the console until close
while (1) {
}
return 0;
}
Now from what I can reason, if I have a screen buffer that is 100 columns by 40 rows, that doesn't translate directly to the size of the window housing the buffer. So my next thought is that I need to determine how many pixels the current console font is using, then multiply the buffer dimensions by that to determine the pixel size and use SetWindowPos or the SetConsoleScreenBufferInfoEx methods.
One thing I am unsure about is why the srWindow attribute is able to modify the display window with a similar description to that of SetConsoleWindowInfo and yet that produces no discernable change.
One thing I am unsure about is why the srWindow attribute is able to
modify the display window with a similar description to that of
SetConsoleWindowInfo and yet that produces no discernable change.
According to the doc,
The function fails if the specified window rectangle extends beyond
the boundaries of the console screen buffer. This means that the Top
and Left members of the lpConsoleWindow rectangle (or the calculated
top and left coordinates, if bAbsolute is FALSE) cannot be less than
zero. Similarly, the Bottom and Right members (or the calculated
bottom and right coordinates) cannot be greater than (screen buffer
height – 1) and (screen buffer width – 1), respectively. The function
also fails if the Right member (or calculated right coordinate) is
less than or equal to the Left member (or calculated left coordinate)
or if the Bottom member (or calculated bottom coordinate) is less than
or equal to the Top member (or calculated top coordinate).
Use GetConsoleScreenBufferInfoEx to get the size of the screen buffer (e.p: 120,30). As long as the SMALL_RECT set is less than 120, 30, you will see the screen buffer size change after calling SetConsoleActiveScreenBuffer.
SMALL_RECT conScreen = { 0, 0, 50, 20 };
...
SetConsoleWindowInfo(hConsole, TRUE, &conScreen);
SetConsoleActiveScreenBuffer(hConsole);
Please always check the return value of each api when debugging.

MFC how to change font when I move the window?

Basically, I am trying to display the output and then update the font of the display when the window is moved. I am trying to use onMove() method to determine if window is moved. However, my problem is somehow, when my program is initialized, it calls the onMove() method instantly. It does not even wait for the window to be moved. Therefore, the font is already changed before the window is moved. What I am trying to do is that, change the font of the display after the window is moved. Honestly, I don't know if there is a way to call onMove() after the window is initialized. In any case, my program will invoke the onMove(). I don't think this is going to be my solution. Does anybody have any other ideas how could I achieve that? Here is a sample execution of What I am trying to do and when I run the program, the test output is already displayed with new font and color. I would like to do that after the window is moved.
void CMainFrame::OnMove()
{
CFrameWnd::OnMove(x, y);
CDC *dc;
dc = GetDC();
CRect rect;
dc->SetTextColor(RGB(255, 128, 0));
CBrush brush;
CFont oFont;
oFont.CreateFont(25, 0, 0, 0, 400, false, false,
0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, _T("Times New Roman"));
dc->SetBkMode(TRANSPARENT);
dc->SelectObject(oFont);
dc->FillRect(&rect, &brush);
Invalidate();
UpdateWindow();
dc->TextOutW(0, 200, _T("TESTTTT"));
}

Printing to a Printer DC with MFC

I've followed Microsoft's tutorial on creating a Device Context, and I've tried looking around the internet for a decent source (apparently, MFC is a mystical thing). The following successfully prints out "Hello, World!"; except it's extremely tiny.
How can I send a CImage to the printer, rather than text?
And how could I get the text's size to be bigger than a couple millimeters?
I've scoured MSDN, but everything is either outdated (like the example code I am using), or just not well documented.
// get the default printer
CPrintDialog dlg(FALSE);
dlg.GetDefaults();
// is a default printer set up?
HDC hdcPrinter = dlg.GetPrinterDC();
if (hdcPrinter == NULL)
{
//MessageBox(_T("Buy a printer!"));
}
else
{
// create a CDC and attach it to the default printer
CDC dcPrinter;
dcPrinter.Attach(hdcPrinter);
// call StartDoc() to begin printing
DOCINFO docinfo;
memset(&docinfo, 0, sizeof(docinfo));
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = _T("CDC::StartDoc() Code Fragment");
// if it fails, complain and exit gracefully
if (dcPrinter.StartDoc(&docinfo) < 0)
{
//MessageBox(_T("Printer wouldn't initalize"));
}
else
{
// start a page
if (dcPrinter.StartPage() < 0)
{
//MessageBox(_T("Could not start page"));
dcPrinter.AbortDoc();
}
else
{
// actually do some printing
//CGdiObject* pOldFont = dcPrinter.SelectStockObject(SYSTEM_FONT);
dcPrinter.SetMapMode(MM_HIENGLISH);
auto font = CreateFont(
3'000, // nHeight
1'500, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Arial")); // lpszFacename
dcPrinter.SelectObject(&font);
dcPrinter.TextOut(450, 450, _T("Hello World!"), 12);
dcPrinter.EndPage();
dcPrinter.EndDoc();
//dcPrinter.SelectObject(pOldFont);
}
}
}
Tiny Text Problem
The problem is that, by default, the size of a font is specified in device-dependent units and printers are generally much higher resolution that a screen. So if you've created a font that is 20 pixels high on the screen (which might have 96 pixels per inch) when you try to use that font on a printer, which maybe has 300 or 600 dots per inch, your text looks really small.
As another answer shows, one idea is to change the mapping mode so that the printer uses units that are closer to what is on the screen.
An alternative way is to create a new font with an appropriate size (the lfHeight field in the LOGFONT structure) based on the DPI of the printer, which you can determine with the GetDeviceCaps function. This can be handy if you want a particular font size, like 14 point text.
LOGFONT lf = {0};
lf.lfHeight = -MulDiv(point_size, ::GetDeviceCaps(dcPrinter, LOGPIXELSY), 72);
// init other field of lf as you like
HFONT hfontPrinter = ::CreateFontIndirect(&lf);
HFONT hfontOld = ::SelectObject(hdcPrinter, hfontPrinter);
// now draw to the hdcPrinter
::SelectObject(hdcPrinter, hfontOld);
::DeleteObject(hfontPrinter);
Sending a CImage
I don't use MFC, but it looks like you can just call CImage::StretchBlt with the printer DC. Once again, you'll probably have to take the printer's much higher resolution into account when you choose the target coordinates.
Use CFont::CreatePointFont() or CFont::CreatePointFontIndirect() to create a font that is reasonable. Most printers are 600 DPI. Most screens are 96 DPI. A 12 point font on the screen is basically a 2 point font and illegible on a printer.
Create the font and select it into your DC. Do not forget to select it out of the DC after using it and before destroying your DC (CDC class). (The CDC destructor automatically deletes the HDC).
Here is the problem:
dcPrinter.SetMapMode(MM_TEXT);
MM_TEXT maps one logical point to one device point; considering typical resolution of 600 DPI for a printer, your stuff will be few times smaller that on the screen.
Use MM_HIENGLISH or some other device-independent mode; here is MSDN link.
I have used the following to successfully print "Hello World!" and "Have a Nice Day!" to the right of a 200x200 monochrome bitmap (MyLogo.bmp) placed at the origin of the printer page (I am using a black & white thermal printer):
CDC printDC( GetMyPrintDC() ); // e.g. as per original code
DOCINFO di( GetMyDocInfo() ); // e.g. as per original code
printDC.StartDoc( &di );
ATL::CImage logo;
logo.Load( "MyLogo.bmp" );
const BOOL result( logo.Draw( printDC.GetSafeHdc(), CPoint( 0, 0 ) ) );
CFont myFont, *old;
myFont.CreatePointFont(100, "Courier New", &printDC);
old = printDC.SelectObject(&myFont);
printDC.TextOut( 250, 50, " Hello World!" );
printDC.TextOut( 250, 150, "Have a nice Day!" );
printDC.SelectObject( old );
myFont.DeleteObject();
printDC.EndPage();
printDC.EndDoc();
printDC.DeleteDC();
The three indented lines highlight all that is required to render a CImage on my printer. Vary the parameter in CreatePointFont() to size the (otherwise tiny) text to suit.

Windows Toolbar - Controlling button size and padding

I'm trying to understand the behaviour of a Windows toolbar - in particular how the following values interact:
the size of the bitmap image used
the effective size of a toolbar button
the padding between the image and the button edge
the height of the toolbar
Text displayed by a button is not relevant in my case.
What I actually want to do is provide an option for the user so he can choose from several toolbar button sizes (that will display bitmaps of say, 16x16, 32x32, or 48x48 pixels) and redisplay the toolbar accordingly after the option value changes. This is implemented by destroying the toolbar's image lists and rebuilding them with the appropriate bitmaps. The problem I currently have is that when switching from size 16 to 48 and back to size 16, the toolbar looks slightly different than before.
This is what the toolbar looks like when the application starts (correct):
Once I switch to size 48 and back again, it looks like this (wrong):
All buttons are higher than before, and each dropdown button has additional space around its bitmap and its dropdown arrow.
(For testing purposes, the toolbar has been made high enough to accomodate all button sizes without requiring an increase in height. This is to rule out the possibility that the change in button size stems from a possible toolbar resize, necessitated by temporarily switching to size 48.)
It looks as if additional padding were being rendered between a button bitmap and the button edge - as if rebuilding the toolbar with larger bitmaps/buttons caused Windows to internally increase the padding (which would make sense), but not decrease it when I subsequently rebuild the toolbar with the smaller bitmaps/buttons. However, sending TB_GETPADDING always returns 0x00060007, which indicates that the standard (correct) padding for 16x16 bitmaps is in place.
In an attempt to solve the problem by setting padding myself, I set the TBSTYLE_AUTOSIZE style on all non-separator buttons (this is required in order to apply padding). With this style, without even calling TB_SETPADDING, after switching to size 48 and back again, the toolbar looks like this:
In this case, the button height is also wrong.
The question is: What is causing the buttons to be displayed differently after rebuilding the image lists?
Some aside notes:
When building the toolbar, I call TB_SETBITMAPSIZE, but neither TB_SETBUTTONSIZE nor TB_SETPADDING, because the bitmap size is all I have, and I assumed the button size would be derived correctly from that.
I'm aware I could simply build the entire toolbar window from scratch (not just the image lists), but would like to avoid that, so I can keep working with the same toolbar window handle.
I'm aware of the CCS_NORESIZE toolbar style (it's currently set) and the TB_AUTOSIZE message, but experiments with them have not led to any insights.
I can't say what is the problem(there is no code in the question) but it is most probable
that the solution of destroying the list of images causes this. You dont need to destroy the lists
but to remove the buttons and then add new ones. The bellow code works fine:
Create ToolBar:
if((toolBarHwnd = CreateWindowEx(
0,
TOOLBARCLASSNAME,,
NULL,
WS_VISIBLE | WS_CHILD | TBSTYLE_WRAPABLE,
0,
0, //820,
0,
0,
winHwnd, //main window
(HMENU)IDC_TOOLBAR,
hThisInstance,
NULL
)) == NULL){/*Error*/}
Create ImageList's for your images:
HIMAGELIST g_hImageListSmall = NULL, g_hImageListMedium = NULL, g_hImageListLarge = NULL;
int numButtons = 3
g_hImageListSmall = ImageList_Create(16, 16, // Dimensions of individual bitmaps.
ILC_COLOR16 | ILC_MASK, // Ensures transparent background.
numButtons, 0);
g_hImageListMedium = ImageList_Create(32, 32,
ILC_COLOR16 | ILC_MASK,
numButtons, 0);
g_hImageListLarge = ImageList_Create(48, 48,
ILC_COLOR16 | ILC_MASK,
numButtons, 0);
Add images to the lists:
HBITMAP hBitmapImageSmall = NULL, hBitmapImageMedium = NULL, hBitmapImageLarge = NULL;
hBitmapImageSmall = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 16, 16, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageSmall, NULL); //I am using the same image
hBitmapImageMedium = LoadImage(NULL, L"....YourBitmap.bmp", IMAGE_BITMAP, 32, 32, 0x10);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
ImageList_Add(g_hImageListSmall , hBitmapImageMedium , NULL);
The same with the large one(48x48)
Add g_hImageListSmall to the ToolBar for start:
//Set the image list.
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);
// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] =
{
{ 0, IDM_NEW, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL },
{ 1, IDM_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL},
{ 2, IDM_SAVE, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}
};
// Add buttons.
SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar
SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0);
That is the first step.
Write two functions:
void RemoveButtons(void){
int nCount, i;
// Remove all of the existing buttons, starting with the last one.
nCount = SendMessage(toolBarHwnd, TB_BUTTONCOUNT, 0, 0);
for(i = nCount - 1; i >= 0; i--){ SendMessage(toolBarHwnd, TB_DELETEBUTTON, i, 0); }
return;
}
enum{SMALL, MEDIUM, LARGE};
void AddButtons(int sizeButtons){
if(sizeButtons == SMALL){
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListSmall);
}
else if(sizeButtons == MEDIUM){
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListMedium);
}
else{
SendMessage(toolBarHwnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)g_hImageListLarge);
}
// Add buttons.
SendMessage(toolBarHwnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(toolBarHwnd, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar
SendMessage(toolBarHwnd, TB_AUTOSIZE, 0, 0);
return;
}
When ever you want to change the size of the buttons in ToolBar:
RemoveButtons();
AddButtons(LARGE); //or SMALL, MEDIUM
References:
How to Create Toolbars
How to Customize Toolbars
The common controls have been a major bug factory in Windows. Microsoft has had a great deal of trouble keeping them compatible across 6 major Windows releases and 10 versions of comctl32.dll. Particularly the visual style renderers have been a problem spot.
Core issue is that the api for them was set in stone 18 years ago with no reasonable way to make it work differently from the way it worked in their first release. Their code acquired a great many deal of appcompat hacks to achieve this. Such an hack will for example doctor a value that was returned by the previous version so that the client program has no idea, and doesn't need to know, that it is working with a very different version from the one it was tested against.
This has side-effects, the kind you'll discover when you use the controls in an unusual way that's very different from the way they are normally used by meat-and-potatoes Windows programs. Exactly like your scenario. Very high odds that you are battling internal state of the toolbar that you cannot see and doesn't get properly restored when you switch sizes. Quite undebuggable, that internal state isn't visible at all. Other than from the undesirable side-effects.
The solution is the one you already know. Recreate the toolbar from scratch. It can't go wrong that way.

Text formatting & font changing in hwnd windows

Me again guys, I've managed to learn up till now about most basics regarding window creation and message system, now I wanted to ask about formatting because I didn't manage to find anything about my particular case on google.
Here is what it looks like so far:
The boxes with 0s in them are Static windows since I didn't really get the Rect paint job. I also need it to be dynamic; the boxes will display an element from an int array that I'll transfer over to a wchar_t array for output.
Now is it possible to change the font, lets say increase it and make it bold? Or is it only possible using print text function?
Any help would be much appreciated since I'm really trying to make this "centered" so to speak.
EDIT:
Another question just so I don't make another post:
I just noticed that my stupid static windows don't update after I change the values in array I'm printing in them and repaint them. E.g. each zero is contained in wchar_t array[16][15]; and after I print this setup and change lets say array[13][0] = 'A'; nothing happens, is it due to Static window type or is it because of me being noobish and using MoveWindow to repaint them XD?
The windows message WM_SETFONT will do it. First there should be a font created, and then it is used in the parameter for WM_SETFONT.
When the font and window have been created, use
SendMessage(wnd, WM_SETFONT, (WPARAM)font, FALSE);
to set the default font for the window.
If you want to use a default windows font, you can create one like this:
HFONT font = NULL;
NONCLIENTMETRICS ncm;
memset(&ncm, 0, sizeof(NONCLIENTMETRICS));
ncm.cbSize = sizeof(NONCLIENTMETRICS);
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof(NONCLIENTMETRICS), &ncm, 0)) {
font = CreateFontIndirect(&ncm.lfMessageFont);
}
There are other default fonts in NONCLIENTMETRICS that you could use.
Of course you can also create a font from a typeface name and other information, but there is no guarantee that there is such a font on different systems.
HFONT CreateFont(
int nHeight, // height of font
int nWidth, // average character width
int nEscapement, // angle of escapement
int nOrientation, // base-line orientation angle
int fnWeight, // font weight
DWORD fdwItalic, // italic attribute option
DWORD fdwUnderline, // underline attribute option
DWORD fdwStrikeOut, // strikeout attribute option
DWORD fdwCharSet, // character set identifier
DWORD fdwOutputPrecision, // output precision
DWORD fdwClipPrecision, // clipping precision
DWORD fdwQuality, // output quality
DWORD fdwPitchAndFamily, // pitch and family
LPCTSTR lpszFace // typeface name
);