What is the correct way to use CFontDialog common dialog? - mfc

I recently change the relationship between the window extent and the view port of an MFC application, and, since then, every time I change my application font size, the selected font become supper gigantic even if I chose the smallest of all font size.
(EDIT: I noticed that CFontDialog::GetSize() return a size that is ten times the size chosen in the dialog. Is that a usual behaviour ? If not what could make the dialogue return such a value? Although I am not sure, but it seem this multiplication of size seem to be my problem. How do I get CFontDialog::GetSize() to return the actual selected size, if that indeed is the problem?)
What am I doing wrong? What is the correct way to use CFontDialog ?
Shown next is a snippet of the font changing code:
CClientDC dc(pView);
pView->OnPrepareDC(&dc)
pLastFont = pLastText->GetFont();
oldColor = pLastText->GetColor();
LOGFONT logFont = (LOGFONT) (*pLastFont);
CFontDialog fontDialog(&logFont);
CSize szPrevSize;
//Some missing codes
MyFigure *pMyFigure;
if(dynamic_cast<MyTextFigure*>(pMyFigure) != NULL)
{
if(dynamic_cast<MyTextBoxFigure*>(pLastText) != NULL)
{
MyTextBoxFigure *pTextBox = dynamic_cast<MyTextBoxFigure*>(pLastText);
szPrevSize = pTextBox->GetTextSize();
}
}
else if(dynamic_cast<MyTableFigure*>(pMyFigure) != NULL)
{
MyTableFigure *pTableFigure = dynamic_cast<MyTableFigure*>(pMyFigure);
TCell *pCell = (TCell *)*pTableFigure;
szPrevSize = pCell->GetTextSize();
}
fontDialog.m_cf.rgbColors = (COLORREF) oldColor;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&logFont);
MyFont newFont = (MyFont) logFont;
MyColor newColor = (MyColor) fontDialog.GetColor();
pMyFigure->SetFont(newFont, &dc);
pMyFigure->SetColor(newColor);
}
Please note that MyFont is a wrapper around the LOGFONT structure and has an operator that enable cast to LOGFONT. Also note that MyFigure class has a MyFont data member that is set by the member function SetFont.
The following code shows how the relationship between the view port and the window extent was set.
void CDisplayView::OnInitialUpdate()
{
CRect rcClient;
GetClientRect(&rcClient);
CClientDC dc(this);
dc.SetMapMode(MM_ISOTROPIC);
CSize szWindow(m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(HORZRES),m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(VERTRES));
CSize szViewport(dc.GetDeviceCaps(HORZRES),dc.GetDeviceCaps(VERTRES));
dc.SetWindowExt(szWindow);
dc.SetViewportExt(szViewport);
dc.DPtoLP(&rcClient);
//And so on
}
ZoomRatio is 1, an SCALE_RATIO is 1.2
The assignment operator is:
MyFont& MyFont::operator=(const MyFont& font)
{
if (this != &font)
{
m_logFont = font.m_logFont;
}
return *this;
}
P.S.
The summary of the code is:
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
}
The focus here is the LOGFONT structure.
The user selected value is gotten with old logfont then assigned to new logfont.
For debugging purposes, I retrieved the size of the selected font and was shocked to find that it is ten times the size that I selected after lunching the CFontDialog for use in my application i.e
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
int iFontSize = fontDialog.GetSize();
//when I selected a font size of 10 from the dialog, what I get here in my code is 100.
}

This is how I do it:
void CExportSettingsDlg::OnBtnSelectFont()
{
CClientDC dc(this);
m_plfCurrentFont->lfHeight =
-MulDiv(m_plfCurrentFont->lfHeight, dc.GetDeviceCaps(LOGPIXELSY), 72);
CMyFontDialog dlgFont( m_plfCurrentFont );
dlgFont.m_cf.Flags |= CF_NOSCRIPTSEL;
dlgFont.m_cf.rgbColors = m_crCurrentFontColour;
if( dlgFont.DoModal() == IDOK)
{
m_plfCurrentFont->lfHeight = -(dlgFont.GetSize()/10);
// We must retrieve the font colour into the correct variable.
// The logfont is already correct because we passed in the
// logfont pointer into the font dialogue.
switch( m_iFontType )
{
case FONT_TITLE:
m_sSettings.crTitleFontColour = dlgFont.GetColor();
break;
case FONT_HEADING:
m_sSettings.crHeadingFontColour = dlgFont.GetColor();
break;
case FONT_GENERAL:
m_sSettings.crGeneralFontColour = dlgFont.GetColor();
break;
case FONT_EVENT:
m_sSettings.crEventFontColour = dlgFont.GetColor();
break;
case FONT_NOTES:
m_sSettings.crNotesFontColour = dlgFont.GetColor();
break;
}
// Update display
SetSampleFont();
// Html Preview
UpdatePreviewHtml();
}
}
If it does not answer your question I will remove the answer. Notice how I set the font size at the start of the code.
If you read up on the MSDN for GetSize it states:
The font's size, in tenths of a point

Related

CUSTOMDRAW in Win32 Header not appropriately drawing outside columns area

I am currently subclassing a ListView control to support custom color schemes - especially Dark Mode themes.
So first, I changed the ListView and associated Header with:
SetWindowTheme(hwndListView, isDarkModeEnabled() ? L"Explorer" : nullptr, nullptr);
SetWindowTheme(hwndListViewHeader, isDarkModeEnabled() ? L"ItemsView" : nullptr, nullptr);
I can set the ListView background color to any custom color I choose, while answering to the WM_THEMECHANGED message and doing a ListView_SetBkColor() macro.
Unfortunately, it seems I can't do it with the associated Header Control, I must resort to Custom Draw. So I did. I subclassed the ListView, because this is the window handle that will receive WM_NOTIFY messages from the Header control, and when responding to NM_CUSTOMDRAW message, my code looks like this:
case WM_NOTIFY:
{
LPNMHDR nmhdr = reinterpret_cast<LPNMHDR>(lParam);
if (nmhdr->code == NM_CUSTOMDRAW)
{
LPNMCUSTOMDRAW lpcd = reinterpret_cast<LPNMCUSTOMDRAW>(lParam);
switch (lpcd->dwDrawStage)
{
case CDDS_PREPAINT:
{
if (!PluginDarkMode::isEnabled())
return CDRF_DODEFAULT;
FillRect(lpcd->hdc, &lpcd->rc, getDarkerBackgroundBrush());
return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
}
case CDDS_ITEMPREPAINT:
{
return DrawListViewHeaderItem(lParam); // This function draws the item entirely and then returns CDRF_SKIPDEFAULT.
}
case CDDS_POSTPAINT:
// Calculates the undrawn border outside columns
HWND hHeader = lpcd->hdr.hwndFrom;
int count = static_cast<int>(Header_GetItemCount(hHeader));
int colsWidth = 0;
RECT wRc = {};
for (int i = 0; i < count; i++)
{
Header_GetItemRect(hHeader, i, &wRc);
colsWidth += wRc.right - wRc.left;
}
RECT clientRect;
GetClientRect(hHeader, &clientRect);
if (clientRect.right > (colsWidth + 1))
{
clientRect.left = colsWidth + 1;
HDC hdc = GetDC(hHeader);
FillRect(hdc, &clientRect, getDarkerBackgroundBrush());
ReleaseDC(hHeader, hdc);
}
return CDRF_SKIPDEFAULT;
}
}
break;
}
Now, my problem is that, although this seems fine, my control will always end up with a black rectangle outside the column's area (the empty space where no column exists). And this effect persists up until I move the mouse outside the header control area.
For example:
And when the mouse leaves the area:
Any ideas of how to solve this?

If I have five Win32 static controls, how can I set one of them with a specific foreground color? [duplicate]

This question already has answers here:
Set static text color Win32
(1 answer)
In Win32, how can the colour of STATIC text be changed?
(2 answers)
Closed 4 years ago.
As far as I can tell, this is not a duplicate question because it's dealing with a collection of static (label) controls. I want to set a foreground color to a specific one that I call in my thin OOP library.
I call a static control a "Label" in my library. This is how I set the color:
void Label::setForeColor(const BYTE red, const BYTE green, const BYTE blue)
{
m_foreColor = RGB(red, green, blue);
}
This just sets a COLORRREF that the control should have. I'm having trouble finding a solution to send a message for that specific static control without affecting others.
Many say to use WM_CTLCOLORSTATIC, but I already am for transparency of controls:
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
{
char class_Name[100];
WNDCLASS lpcls{};
SetBkMode((HDC)wParam, TRANSPARENT);
// SetTextColor((HDC)wParam, RGB(0, 0, 255)); This works and can set all statics as blue, but I need just one control blue.
GetClassName(hWnd, class_Name, 100);
GetClassInfo(frm.getInstance(), class_Name, &lpcls);
return (LRESULT)lpcls.hbrBackground;
}
But here's the issue: I may have more than one label on a window, so this goes beyond than just setting a single label as most examples show. There may be 5 labels with 5 different colors.
This is the top layer:
Label lblName("This is a label.", 330, 303);
lblName.setVisible(true);
lblName.setForeColor(0, 0, 255);
lblName.setFont("Garamond", 24, false, false, false);
lblName.OnMouseOver(lblName_onMouseOver);
Ideally, I would like to set the color in my setFont() function by sending a message.
bool Label::setFont(const std::string &fontName, const int size, const bool bold,
const bool italic, const bool underlined)
{
DWORD dwItalic;
DWORD dwBold;
DWORD dwUnderlined;
SIZE linkSize;
HFONT old_font;
dwItalic = (italic) ? TRUE : FALSE;
dwBold = (bold) ? FW_BOLD : FW_DONTCARE;
dwUnderlined = (underlined) ? TRUE : FALSE;
m_font = CreateFont(size, 0, 0, 0, dwBold, dwItalic, dwUnderlined, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, fontName.c_str());
SendMessage(m_handle, WM_SETFONT, WPARAM(m_font), TRUE);
// Calculate the correct width and height size
HDC hDC = GetDC(m_handle);
old_font = SelectFont(hDC, m_font);
GetTextExtentPoint32(hDC, m_text.c_str(), (int)m_text.length(), &linkSize);
setSize(linkSize.cx, size);
DeleteFont(old_font);
ReleaseDC(m_handle, hDC);
return true;
}
Finally, this is how I retrieve my labels that I'm interested in. I wonder if I need to set the font color similarly.
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
X3D::Windows::Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
{
lbl->setHovering(true);
lbl->invokeOnMouseHover();
}
else
{
lbl->setHovering(false);
}
break;
}
Overall question: If I have five Win32 static controls, how can I set one of them with a specific foreground color?
Update:
This is my current code. Assert() is barking at me: Expression: map/set iterator not dereferencable
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
{
char class_Name[100];
WNDCLASS lpcls{};
SetBkMode((HDC)wParam, TRANSPARENT);
GetClassName(hWnd, class_Name, 100);
GetClassInfo(frm.getInstance(), class_Name, &lpcls);
for (int i = 0; i < frm.getControlCount(); i++)
{
if (frm.getControls().find(i)->second->getHandle() == (HWND)lParam)
{
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(i)->second;
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl != NULL)
{
SetTextColor((HDC)wParam, lbl->getForeColor());
break;
}
}
}
return (LRESULT)lpcls.hbrBackground;
}
Update:
App runs, but the font color isn't updating. Something wrong in this?
int id = GetDlgCtrlID((HWND)lParam);
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().at(id);
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl != NULL)
{
SetTextColor((HDC)wParam, lbl->getForeColor());
break;
}
Tried this too:
int id = GetDlgCtrlID((HWND)lParam);
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(id)->second;
if (ctrl == NULL)
return 0;
If all of that looks correct, I'll just have to debug it tomorrow.
WM_CTLCOLORSTATIC is sent multiple times, you can choose to take different actions depending on which child window is generating it.
As shown on MSDN, the lParam that arrives with the message is the HWND for the control. Compare it directly, or GetDlgCtrlID() if you want to work with dialog item IDs.
No need to do anything special in the subclass wndproc, because WM_CTLCOLORSTATIC is sent to the parent window.
But there is no message to send to change the color or font, because the STATIC window class doesn't use a permanent device context per label (which is a good thing, because many programs have a lot of labels). So the text configuration like color and font need to be reapplied to the DC each time it is borrowed from the pool. WM_CTLCOLORSTATIC is sent to the parent window at the ideal time to do this.
If your setter for the color is called, be sure to use InvalidateRect() to trigger a repaint. That repaint will send WM_CTLCOLORSTATIC again, giving you the opportunity to act on your updated color.

How to block resizing after a double-click on a dialog window's top or bottom edges in Windows 10?

I'm coding a customized popup window with C++ using Win32. The condition for this popup window is that it can be only resized from the bottom down. The following is the implementation of such restriction:
RECT rcInitialWindowRectangle = {0};
//The dialog has WS_THICKFRAME style
LRESULT CALLBACK DlgWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_INITDIALOG:
{
//Set minimum window size
::GetWindowRect(hDlg, &rcInitialWindowRectangle);
}
break;
case WM_SIZING:
{
//Restrict sizing on all sides but bottom
if(wParam != WMSZ_BOTTOM)
{
RECT* pRcWnd = (RECT*)lParam;
//Preserve all sides but bottom
int b = pRcWnd->bottom;
*pRcWnd = rcInitialWindowRectangle;
pRcWnd->bottom = b;
return TRUE;
}
}
break;
case WM_GETMINMAXINFO:
{
//The following is needed to restrict minimum window size
int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
if(w != 0)
{
MINMAXINFO* pMMI = (MINMAXINFO*)lParam;
pMMI->ptMinTrackSize.x = w;
pMMI->ptMinTrackSize.y = rcInitialWindowRectangle.bottom - rcInitialWindowRectangle.top;
pMMI->ptMaxTrackSize.x = w;
}
}
break;
case WM_NCHITTEST:
{
//The following is needed to display correct cursor for resizing
POINT pnt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
RECT rcWnd;
::GetWindowRect(hDlg, &rcWnd);
//L, T, R, B
RECT rcBtm = {rcInitialWindowRectangle.left,
rcWnd.bottom - 16, //Some arbitrary border
rcInitialWindowRectangle.right,
rcWnd.bottom};
return ::PtInRect(&rcBtm, pnt) ? HTNOWHERE : HTBORDER;
}
break;
return 0;
}
So this works except one thing. On Windows 10, there's evidently a new feature -- when someone double-clicks on the bottom (or top) edge of a window -- here's an example with Notepad so that you can try:
that window is resized (stretched) to the top and bottom of the screen (akin to maximization, but only vertically.)
So my question is how do I block this double-click resizing? (In my case the top of the popup window should not move.)
PS. My first instinct was to block all double-clicks on the window's edge, but then I thought that maybe there's a less barbaric way to achieve this?
You are already handling WM_NCHITTEST. Handle WM_NCLBUTTONDBLCLK and don't forward to DefWindowProc unless the hit test (in wParam) indicates the lower frame.
here's the workaround that seems to work for me. It is not really about blocking the double-clicks. (Apart from subclassing the WndProc of a dialog box, which I haven't tried, I failed to block that double-click effect in DlgProc alone.) My workaround presented below is to achor the top of the popup window and let it drop down to the bottom of the screen, if the user who double-clicks the bottom border wants similar stuff to happen.
Since this does not answer my original question, I won't mark it as such.
//Add this case statement to my original code
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pWP = (WINDOWPOS*)lParam;
if(!(pWP->flags & (SWP_NOMOVE | SWP_NOSIZE)))
{
int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
if(w > 0)
{
//Anchor the top of the popup window
pWP->x = rcInitialWindowRectangle.left;
pWP->y = rcInitialWindowRectangle.top;
pWP->cx = w;
int h = pWP->cy;
//Make sure that the height fits the screen
POINT pnt = {pWP->x, pWP->y};
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
if(::GetMonitorInfo(::MonitorFromPoint(pnt, MONITOR_DEFAULTTONEAREST), &mi))
{
if(pWP->y + h > mi.rcWork.bottom)
{
int nMinDefaultH = rcInitialWindowRectangle.bottom -
rcInitialWindowRectangle.top;
int nAh = mi.rcWork.bottom - y;
if(nAh >= nMinDefaultH)
h = nAh;
else
h = nMinDefaultH;
}
}
pWP->cy = h;
}
}
}
break;

how to attach scrollbar to a dialog

I have to find the correct way to attach a scrollbar to a set of images that are created dynamically by the application.
In my code I create the scrollbar but it is not working properly. The main window does not scroll to view all the images.
int currentLength = iImage * (WIDTH + SPACER);
picName.Format(_T("Image %d"),iImage);
CPoint topLeft(currentLength,0);
CPoint bottomRigth(currentLength + (WIDTH), HEIGHT);
CRect miniCRect(topLeft, bottomRigth);
Miniature[iImage] = new CStatic();
Miniature[iImage]->Create(picName, WS_CHILD|WS_VISIBLE|SS_BITMAP, miniCRect, this);
if((bottomRigth.x > 500) && (currentLength <= 500))
{
//creo la scrool bar
CPoint ptnrigin(0,210);
CPoint endptn(bottomRigth.x,230);
CRect workingArea(ptnrigin,endptn);
cs.Create(WS_VISIBLE,workingArea,this,0);
cs.EnableScrollBar(ESB_ENABLE_BOTH);
SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_ALL;
info.nMin = 1;
info.nMax = 10;
info.nPage = 2;
info.nPos = 5;
info.nTrackPos = 5;
cs.SetScrollInfo(&info);
}
HDC hDCScreen_mini = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
HDC hDCMem_mini = ::CreateCompatibleDC(hDCScreen_mini);
// create a compatible bitmap and select it in the memory DC
HBITMAP hBitmap_mini = ::CreateCompatibleBitmap(hDCScreen_mini, miniCRect.Width(), miniCRect.Height());
HBITMAP hBmpOld_mini = (HBITMAP)::SelectObject(hDCMem_mini, hBitmap_mini);
BitBlt(hDCMem_mini, 0, 0, desktopRect.Width(), desktopRect.Height(), hDCScreen_mini, desktopRect.left, desktopRect.top, dwRop);
Miniature[iImage]->SetBitmap(hBitmap_mini);
Invalidate();
// restore the memory DC and perform cleanup
SelectObject(hDCMem_mini, hBmpOld_mini);
DeleteDC(hDCMem_mini);
DeleteDC(hDCScreen_mini);
//end capture
Can someone help me?
A scrollbar does not scroll your window or its contents. It only provides input from the user to code that you must write to reposition the images. Such code typically uses ScrollWindow to move the visible portion, followed by adding the newly-visible portion in WM_PAINT.
A different approach (that does do the scrolling for you) would be to put the images in an owner-drawn list box.

Possible GDI Leak when ReDrawing CButton's

I have a problem when re-sizing a CControlBar that contains a few CButtons. After re-sizing for a while, the whole display breaks and stops painting correctly.
Based on what I can find about these kinds of issues, I am thinking that I am leaking GDI objects when the Button's are re-drawn.
Below is my DrawItem method. I keep finding different methods to use online but I still get the problem.
Please can someone help me pinpoint exactly what I need to change and how.
void CNJABarFolderButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
UINT uState=DFCS_BUTTONPUSH;
if( lpDrawItemStruct->itemState & ODS_SELECTED )
{
uState|=DFCS_PUSHED;
}
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
dc.DrawFrameControl(&lpDrawItemStruct->rcItem,DFC_BUTTON,uState);
if( !IsWindowEnabled() )
{
dc.SetTextColor(::GetSysColor(COLOR_3DSHADOW));
}
CString csText;
GetWindowText(csText);
if (m_iDisplayType != 2 || !m_hIcon)
{
CFont font;
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = m_iFontSize;
strcpy(lf.lfFaceName, "Tahoma Bold");
VERIFY(font.CreateFontIndirect(&lf));
CFont* def_font = dc.SelectObject(&font);
RECT buttonRect = lpDrawItemStruct->rcItem;
buttonRect.left += 10;
buttonRect.right += 10;
if (m_iDisplayType != 1 || !m_hIcon) //text & Icon
{
buttonRect.left += 30;
buttonRect.right += 30;
}
dc.DrawText(csText,&buttonRect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
dc.SelectObject(def_font);
font.DeleteObject();
}
if (m_hIcon && m_iDisplayType != 1)
{
CSize czText = dc.GetTextExtent(csText);
dc.DrawIcon(0,0,m_hIcon);
}
dc.Detach();
}