I'm having a problem setting fonts for static text - c++

My problem is that the font in the text won't show up as the font that I want it to be. I looked up the issue but I didn't see any solutions. The text is the "Sign In"
BOOL Account::OnInitDialog() {
CDialogEx::OnInitDialog();
CFont font;
VERIFY(font.CreatePointFont(160, _T("Arial")));
SignInStatic.SetFont(&font);
font.DeleteObject();
return TRUE;
}

When you call CWnd::SetFont(), the window you assigned the font to does not take ownership of the font. You are responsible to delete the font, but only when it is no longer in use.
As often, the documentation of the underlying Windows API, which is WM_SETFONT, provides more information than the MFC documentation:
The application should call the DeleteObject function to delete the
font when it is no longer needed; for example, after it destroys the
control.
As you are using the CFont class, you don't have to explicitly call DeleteObject(). CFont is a RAII class that automatically destroys its associated resource in its destructor.
All you have to care about is the scope of the CFont instance. Currently you create a local variable of CFont in the OnInitDialog method. Even when you remove the explicit DeleteObject call, the font will be destroyed when OnInitDialog returns and the window you assigned the font to now refers to an invalid font handle.
Solution
Declare an instance of CFont as a member variable of the Account class:
class Account : public CDialogEx
{
public:
// Some stuff
private:
CFont m_signInFont;
};
In OnInitDialog you have to use the member variable instead of the local variable and remove the DeleteObject call:
BOOL Account::OnInitDialog() {
CDialogEx::OnInitDialog();
VERIFY(m_signInFont.CreatePointFont(160, _T("Arial")));
SignInStatic.SetFont(&m_signInFont);
return TRUE;
}
Now the font object will exist for the whole livetime of the dialog, which includes its children. You could even assign it to other children, as needed.

Related

Getting a debug assertion error when calling GetDC() (MFC)

I'm making a MFC application using the Doc/View architecture with Visual Studio 2017, and for some reason I get that error whenever I call GetDC() inside this function:
void CDigitRecognizerView::ClearScreen(void)
{
CDC* dc;
dc = GetDC(); // debug assertion error here
CBrush brush;
brush.CreateSolidBrush(0xFFFFFF);
dc->SelectObject(&brush);
CRect rect;
GetWindowRect(&rect);
dc->FillRect(&rect, &brush);
CDigitRecognizerDoc* pDocument = GetDocument();
ReleaseDC(dc);
}
This is the message map macro defined in the app class:
BEGIN_MESSAGE_MAP(CDigitRecognizerApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, &CDigitRecognizerApp::OnAppAbout)
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
ON_COMMAND(ID_EDIT_CLEARSCREEN, CDigitRecognizerView::ClearScreen)
END_MESSAGE_MAP()
So whenever I select the "Clear Screen" option from the menu of the app, ClearScreen() gets called but I can't get the DC of the View, it crashes.
I have looked at the variables in the debugger and the window handle seems OK so I don't know really.
I am also wondering what other way I could call a function of the View class from the App class whenever I select a menu option because this doesn't seem to work.
How did you get the CWinApp message-map pointing to a CDigitRecognizerView function? I think the "wizard" wouldn't do this. Did you add the handler manually?
As for accessing Doc/View instances from the CWinApp class, there are some functions available:
GetFirstDocTemplatePosition() / GetNextDocTemplate(), members of CWinApp class. Alternatively you can simply store the pDocTemplate instance created in the InitInstance() function. Then call:
GetFirstDocPosition() / GetNextDoc(), members of the CDocTemplate class, and finally:
GetFirstViewPosition() / GenNextView(), members of the CDocument class
But this is normally not needed (the events could be handled in the Doc/View classes), unless you want to perform some operation(s) on all (or some of the) DocTemplate/Doc/View instances (which rather implies that you are developing an MDI application).

MFC: Does CWnd::SendMessage() only send messages to its class, or can other classes catch it?

Let's say I have a list component class called ListCtrl that derives from CWnd.
And let's say I also have a dialog class called DialogA that derives from CDialog.
DialogA uses ListCtrl to map it to a list component. For example,
void DialogA::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_CONTROL, listCtrl);
}
Where
ListCtrl listCtrl;
So if ListCtrl were to call the SendMessage(), can DialogA handle it?
If not, how can I have DialogA handle something that ListCtrl does.
Ultimately, I want DialogA to use a "copy" function of it's own when the ListCtrl right-click menu option for "Copy" is clicked, and prevent the copy function of ListCtrl from executing.
CWnd::SendMessage will send a message to the window wrapped by that CWnd derived class. So if you use SendMessage from your ListCtrl (which is a child window of your dialog), the dialog won't see it.
You either need to have the raw HWND of the dialog window and use the global SendMessage like:
::SendMessage(hWnd, WM_WHATEVER, 0, 0); // note the "::" scoping operator
Or you can possibly use the parent window of your list control (assuming that the dialog is its parent):
GetParent()->SendMessage(WM_WHATEVER, 0, 0);
In this last case, it would be more robust to ensure that GetParent() does not return NULL so perhaps:
CWnd *pParent = GetParent();
if (pParent != NULL)
pParent->SendMessage(WM_WHATEVER, 0, 0);
else
// error handling
Any window (and a control is child window) can send a message to any window in the same process.
The question is if that's a good idea for your use case.
Perhaps, if you're going to derive a class for the list control anyway, just pass it a pointer to an object it can call member functions on as appropriate for whatever it's doing.

dynamically created button is does not show in mfc

So I have this code
CButton details;
details.Create(_T("details"),WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON,CRect(120,100,100,30), this, 15000);
but it doesn't do anything(created button is not visible after creating it). What am I missing?
EDIT: The code is in a dialog based application's OnInitDialog function. What it should do is to display the button.
Your CButton is created with automatic storage duration. So it is destroyed when OnInitDialog returns. (Which is before the dialog is visible.) Make the CButton a member variable instead.
The values under CRect provided by you is incorrect, it must be CRect(120, 100, 220, 130).
this is depending to declaring CButton details;!
you must declare CButton details; as general instance(not local instance)
define your CButton details; instance as general, so your problem will solved!
bellow code work 100 percentage :
#define BBB 10000
CButton c;
void CThreadsDlg::OnBnClickedButton1()
{
bool a = c.Create(_T("new button"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, CRect(100, 100, 220, 230), this, BBB);
}
if c button created, a variable will be true.
My experience: nothing is shown if your dialog is inherited from CDHtmlDialog, but works OK with normal CDialog. So change the first line in OnInitDialog()
//CDHtmlDialog::OnInitDialog();
CDialog::OnInitDialog();
and of course, the button variable should be global or class member, not local.

Is it necessary to explicitly restore a control font when its parent window is destroyed?

I use the following code in my windows create method
HANDLE hFont = ::GetStockObject(DEFAULT_GUI_FONT);
m_InfoTab.SendMessage(WM_SETFONT, (WPARAM)hFont);
m_InfoTab is a standard Windows/MFC tab control. Is it necessary to save the original font and restore it when the parent window is destroyed?
It is not required to restore the original font. You are however responsible for managing the font object yourself, i.e. deleting it when it is no longer used. The documentation for WM_SETFONT is fairly explicit here:
The application should call the DeleteObject function to delete the font when it is no longer needed; for example, after it destroys the control.
In this particular case you will not run into any problems since you retrieved the font object through a call to GetStockObject(). These objects are controlled by the system and it is not required to call DeleteObject on them (although it is not harmful either).
Whether or not your application leaks GDI handles can easily be verified using Task Manager. Go to the Processes tab, select View -> Select Columns... and tick GDI Objects. With the monitoring in place change your code and install a timer using SetTimer() with uElapsed = 1000 (once a second). Add an OnTimer handler with the following code:
void CMyDialog::OnTimer(UINT_PTR nIDEvent)
{
// Retrieve the system font
HFONT hFontSystem = (HFONT)GetStockObject( DEFAULT_GUI_FONT );
LOGFONT lfSystem = { 0 };
GetObject( hFontSystem, sizeof( lfSystem ), &lfSystem );
// And construct and identical font object
HFONT hFontNew = CreateFontIndirect( &lfSystem );
// This will leak the font object
m_InfoTab.SendMessage( WM_SETFONT, (WPARAM)hFontNew );
__super::OnTimer(nIDEvent);
}
Now open Task Manager, start the application and watch the GDI Objects count increase by 1 each second.
No, the window in question (m_infoTab) is responsible for managing its fonts, and freeing the old one if necessary.

How can I change the default CDialog font for a non-modal dialog?

It is necessary to switch off the "ClearType" property of the default font for all dialog controls. It is possible to do that for one control by setting
logfont.lfQuality = ANTIALIASED_QUALITY
There are a lot of suggestion how to do the same for modal dialogs (http://neelaakash.wordpress.com/2007/12/31/change-default-dialog-font-of-cdialog/ and others), but that should be done for non-modal dialogs (are instantiated with new and Create(...) methods). I've tried to do that myself:
Override 'Create' method, and modify dialog template:
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
CDialogTemplate dlt;
int nResult;
// load dialog template
if (!dlt.Load(MAKEINTRESOURCE(nIDTemplate))) return -1;
// set your own font, for example “Arial”, 10 pts.
dlt.SetFont(L"Arial", 12);
// get pointer to the modified dialog template
LPSTR pdata = (LPSTR)GlobalLock(dlt.m_hTemplate);
// let MFC know that you are using your own template
m_lpszTemplateName = NULL;
InitModalIndirect(pdata);
// display dialog box
nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// unlock memory object
GlobalUnlock(dlt.m_hTemplate);
return nResult ;
}
Seems like this method do nothing (it is called, I've checked that with putting break-point inside).
I've tried to call
nResult = CActivationChildDialog::Create(NULL, pParentWnd);
...but got a lot of ASSERTs.
I've also tried to override the 'OnSetFont' method:
void CActivationChildDialogLicenseInfo::OnSetFont(CFont *pFont)
{
CActivationChildDialog::OnSetFont(pFont);
LOGFONT logfont;
pFont->GetLogFont(&logfont);
LOGFONT logfont2=logfont;
pFont->DeleteObject();
logfont2.lfItalic = true;
logfont2.lfQuality = ANTIALIASED_QUALITY;
pFont->CreateFontIndirect(&logfont2);
}
That causes ASSERT during run-time and resulted in a VERY big font being used (lost default font settings, doesn't accept new specified settings)... I don't know why.
Please advise, how can I change a default dialog font that will be "inherited" by all dialog controls?
Thank you very much.
First off: the simple, reliable way to do this is to create the dialog and then send WM_SETFONT (or call SetFont()) to the dialog and each control in it. I'll show you how to do that below, but first, here's why the two strategies you've already tried didn't (and can't) work:
Modifying the dialog template
First off, you should be calling CDialog::CreateIndirect() if you wish to use a dialog template that you've already loaded.
But don't bother. The dialog template contains only the face name and point size - it does not allow you to specify other LOGFONT values such as lfQuality. If it did, you could simply specify that in your resource definition and avoid writing any runtime code at all!
Intercepting WM_SETFONT
In theory, you could make this work. But it's not practical. Your code has several problems: first off, you'd have to intercept this message for every child control in order for it to do anything useful: the dialog itself probably doesn't render any text. But worse, you're passing the original font to the base class (which hands it to the default window procedure, which stores it internally for later use) and then immediately destroying it - this means the dialog (and everything else using that font, including all of the child controls) will be trying to draw text using a bogus font, and reverting to the default font as a result. Finally, you're creating a new font attached to a temporary object (pFont) created and destroyed by MFC - internally, the CFont object you're working with will be detached from the font handle and destroyed, leaking a handle to a font object that nothing uses.
Leaky abstractions: a note on HFONT and CFont
HFONT is a handle type that Windows uses to represent a font object. Like most of GDI, there are specific functions for creating fonts, and the general-purpose DeleteObject() function for destroying them.
CFont is a light-weight wrapper class for HFONTs. A CFont instance can be attached and detached from an existing HFONT, or used to create a new one. If a CFont instance is still attached to a HFONT when its deconstructor executes, it will call DeleteObject() to destroy the underlying object. Internally, MFC utilizes temporary CFont instances that are attached and detached from HFONTs when calling various message handlers (such as OnSetFont). It's worth remembering that internally, Windows knows nothing about CFont, and a single HFONT may belong to 0 or more CFont instances at any given point in time.
A note on fonts and WM_SETFONT
When you create a new font - whether or not it is wrapped in a CFont object - you are the owner of that font, and it is your responsibility to destroy it once you are finished using it. Passing it to WM_SETFONT (CWnd::SetFont()) doesn't change ownership! This is actually quite useful, as it allows you to pass the same font to multiple windows without worrying about which one will destroy it - you're still the owner, and so you can (and must) destroy it yourself (once there are no windows still using it).
Finally - how to quickly create and set a font on a dialog and all its children
So you should now have enough background to understand the necessary steps:
Create the dialog
Create the desired font
Pass the font to the dialog and its children (by either sending WM_SETFONT messages, or by calling CWnd::SetFont... which itself send a WM_SETFONT message).
When the dialog is destroyed, also destroy your font.
Example
// define this as a class member - class destructor then handles step four!
CFont m_nonCleartypeFont;
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
// step one: create dialog normally
BOOL nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// step two: create custom font
// relying on destructor to destroy font once we're done with it
// so be careful to only create it once!
if ( NULL == m_nonCleartypeFont.m_hObject )
{
CFont* pOriginalFont = GetFont(); // use template font as... template!
// pull information from original font
LOGFONT logfont;
pOriginalFont->GetLogFont(&logfont);
// make font adjustments:
// specify italics
logfont.lfItalic = true;
// and non-cleartype antialiasing
logfont.lfQuality = ANTIALIASED_QUALITY;
// create our font based on adjusted information
m_nonCleartypeFont.CreateFontIndirect(&logfont);
}
// step three: set our custom font on the dialog and all children
SetFont(&m_nonCleartypeFont, FALSE);
// Send message to quickly set this font for all children.
// See documentation for SendMessageToDescendants()
// - this is actually the example given!
SendMessageToDescendants(WM_SETFONT,
(WPARAM)m_nonCleartypeFont.m_hObject,
MAKELONG(FALSE, 0),
FALSE);
return nResult;
}