Setting individual items in a CListBox as bold with MFC - mfc

I stumbled over this article:
http://asg.unige.ch/Past/fuentes/Mfc/HowTo_44.html
So, I reproduced the class in my project:
// FontStyleListBox.cpp : implementation file
//
#include "stdafx.h"
#include "Meeting Schedule Assistant.h"
#include "FontStyleListBox.h"
// CFontStyleListBox
IMPLEMENT_DYNAMIC(CFontStyleListBox, CListBox)
CFontStyleListBox::CFontStyleListBox()
{
}
CFontStyleListBox::~CFontStyleListBox()
{
}
BEGIN_MESSAGE_MAP(CFontStyleListBox, CListBox)
ON_WM_DRAWITEM_REFLECT()
END_MESSAGE_MAP()
// CFontStyleListBox message handlers
void CFontStyleListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect;
// Draw the colored rectangle portion
rect.CopyRect(&lpDrawItemStruct->rcItem);
pDC->SetBkMode(TRANSPARENT);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
pDC->FillSolidRect(rect, GetSysColor(COLOR_HIGHLIGHT));
pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
pDC->FillSolidRect(rect, GetSysColor(COLOR_WINDOW));
pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
}
if ((int)(lpDrawItemStruct->itemID) >= 0) // Valid ID
{
CString sText;
int nIndex;
CFont newFont;
CFont *pOldFont;
nIndex = lpDrawItemStruct->itemID;
GetText(nIndex, sText);
FONTSTYLE fontStyle = (FONTSTYLE)GetItemData(nIndex);
// To avoid unnecessary processing
if (fontStyle == NORMAL) {
pDC->DrawText(sText, rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
return;
}
LOGFONT logFont;
CFont *pFont = GetFont();
pFont->GetLogFont(&logFont);
switch (fontStyle)
{
//case NORMAL: logFont.lfWeight = FW_NORMAL;
// break;
case BOLD: logFont.lfWeight = FW_BOLD;
break;
case ITALIC: logFont.lfItalic = TRUE;
break;
}
newFont.CreatePointFontIndirect(&logFont);
pOldFont = pDC->SelectObject(&newFont);
pDC->DrawText(sText, rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
pDC->SelectObject(pOldFont);
newFont.DeleteObject();
}
}
int CFontStyleListBox::GetFontStyle(int nIndex)
{
return (FONTSTYLE)GetItemData(nIndex);
}
void CFontStyleListBox::SetFontStyle(int nIndex, FONTSTYLE fontStyle)
{
SetItemData(nIndex, (DWORD)fontStyle);
Invalidate();
}
I then used it in my application. I set the properties correctly for ownerdraw etc. but here is the results:
The bold entry is the last one. Why is it not rendering correctly?
How do I fix this and / or are there newer ways to get this done?

You just need to CFont::CreateFontIndirect use. Using CFont::CreatePointFontIndirect causes a conversion from the font points into physical points. You don't need that.
Also create the font only once. You may create it on demand in DrawItem. Just create a member in your subclassed CListBox...

Related

Set position of MFC dialog-based application window

I have a CDialog based MFC application. I save the window's current position when the program ends. When the program starts, I want to restore the previous position. I'm currently trying to do this in OnInitDialog(), however, the program asserts when I call SetWindowPos() from within OnInitDialog(). My call to SetWindowPos() is similar to:
SetWindowPos(&CWnd::wndTop, 10, 10, 500, 500, SWP_NOREDRAW | SWP_NOZORDER);
The assertion relates to a null m_hWnd handle.
Is this the correct place to re-position a dialog-based application's window?
Any ideas on why I'm asserting?
In the process of providing more information to my initial question, I discovered that calling SetWindowPos() passed messages to toolbars within the dialog that hadn't been created, yet. I moved the SetWindowPos() to the end of OnInitiDialog() and it worked.
Thanks for the encouragement, #zett42.
This is how I do it:
#include "stdafx.h"
#include "resource.h"
#include "ResizingDialog.h"
IMPLEMENT_DYNAMIC(CResizingDialog, CDialogEx)
CResizingDialog::CResizingDialog(const CString& strWindowID, UINT nIDTemplate, CWnd* pParent /* nullptr */, bool bOnlyStorePosition /* false */)
: CDialogEx(nIDTemplate, pParent)
, m_strWindowID(strWindowID)
, m_bOnlyStorePosition(bOnlyStorePosition)
, m_bDoNotShowResizeIcon(false)
{
m_rcInit.SetRect(0, 0, 0, 0);
}
CResizingDialog::~CResizingDialog()
= default;
void CResizingDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CResizingDialog, CDialogEx)
ON_WM_GETMINMAXINFO()
ON_WM_DESTROY()
ON_WM_PAINT()
ON_WM_NCHITTEST()
ON_WM_SIZE()
END_MESSAGE_MAP()
BOOL CResizingDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Save Initial window size to m_rcInit
GetWindowRect(&m_rcInit);
//if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
//InitialiseResizeIcon(m_bmpResize, m_lblResize, this);
RestoreWindowPosition(m_strWindowID, this, true);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CResizingDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// Set the minimum window size to initial size.
lpMMI->ptMinTrackSize.x = m_rcInit.Width();
lpMMI->ptMinTrackSize.y = m_rcInit.Height();
CDialogEx::OnGetMinMaxInfo(lpMMI);
}
void CResizingDialog::RestoreWindowPosition(CString strWindow, CWnd* pWindow, bool bOverrideState)
{
int max_x, max_y;
RECT rtWindow;
if (pWindow == nullptr)
return;
// Only restore if there is a previously saved position
if ((rtWindow.top = AfxGetApp()->GetProfileInt(strWindow, _T("Top"), -1)) != -1 &&
(rtWindow.left = AfxGetApp()->GetProfileInt(strWindow, _T("Left"), -1)) != -1 &&
(rtWindow.bottom = AfxGetApp()->GetProfileInt(strWindow, _T("Bottom"), -1)) != -1 &&
(rtWindow.right = AfxGetApp()->GetProfileInt(strWindow, _T("Right"), -1)))
{
max_x = rtWindow.right - rtWindow.left;
max_y = rtWindow.bottom - rtWindow.top;
// Get a handle to the monitor
HMONITOR hMonitor = ::MonitorFromPoint(
CPoint(rtWindow.left, rtWindow.top), MONITOR_DEFAULTTONEAREST);
// Get the monitor info
MONITORINFO monInfo;
monInfo.cbSize = sizeof(MONITORINFO);
if (::GetMonitorInfo(hMonitor, &monInfo) == 0)
AfxMessageBox(_T("GetMonitorInfo failed"));
else
{
// Adjust for work area
rtWindow.left += monInfo.rcWork.left - monInfo.rcMonitor.left;
rtWindow.top += monInfo.rcWork.top - monInfo.rcMonitor.top;
// Ensure top left point is on screen
if (CRect(monInfo.rcWork).PtInRect(CPoint(rtWindow.left, rtWindow.top)) == FALSE)
{
rtWindow.left = monInfo.rcWork.left;
rtWindow.top = monInfo.rcWork.top;
}
rtWindow.right = rtWindow.left + max_x;
rtWindow.bottom = rtWindow.top + max_y;
// Restore window size
pWindow->MoveWindow(&rtWindow, FALSE);
}
if (bOverrideState)
{
// Let us override by restoring the window state
int iState = AfxGetApp()->GetProfileInt(strWindow, _T("ShowCmd"), SW_SHOWNORMAL);
pWindow->ShowWindow(iState);
}
}
}
void CResizingDialog::SaveWindowPosition(CString strWindow, CWnd* pWindow)
{
WINDOWPLACEMENT wp;
if (pWindow == nullptr)
return;
pWindow->GetWindowPlacement(&wp);
// Commit to registry
AfxGetApp()->WriteProfileInt(strWindow, _T("Top"), wp.rcNormalPosition.top);
AfxGetApp()->WriteProfileInt(strWindow, _T("Left"), wp.rcNormalPosition.left);
AfxGetApp()->WriteProfileInt(strWindow, _T("Bottom"), wp.rcNormalPosition.bottom);
AfxGetApp()->WriteProfileInt(strWindow, _T("Right"), wp.rcNormalPosition.right);
AfxGetApp()->WriteProfileInt(strWindow, _T("ShowCmd"), wp.showCmd);
}
void CResizingDialog::InitialiseResizeIcon(CBitmap& rBmpResize, CStatic& rLblResize, CWnd* pDialog)
{
CRect rcIcon, rcClient;
if (pDialog != nullptr)
{
rBmpResize.LoadOEMBitmap(OBM_SIZE);
rLblResize.Create(nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP,
CRect(0, 0, 16, 16), pDialog, IDC_STATIC_RESIZE);
rLblResize.SetBitmap(rBmpResize);
//theApp.UpdateBitmapBackground(rLblResize.GetBitmap(), true, GetSysColor(COLOR_ACTIVECAPTION));
pDialog->GetClientRect(rcClient);
rLblResize.GetClientRect(rcIcon);
rLblResize.SetWindowPos(&CWnd::wndTop,
rcClient.right - rcIcon.Width(),
rcClient.bottom - rcIcon.Height(), 0, 0, SWP_NOSIZE);
CMFCDynamicLayout *pDynamicLayout = pDialog->GetDynamicLayout();
if (pDynamicLayout != nullptr)
{
CMFCDynamicLayout::MoveSettings moveSettings = CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100);
CMFCDynamicLayout::SizeSettings sizeSettings = CMFCDynamicLayout::SizeNone();
pDynamicLayout->AddItem(rLblResize.GetSafeHwnd(), moveSettings, sizeSettings);
}
}
}
void CResizingDialog::OnDestroy()
{
CDialogEx::OnDestroy();
SaveWindowPosition(m_strWindowID, this);
}
void CResizingDialog::DoNotShowResizeIcon()
{
m_bDoNotShowResizeIcon = true;
}
void CResizingDialog::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialogEx::OnPaint() for painting messages
if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
{
CRect rc;
GetClientRect(&rc);
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
HTHEME ht = OpenThemeData(GetSafeHwnd(), L"STATUS");
if (ht)
{
DrawThemeBackground(ht, dc, SP_GRIPPER, 0, &rc, nullptr);
CloseThemeData(ht);
}
else
{
dc.DrawFrameControl(rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
}
}
LRESULT CResizingDialog::OnNcHitTest(CPoint point)
{
CRect rc;
GetWindowRect(rc);
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
if (rc.PtInRect(point))
return HTBOTTOMRIGHT;
return CDialogEx::OnNcHitTest(point);
}
void CResizingDialog::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
Invalidate(TRUE);
}
I don't know how you are storing the window position but my code factors in for multiple monitor configurations.

An Aero caption title bar issue using DWM API on the windows 10

In order to draw the icon on the caption title bar, I have refereed this MSDN article and used DWM API to create my customize client area by calling DwmExtendFrameIntoClientArea.
my code:
CMainFrame::CMainFrame()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
TRACE0("DWM is enabled\n");
TCHAR szLogoPath[MAX_PATH];
GetModuleFileName ( GetModuleHandle(NULL), szLogoPath, _countof(szLogoPath) );
PathRemoveFileSpec ( szLogoPath );
PathAppend ( szLogoPath, _T("lena.bmp") );
m_pLogoImage = m_pLogoImage->FromFile ( CT2CW(szLogoPath) );
if(NULL == m_pLogoImage)
TRACE0("load image fail\n");
}
void CMainFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
int xFrame = 2;
int yFrame = 2;
int nTHight = 30;
NCCALCSIZE_PARAMS * p;
RECT * rc;
RECT aRect;
RECT bRect;
RECT acRect;
p = (NCCALCSIZE_PARAMS *)lpncsp;
CopyRect(&bRect,&p->rgrc[1]);
CopyRect(&aRect,&p->rgrc[0]);
acRect.left = aRect.left + xFrame;
acRect.top = aRect.top - nTHight;
acRect.right = aRect.right - xFrame;
acRect.bottom = aRect.bottom - yFrame;
CopyRect(&p->rgrc[0],&acRect);
CopyRect(&p->rgrc[1],&aRect);
CopyRect(&p->rgrc[2],&bRect);
CFrameWnd::OnNcCalcSize(TRUE, lpncsp);
}
LRESULT CMainFrame::OnNcHitTest(CPoint p)
{
BOOL dwm_enabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
{
LRESULT result = 0;
if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
result = HitTestNCA(m_hWnd, p);
if (result == HTNOWHERE && GetForegroundWindow() != this)
{
return HTCAPTION;
}
return result;
}
return CWnd::OnNcHitTest(p);
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if(cs.hMenu!=NULL)
{
::DestroyMenu(cs.hMenu);
cs.hMenu = NULL ;
}
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_OVERLAPPED| WS_SYSMENU | WS_THICKFRAME;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(0);
return TRUE;
}
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
{
if(nState == WA_ACTIVE )
{
MARGINS margins = {-1};
/*margins.cyTopHeight = 30;
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyBottomHeight = 0;*/
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr))
TRACE0("Failed in DwmExtendFrameIntoClientArea\n");
}
}
}
void CMainFrame::OnNcPaint()
{
CFrameWnd::OnPaint();
CDC* dc = GetWindowDC();
RECT rcClient;
GetWindowRect(&rcClient);
dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(255,0,0));
CPaintDC gdc(this); // device context for painting
Graphics gr(gdc.m_hDC);
gr.DrawImage ( m_pLogoImage, 0, 0 );
ReleaseDC(dc);
}
The result under Windows 7 is fine.
However, my window appears another unknown caption title bar under Windows 10.
I found out the unknown caption is caused by WS_THICKFRAME in the cs.style.
If I remove WS_THICKFRAME, the unknown cation bar will disappear, but I cannot resizing the border of my window. Furthermore, my program cannot capture the minimum, maximum and the close button message on my custom caption bar anymore.
I want to remove the unknown title bar without any side effect.
Does anyone could provide me a good solution or suggestion?
Best Regards,
When using DwmExtendFrameIntoClientArea, it means frame is extended in to client area. It is no longer in non-client area. So there is no need to override OnNcPaint, you can do all of the painting in OnPaint
void CMainFrame::OnPaint()
{
CPaintDC dc(this);
//paint titlebar area (this used to be the non-client area)
CRect rc;
GetClientRect(&rc);
rc.bottom = titlebar_height;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
BITMAPINFOHEADER bmpInfoHeader = {
sizeof(BITMAPINFOHEADER), rc.Width(), -rc.Height(), 1, 32 };
HBITMAP hbitmap = CreateDIBSection(
dc, (BITMAPINFO*)(&bmpInfoHeader), DIB_RGB_COLORS, NULL, NULL, 0);
auto oldbitmap = memdc.SelectObject(hbitmap);
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
DeleteObject(hbitmap);
//begin normal paint
//The new client area begins below titlebar_height which we define earlier
GetClientRect(&rc);
rc.top = titlebar_height;
dc.FillSolidRect(&rc, RGB(0, 0, 255));
Gdiplus::Image *image = Gdiplus::Image::FromFile(L"file.jpg");
Gdiplus::Graphics gr(dc);
gr.DrawImage(image, 0, 0);
delete image;
}
Use a member variable CRect m_border to keep track of border's thickness. You can use AdjustWindowRectEx to find the thickness of the borders.
void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
titlebar_height = 100;
//find border thickness
if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
{
m_border = { 0,0,0,0 };
AdjustWindowRectEx(&m_border, GetWindowLongPtr(m_hWnd,
GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
m_border.left = abs(m_border.left);
m_border.top = abs(m_border.top);
}
else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
{
m_border = { 1,1,1,1 };
}
else
{
m_border = { 0,0,0,0 };
}
//Extend frame in to client area
MARGINS margins = { 0 };
margins.cyTopHeight = titlebar_height; //<<=== *** edited
DwmExtendFrameIntoClientArea(m_hWnd, &margins);
SetWindowPos(NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
}
m_border will be for example {7,7,7,7};
Allow Windows to do the painting on left, right, bottom border. The top border is the only one changed
void CMainFrame::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* lpncsp)
{
if (validate)
{
lpncsp->rgrc[0].left += m_border.left;
lpncsp->rgrc[0].right -= m_border.right;
lpncsp->rgrc[0].bottom -= m_border.bottom;
}
else
{
CFrameWnd::OnNcCalcSize(validate, lpncsp);
}
}
see also How to glow the minimum. maximum and close button?

How to glow the minimum. maximum and close button?

I followed below guide to create a custom Aero Frame using DWM API.
Custom Window Frame Using DWM
My work:
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
{
if(nState == WA_ACTIVE )
{
MARGINS margins ={-1};
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr));
}
}
}
void CMainFrame::OnNcPaint(){
RECT rcClient;
GetWindowRect(&rcClient);
// Inform the application of the frame change.
SetWindowPos(
NULL,
rcClient.left, rcClient.top,
RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
SWP_FRAMECHANGED);
CFrameWnd::OnNcPaint();
CDC* dc = GetWindowDC();
dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(0,0,0));
}
LRESULT CMainFrame::OnNcHitTest(CPoint p)
{
LRESULT r ;
r = CFrameWnd::OnNcHitTest( p);
if(r == HTMINBUTTON || r == HTMAXBUTTON || r == HTCLOSE)
return r;
else
r = HitTestNCA(m_hWnd,p); // this function is direct copied from above link.
return r;
}
Result:
I found out the minimum, maximum and close button that will not be glowed when I move the mouse on these buttons.
General situation:
How to fix this problem?
Best Regards,
DwmDefWindowProc is required to handle caption buttons. From msdn:
For caption button hit testing, DWM provides the DwmDefWindowProc
function. To properly hit test the caption buttons in custom frame
scenarios, messages should first be passed to DwmDefWindowProc for
handling. DwmDefWindowProc returns TRUE if a message is handled and
FALSE if it is not. If the message is not handled by DwmDefWindowProc,
your application should handle the message itself or pass the message
onto DefWindowProc.
In MFC it can work out as follows:
LRESULT cframeWnd::OnNcHitTest(CPoint p)
{
BOOL dwm_enabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
{
LRESULT result = 0;
if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
result = HitTestNCA(m_hWnd, p);
if (result == HTNOWHERE && GetForegroundWindow() != this)
{
return HTCAPTION;
}
return result;
}
return CWnd::OnNcHitTest(p);
}
I added a fix with GetForegroundWindow(), because the HitTestNCA function from MSDN example is wrong, it doesn't return HTCLIENT when it should. So when another window has focus, it won't switch windows upon mouse click in client area.
Also, there is a leak in OnNcPaint:
CDC* dc = GetWindowDC();
Whenever GetWindowDC() is called it should be followed by ReleaseDC. Or just use CWindowDC which has automatic cleanup. You don't actually need to override OnNcPaint because frame has been extended to "client area".
Here is a full example:
class cglassWnd : public CWnd
{
void OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS FAR*);
LRESULT OnNcHitTest(CPoint p);
void OnNcMouseLeave();
int OnCreate(LPCREATESTRUCT lpCreateStruct);
void OnActivate(UINT state, CWnd* otherWnd, BOOL minimized);
void OnPaint();
CRect borders;
int titlebar_height;
DECLARE_MESSAGE_MAP()
public:
cglassWnd();
};
BEGIN_MESSAGE_MAP(cglassWnd, CWnd)
ON_WM_NCHITTEST()
ON_WM_NCCALCSIZE()
ON_WM_NCMOUSELEAVE()
ON_WM_ACTIVATE()
ON_WM_CREATE()
ON_WM_PAINT()
END_MESSAGE_MAP()
cglassWnd::cglassWnd()
{
BOOL dwm_enabled = FALSE;
DwmIsCompositionEnabled(&dwm_enabled);
if (!dwm_enabled)
TRACE("Error: don't use this class, add error handling...");
//modified height for the new title bar
titlebar_height = 60;
}
int cglassWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int res = CWnd::OnCreate(lpCreateStruct);
//find border thickness
borders = { 0,0,0,0 };
if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&borders,
GetWindowLongPtr(m_hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
borders.left = abs(borders.left);
borders.top = abs(borders.top);
}
else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
{
borders = { 1,1,1,1 };
}
//Extend caption in to client area
MARGINS margins = { 0 };
margins.cyTopHeight = titlebar_height;
DwmExtendFrameIntoClientArea(m_hWnd, &margins);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
return res;
}
void cglassWnd::OnPaint()
{
CPaintDC dc(this);
//paint titlebar area (this used to be the non-client area)
CRect rc;
GetClientRect(&rc);
rc.bottom = titlebar_height;
//see MSDN reference for explanation of this code
//upside-down bitmap is for the sake of DrawThemeTextEx
CDC memdc;
memdc.CreateCompatibleDC(&dc);
BITMAPINFOHEADER infhdr = { sizeof(infhdr), rc.right, -rc.bottom, 1, 32 };
HBITMAP hbitmap = CreateDIBSection(dc,(BITMAPINFO*)(&infhdr),DIB_RGB_COLORS,0,0,0);
auto oldbitmap = memdc.SelectObject(hbitmap);
//do extra titlebar painting here
//for example put DrawThemeTextEx for window's name
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
DeleteObject(hbitmap);
//begin normal paint
//The new client area begins below titlebar_height which we define earlier
GetClientRect(&rc);
rc.top = titlebar_height;
dc.FillSolidRect(&rc, RGB(128, 128, 255));
}
void cglassWnd::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* sz)
{
if (validate)
{
sz->rgrc[0].left += borders.left;
sz->rgrc[0].right -= borders.right;
sz->rgrc[0].bottom -= borders.bottom;
}
else
{
CWnd::OnNcCalcSize(validate, sz);
}
}
LRESULT cglassWnd::OnNcHitTest(CPoint pt)
{
LRESULT result = 0;
//handle close/minimize/maximize button
if (DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y), &result))
return result;
//cursor is over the frame or client area:
result = CWnd::OnNcHitTest(pt);
if (result == HTCLIENT)
{
ScreenToClient(&pt);
if (pt.y < borders.top) return HTTOP;
if (pt.y < titlebar_height) return HTCAPTION;
}
return result;
}
void cglassWnd::OnNcMouseLeave()
{
//This is for close/minimize/maximize/help buttons
LRESULT result;
DwmDefWindowProc(m_hWnd, WM_NCMOUSELEAVE, 0, 0, &result);
CWnd::OnNcMouseLeave();
}
void cglassWnd::OnActivate(UINT state, CWnd* otherWnd, BOOL minimized)
{
CWnd::OnActivate(state, otherWnd, minimized);
Invalidate(FALSE);
}

Changing MFC List Control header color

I am looking for the simplest way to change the header color of a 'List Control' header in MFC C++. I have found ways to change individual cells and rows, but can't get a working version that changes the header color. This is all the code I am using that deals with the header:
//Initializes the List Control with four columns
m_CListCtrl.InsertColumn(0, _T("Option"), LVCFMT_LEFT, 200);
m_CListCtrl.InsertColumn(1, _T("User"), LVCFMT_LEFT, 60);
m_CListCtrl.InsertColumn(2, _T("Value"), LVCFMT_LEFT, 80);
m_CListCtrl.InsertColumn(3, _T("Description"), LVCFMT_LEFT, 800);
This can be done, but, not without a little extra coding. What you’ll need to do:
Derive your own class from CListCtrl.
Derive your own class from CHeaderCtrl.
Replace the standard CListCtrl header with yours.
Use custom drawing for the header control.
In derived CListCtrl,
void MyListCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
CHeaderCtrl* pHeader = NULL;
pHeader = GetHeaderCtrl();
if (pHeader != NULL)
{
VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd)); // m_HeaderCtrl is the new wrapper object
}
CListCtrl::PreSubclassWindow();
}
In the header class,
void MyHeader::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = CDRF_DODEFAULT;
if (pNMCD->dwDrawStage == CDDS_PREPAINT)
{
CDC* pDC = CDC::FromHandle(pNMCD->hdc);
CRect rect(0, 0, 0, 0);
GetClientRect(&rect);
pDC->FillSolidRect(&rect, RGB(255, 0, 0));
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (pNMCD->dwDrawStage == CDDS_ITEMPREPAINT)
{
HDITEM hditem;
TCHAR buffer[MAX_PATH] = { 0 };
SecureZeroMemory(&hditem, sizeof(HDITEM));
hditem.mask = HDI_TEXT;
hditem.pszText = buffer;
hditem.cchTextMax = MAX_PATH;
GetItem(pNMCD->dwItemSpec, &hditem);
CDC* pDC = CDC::FromHandle(pNMCD->hdc);
pDC->SetTextColor(RGB(0, 0, 0));
pDC->SetBkColor(RGB(255, 0, 0));
CString str(buffer);
pDC->DrawText(str, CRect(pNMCD->rc), DT_VCENTER | DT_LEFT);
*pResult = CDRF_SKIPDEFAULT;
}
}
Using my sample code, you should see....
I'll leave any customization for you to finish.

Issue in drawing color on propertysheet footer?

I designed a property sheet and painted its footer to some gradient in the OnPaint() event.
The footer looks like as below.Observe the button area circled in red colour.
In the OnPaint I was doing as follows,
//CMySheet is derived from CPropertySheet.
void CMySheet::OnPaint()
{
if(IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND,reinterpret_cast<WPARAM>(dc.GetSafeHdc()),0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1)/2;
int y = (rect.Height() - cyIcon + 1)/2;
}
else
{
CPaintDC dc(this);
UpdateData(false);
CRect Clientrect;
GetClientRect(&Clientrect);
LONG RectDifference = ((Clientrect.bottom - m_PageRectBottom)-2);//m_pageRectBottom is of page bottom rect
CRect rectFooter(Clientrect.top,(Clientrect.bottom - RectDifference),Clientrect.right,Clientrect.bottom);//638//520
//CRect rectFooter(0,390,640,445);
FillGradation(&dc,rectFooter,RGB(150,150,150),RGB(0,0,0),true);
}
}
}
void CMySheet::OnPaint(CDC* pDC, CRect rc, COLORREF colBegin, COLORREF colEnd, bool bV)
{
TRIVERTEX av[2] = {rc.left,rc.top,GetRValue(colBegin) << 8,GetGValue(colBegin) << 8,GetBValue(colBegin) << 8 ,0xff00,
rc.right,rc.bottom,GetRValue(colEnd) << 8 ,GetGValue(colEnd) << 8,GetBValue(colEnd) << 8,0xff00,};
GRADIENT_RECT gr = {0,1};
ULONG ulMode;
if(bV){
ulMode = GRADIENT_FILL_RECT_V;
}
else{
ulMode = GRADIENT_FILL_RECT_H;
}
GradientFill(pDC->GetSafeHdc(),av,2,&gr,1,ulMode);
}
The buttons are not transparent in the above image ,but actually the background of the button should look like as in below image.
The wizard buttons background or the footer area should look like the above image.But if you can have a look at the first image in that there is some white colour around the Back button ,Next and cancel buttons.
HBRUSH CMySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CPropertySheet::OnCtlColor(pDC, pWnd, nCtlColor);
if((pWnd->GetDlgCtrlID() == ID_WIZBACK) || (pWnd->GetDlgCtrlID() == ID_WIZNEXT) ||
(pWnd->GetDlgCtrlID() == ID_WIZFINISH) || (pWnd->GetDlgCtrlID() == IDCANCEL))
{
return CreateSolidBrush(RGB(130,130,130));
}
return hbr;
}
If I am doing like this ,the Image is as follows with gray colour .But that colour should be gradient right,I am not able to create a Gradient brush.
I tried returning NULL in CtlColor but I could not see any difference.
Derived my own classes from CPropertySheet and CButton ,
//Overrided the DrawItem and PreSubclassWindow
void CPropSheetButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;
if (state & ODS_SELECTED)
pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
else
pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH);
UINT uStyle = DFCS_BUTTONPUSH;
HTHEME hTheme = OpenThemeData(m_hWnd, L"BUTTON");
HRESULT hr = DrawThemeBackground(hTheme, lpDrawItemStruct->hDC, BP_PUSHBUTTON, PBS_DEFAULTED, &lpDrawItemStruct->rcItem, NULL);
// Get the button's text.
CString strText;
GetWindowText(strText);
CloseThemeData(hTheme);
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(),
&lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
int nMode = pDC->SetBkMode(TRANSPARENT);
pDC->SetBkMode(nMode);
}
void CPropSheetButton::PreSubclassWindow()
{
CButton::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW); // make the button owner drawn
}
//In the Sheet derived class OnInitDialog ,
BOOL CMySheetWizard::OnInitDialog()
{
CPropertySheet::OnInitDialog();
CMyButton backbutton;
BOOL bRet = backbutton.SubclassDlgItem(ID_WIZBACK,this);
}
Can anyone please let me know how I can remove the border around those buttons.
Using your painting code to render the background and some extra classes, I was able to achieve this...
I think this was what you were trying to achieve. I was able to accomplish this by doing the following:
Derive my own CPropertySheet and CButton classes.
Subclass the property sheet buttons and make them owner drawn.
And here's the code that draws the buttons from the button class.
void SheetButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
UINT uStyle = DFCS_BUTTONPUSH;
HTHEME hTheme = OpenThemeData(m_hWnd, L"BUTTON");
DrawThemeBackground(hTheme, lpDrawItemStruct->hDC, BP_PUSHBUTTON, PBS_DEFAULTED, &lpDrawItemStruct->rcItem, NULL);
// Get the button's text.
CString strText;
GetWindowText(strText);
// Draw the button text using the text color red.
COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(255, 0, 0));
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(),
&lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
::SetTextColor(lpDrawItemStruct->hDC, crOldColor);
CloseThemeData(hTheme);
}
BTW...you still need to add code to return a null brush for the buttons. I did not, however, account for the different states of the buttons in the drawing code. I left that up to you as an exercise.