I have a simple dialog application in MFC with 2 combo boxes.
I am trying to use the dialogs paint handler to add a rectangle around the combos if they have no item selected:
void CFieldServiceGroupManagerDlg::OnPaint()
{
CPaintDC dc(this); // Device context for painting
// Lambda for drawing a red rectangle around a control
auto drawRedRect = [&](CWnd* pCtrl)
{
if (!pCtrl) return;
CRect rect;
pCtrl->GetWindowRect(&rect);
ScreenToClient(&rect);
// Inflate slightly to make it visible around the border
rect.InflateRect(2, 2);
CPen redPen(PS_SOLID, 2, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&redPen);
dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(&rect);
dc.SelectObject(pOldPen);
};
// Only draw rectangles if there’s no selection
if (m_cbGroupOverseer.GetCurSel() == CB_ERR)
drawRedRect(&m_cbGroupOverseer);
if (m_cbGroupAssistant.GetCurSel() == CB_ERR)
drawRedRect(&m_cbGroupAssistant);
}
I see no rectangle even though the code is called.
You would expect it to work, because it is described here:
Draw a rectangle around a control, C++ MFC
But it doesn’t.
The rectangle seems to have correct values:
I tried t do it the way in thecomments, but it is either flawed or my code is wrong.
I added two functions:
void CFieldServiceGroupManagerDlg::DrawRedRectAroundControl(CWnd* pCtrl)
{
if (!pCtrl || !::IsWindow(pCtrl->GetSafeHwnd()))
return;
CClientDC dc(this);
int nSavedDC = dc.SaveDC();
CRect rect;
pCtrl->GetWindowRect(&rect);
ScreenToClient(&rect);
rect.InflateRect(2, 2);
CPen redPen(PS_SOLID, 2, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&redPen);
CBrush* pOldBrush = static_cast<CBrush*>(dc.SelectStockObject(NULL_BRUSH));
dc.Rectangle(&rect);
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
dc.RestoreDC(nSavedDC);
}
void CFieldServiceGroupManagerDlg::HighlightUnselectedCombos(bool const checkOverseer, bool const checkAssistant)
{
if (checkOverseer && m_cbGroupOverseer.GetCurSel() == -1)
{
DrawRedRectAroundControl(&m_cbGroupOverseer);
}
if (checkAssistant && m_cbGroupAssistant.GetCurSel() == -1)
{
DrawRedRectAroundControl(&m_cbGroupAssistant);
}
}
In my selection change handlers for both commobos I respectively call:
HighlightUnselectedCombos(true, false); orHighlightUnselectedCombos(false, true);But the problem is if the rectangle is displayed (because no item is selected) and I select an item, the rectangle remains.
CWnd::InvalidateRect in my ON_CBN_SELCHANGE event handlers.PS_INSIDEFRAME pen style instead of PS_SOLID:The pen is solid. When this pen is used in any GDI drawing function that takes a bounding rectangle, the dimensions of the figure are shrunk so that it fits entirely in the bounding rectangle, taking into account the width of the pen. This applies only to geometric pens.
This is how I addressed the issue in the end:
CRect m_rcOverseerHighlightBounds;
CRect m_rcAssistantHighlightBounds;
CDialog::OnInitDialog:void CFieldServiceGroupManagerDlg::CalculateSizesWithBorderHilight()
{
auto getInflatedClientRect = [this](CWnd& wnd) -> CRect {
CRect rect;
wnd.GetWindowRect(&rect);
ScreenToClient(&rect);
rect.InflateRect(2, 2);
return rect;
};
m_rcOverseerHighlightBounds = getInflatedClientRect(m_cbGroupOverseer);
m_rcAssistantHighlightBounds = getInflatedClientRect(m_cbGroupAssistant);
}
CDialog::OnPaint handler:I can't find any official CDialog documentation for this function on the Microsoft website. 😵
void CFieldServiceGroupManagerDlg::OnPaint()
{
CPaintDC dc(this); // Device context for painting
// Lambda for drawing a red rectangle
auto drawRedRect = [&](const CRect& rect)
{
CPen redPen(PS_INSIDEFRAME, 2, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&redPen);
CBrush* pOldBrush = static_cast<CBrush*>(dc.SelectStockObject(NULL_BRUSH));
dc.Rectangle(&rect);
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
};
if (m_cbGroupOverseer.GetCurSel() == -1)
drawRedRect(m_rcOverseerHighlightBounds);
if (m_cbGroupAssistant.GetCurSel() == -1)
drawRedRect(m_rcAssistantHighlightBounds);
}
ON_CBN_SELCHANGE handlers. Example:InvalidateRect(m_rcAssistantHighlightBounds, m_cbGroupAssistant.GetCurSel() != CB_ERR);
Here are a couple of examples showing both combos with the red rectangle when my application is in dark-mode: