I'm trying to define new colors in some regions of a CPropertySheet (mfc libaries). What I've tried is to overload OnCtlColor
and define a new background color. This methods works well but it doesn't colorize the region I want.
In the next image you can see what I get with my method.
In this image you can see 4 colorized regions:
OnCtlColor
OnCtlColor
of the object CPropertyPageI don't know what to do to colorize all regions using this libraries or using any Customizable object. Any help will be appreciated.
Thanks!
After the answer of Adrian it looks like this
However, there's still one region we cannot colorize.
Before trying a lot of combinations, I've done the next two objects which allows me to define the colors I need. You can find all source code behind. The result of this code can be checked in this picture
PropertyPage
Header
class CustomPropertyPage : public CPropertyPage
{
public:
static const COLORREF PROPERTYPAGE_BACKGROUND = RGB(68, 74, 80);
DECLARE_MESSAGE_MAP()
public:
CustomPropertyPage(UINT nIDTemplate);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
cpp
CustomPropertyPage::CustomPropertyPage(UINT nIDTemplate) : CPropertyPage(nIDTemplate)
{
}
BEGIN_MESSAGE_MAP(CustomPropertyPage, CPropertyPage)
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
HBRUSH CustomPropertyPage::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() != 0)
return CPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor);
HBRUSH hbr = CreateSolidBrush(PROPERTYPAGE_BACKGROUND_COLOR);
return hbr;
}
PropertySheet
Header
class CustomPropertySheet : public CPropertySheet
{
DECLARE_MESSAGE_MAP()
public:
CustomPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage);
virtual BOOL OnInitDialog();
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
private:
void Draw_Background(CDC *pDC);
};
cpp
CustomPropertySheet::CustomPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) : CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
}
BEGIN_MESSAGE_MAP(CustomPropertySheet, CPropertySheet)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
END_MESSAGE_MAP()
BOOL CustomPropertySheet::OnInitDialog()
{
BOOL answer = CPropertySheet::OnInitDialog();
CWnd* pTab = GetDlgItem(AFX_IDC_TAB_CONTROL);
SetWindowLongPtr(pTab->m_hWnd, GWL_STYLE, GetWindowLongPtr(pTab->m_hWnd, GWL_STYLE) | TCS_OWNERDRAWFIXED);
pTab->RedrawWindow();
return answer;
}
void CustomPropertySheet::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (nIDCtl == AFX_IDC_TAB_CONTROL)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
Draw_Background(pDC);
CRect rc(lpDrawItemStruct->rcItem);
rc.bottom += 1;
pDC->FillSolidRect(rc, CEasyPropertyPage::PROPERTYPAGE_BACKGROUND);
pDC->SetTextColor(GENERIC_TEXT_COLOR);
pDC->SetBkMode(TRANSPARENT);
char text[256];
TCITEM tci = { TCIF_TEXT | TCIF_STATE, 0, 0, text, 255, -1, 0 };
HWND tcw = ::GetDlgItem(m_hWnd, nIDCtl);
int i, tic = int(::SendMessage(tcw, TCM_GETITEMCOUNT, 0, 0));
for (i = 0; i < tic; ++i)
{
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
CRect tir;
::SendMessage(tcw, TCM_GETITEM, WPARAM(i), LPARAM(&tci));
::SendMessage(tcw, TCM_GETITEMRECT, WPARAM(i), LPARAM(&tir));
pDC->DrawText(text, tir, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
}
else CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
HBRUSH CustomPropertySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() != 0)
return CPropertySheet::OnCtlColor(pDC, pWnd, nCtlColor);
HBRUSH hbr = CreateSolidBrush(GENERIC_BACKGROUND_COLOR);
return hbr;
}
void CustomPropertySheet::Draw_Background(CDC* pDC)
{
CRect rect; this->GetClientRect(rect);
pDC->FillSolidRect(rect, GENERIC_BACKGROUND_COLOR);
rect.DeflateRect(0, 20, 0, 0);
pDC->FillSolidRect(rect, GENERIC_BORDER_COLOR);
}
To customize the light gray area (which is the embedded tab control) you need to override the OnDrawItem
method in your class that is derived from CPropertySheet
and do your custom drawing for the control with the AFX_IDC_TAB_CONTROL
identifier. Something like this:
void MyPropertySheet::OnDrawItem(int nID, LPDRAWITEMSTRUCT pDIS)
{
if (nID == AFX_IDC_TAB_CONTROL) {
CDC* pDC = CDC::FromHandle(pDIS->hDC);
CRect rc(pDIS->rcItem); rc.bottom += 1;
pDC->FillSolidRect(rc, RGB(255, 0, 0)); // Or whatever b/g/ colour you want
pDC->SetTextColor(RGB(0,0,0)); // Or whatever text colour you want
pDC->SetBkMode(TRANSPARENT);
char text[256];
TCITEM tci = { TCIF_TEXT | TCIF_STATE, 0, 0, text, 255, -1, 0 };
CRect tir;
HWND tcw = ::GetDlgItem(m_hWnd, nID);
int i, tic = int(::SendMessage(tcw, TCM_GETITEMCOUNT, 0, 0));
for (i = 0; i < tic; ++i) {
::SendMessage(tcw, TCM_GETITEM, WPARAM(i), LPARAM(&tci));
::SendMessage(tcw, TCM_GETITEMRECT, WPARAM(i), LPARAM(&tir));
if (pDIS->itemState & ODS_SELECTED)
pDC->DrawText(text, tir, DT_CENTER |DT_VCENTER | DT_SINGLELINE);
}
pDC->Detach();
}
else { // Pass other stuff to the base class
CPropertySheet::OnDrawItem(nID, pDIS);
}
return;
}
Of course, be sure to add ON_WM_DRAWITEM()
to the message map!
EDIT: You must also explicitly set the style of the embedded tab control to include TCS_OWNERDRAWFIXED
. You can do this in the OnInitDialog
override for your class.
EDIT 2: I have a slightly better way to get a pointer to the tab control, now! Also, I have added a few lines of code that act as a "cheat" to address the remaining area that needs to be coloured - by expanding the tabs to fit the width of the underlying control ...
BOOL MyPropertySheet::OnInitDialog()
{
BOOL answer = CPropertySheet::OnInitDialog(); // Call base class first!
// ... whatever other stuff you may wish to do
// CWnd* pTab = GetDlgItem(AFX_IDC_TAB_CONTROL);
CTabCtrl* pTab = GetTabControl(); // This is a bit clearer than above line!
// The following 4 lines comprise a 'first stab' at fixing the remaining issue:
CRect rcTab; pTab->GetWindowRect(&rcTab);
int nItems = pTab->GetItemCount();
int border = GetSystemMetrics(SM_CXEDGE) * 2;
pTab->SetMinTabWidth((rcTab.Width() - border) / nItems);
// ...
SetWindowLongPtr(pTab->m_hWnd, GWL_STYLE, GetWindowLongPtr(pTab->m_hWnd, GWL_STYLE) | TCS_OWNERDRAWFIXED);
pTab->RedrawWindow();
return answer;
}
Feel free to ask for further clarification and/or explanation.