I want to set the background color as the color picked from my CMFCColorButton - c++

I have the color picked from my CMFCColorButton and now I want to set it to the background (if it's not already the current color).
I can't seem to figure out how, so I would really appreciate your help and explanation.
void CMainFrame::OnColor()
{
// m_TextColors is the ID of the color button I created in the resource editor.
CMFCRibbonColorButton* pColorBtn = DYNAMIC_DOWNCAST(CMFCRibbonColorButton, m_wndRibbonBar.FindByID(m_TextColors));
COLORREF color = pColorBtn->GetColor();
CWnd* pwndParent = this->GetParent();
CRect rcClient;
pwndParent->GetClientRect(&rcClient);
if (color != GetSysColor(COLOR_BACKGROUND)) {
CBrush brush;
CClientDC dc(this);
brush.CreateSolidBrush(color);
dc.FillRect(rcClient, &brush);
}
else {
MessageBox(_T("Same Color."), MB_OK);
}
}
I made some changes:
void CMainFrame::OnColor()
{
// m_TextColors is the ID of the color button I created in the resource editor.
CMFCRibbonColorButton* pColorBtn = DYNAMIC_DOWNCAST(CMFCRibbonColorButton, m_wndRibbonBar.FindByID(m_TextColors));
COLORREF color = pColorBtn->GetColor();
CBrush brush;
brush.CreateSolidBrush(color);
CRect rc;
GetClientRect(&rc);
GetWindowRect(&rc);
CClientDC dc(this);
dc.SelectObject(&rc);
if (color != GetSysColor(COLOR_WINDOW)) {
dc.FillRect(rc, &brush);
} else {
MessageBox(_T("Same Color."), MB_OK);
}
}
And this is the result:
It's tracing the document's color but not changing it, it's changing the whole window's color.
Update: I've tried the invalidateRect function, this is the result:
It seems like it's adding color on top of my MDI client area not in it in the background like i intended,

Have you tried:
void CMainFrame::OnButColor()
{
HBRUSH hBrush=CreateSolidBrush(RGB(0,0,255));
SetClassLong(m_hWndMDIClient, BGCL_HBRBACKGROUND,(LONG)hBrush);
::InvalidateRect(m_hWndMDIClient,0,TRUE);
}

Related

How to change the color scheme of CDateTimeCtrl in MFC?

I need to change the colors of the background, the text, the color of the dropdown button of
CDateTimeCtrl in MFC. I created new class derived from CDateTimeCtrl and tried overwritten OnCtlColor and CtlColor, but these functions are never called (the message map has been added using the Class Wizard). How should I achieve this without writing a completely new datetimectrl class of my own?
You can try something like this:
// header
CBrush* m_pBkgBrush { nullptr };
COLORREF m_bkgColor { RGB(255, 255, 255) };
COLORREF SetBackgroundColor(BOOL bSysColor, COLORREF cr);
and implementation now:
CYourDateTimeCtrl::CYourDateTimeCtrl()
: CDateTimeCtrl()
, m_bkgColor(::GetSysColor(COLOR_WINDOW))
{
m_pBkgBrush = new CBrush(::GetSysColor(COLOR_WINDOW));
}
CYourDateTimeCtrl::~CYourDateTimeCtrl()
{
if (nullptr != m_pBkgBrush)
delete m_pBkgBrush;
}
COLORREF CYourDateTimeCtrl::SetBackgroundColor(BOOL bSysColor, COLORREF cr)
{
COLORREF color = m_bkgColor;
m_bkgColor = bSysColor ? ::GetSysColor(COLOR_WINDOW) : cr;
if (color != m_bkgColor)
{
delete m_pBkgBrush;
m_pBkgBrush = new CBrush(m_bkgColor);
Invalidate();
}
return color;
}
BOOL CYourDateTimeCtrl::OnEraseBkgnd(CDC* pDC)
{
CBrush* pOldBrush = pDC->SelectObject(m_pBkgBrush);
CRect rect;
pDC->GetClipBox(&rect);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOldBrush);
return TRUE;
}
When you use, you do:
MyMonthCalCtrl.SetBackgroundColor(FALSE, RGB(255, 255, 0));
I hope it helps.

Retaining CMFCEditBrowseCtrl background colour when there is a popup window

So I have this excellent answer that explains how to set the background colour to a CMFCBrowseEditCtrl when it is in focus:
https://stackoverflow.com/a/36394562/2287576
class cmfc_edit : public CMFCEditBrowseCtrl
{
public:
COLORREF bkcolor;
CBrush brush;
void setBrushColor(COLORREF clr)
{
bkcolor = clr;
brush.DeleteObject();
brush.CreateSolidBrush(clr);
}
HBRUSH CtlColor(CDC* pDC, UINT)
{
if (!brush.GetSafeHandle())
return GetSysColorBrush(COLOR_WINDOW);
pDC->SetBkColor(bkcolor);
return brush;
}
//optional, change color on focus change
void OnSetFocus(CWnd* w)
{
setBrushColor(RGB(255, 0, 0));
CMFCEditBrowseCtrl::OnSetFocus(w);
}
void OnKillFocus(CWnd* w)
{
setBrushColor(RGB(255, 255, 255));
CMFCEditBrowseCtrl::OnKillFocus(w);
}
DECLARE_MESSAGE_MAP()
};
It works fine and I have no issues with it. The only problem is when I invoke a popup window. Since the popup window now has focus the background highlight I had set is being reset to the default. Is it possible to retain the requested background even when a popup window is displayed?
So, I only want my edit control to have a yellow background when it has focus, and retain this background whilst a popup window is activated. The yellow should also go when I move to another control on the dialog.
Is this possible?
This works:
void CChristianLifeMinistryStudentEdit::OnKillFocus(CWnd* pNewWnd)
{
if(GetParent()->IsChild(pNewWnd))
SetBrushColour(GetSysColor(COLOR_WINDOW));
CMFCEditBrowseCtrl::OnKillFocus(pNewWnd);
}

How to change CTabCtrl tab colors?

Hello and happy new year, (it is acceptable to say it until Thursday)
I am trying to change the color of the tabs in the CTabCtrl class. I am trying to create my own ReskinCTablCtrl so that I can just call it in separate classes and easily use it throughout my program.
Currently I am able to change the background color of the CTabCtrl but I cannot modify the tab's themselves.
I used ON_WM_ERASEBKGND() for painting the background and it worked without a problem:
BOOL ReskinCTabCtrl::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(51, 51, 51)); // dialog background color
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SetBkColor(RGB(51, 51, 51));
pDC->FillRect(&rect, &myBrush);
return bRes;
}
However, I have been unsuccesfull at changing the tab colors themselves. They are still the default MFC colors. I have tried to implement ON_WM_PAINT() and ON_WM_DRAWITEM() without any success. I think I can get to the specific tab rect with using both OnDraw and DrawItem similar to the second link example that I have posted in the end of this question.
void ReskinCTabCtrl::OnPaint() {
...
// paint the tabs first and then the borders
int nTab = GetItemCount();
int nSel = GetCurSel();
if (!nTab) // no pages added
return;
while (nTab--)
{
if (nTab != nSel)
{
dis.itemID = nTab;
dis.itemState = 0;
VERIFY(GetItemRect(nTab, &dis.rcItem));
dis.rcItem.bottom -= 2;
DrawItem(&dis);
DrawItemBorder(&dis);
}
}
...
}
I would really appreciate at least some direction to go about this problem, perhaps some more examples or what methods I should focus on using. I don't need the tabs to be different colors, maybe there is an easy way of doing this?
I've been trying to follow some examples like the following links but I still couldn't figure out the right way to do it.
https://support.microsoft.com/en-us/help/179909/how-to-change-the-background-color-of-a-tab-control
https://www.codeproject.com/Articles/1786/Ownerdraw-Tab-Controls-Borders-and-All
Enable OwnerDraw for tab control, either in resource editor, or set TCS_OWNERDRAWFIXED in OnInitDialog
CTabCtrl has message reflection for WM_DRAWITEM therefore we don't want to override WM_DRAWITEM/OnDrawItem from parent class. Instead override in CTabCtrl::DrawItem(LPDRAWITEMSTRUCT).
Unfortunately the result is rather ugly. It's sort of like overriding DrawItem in a button.
If Visual Style is available and enabled, then you can override CTabCtrl::OnPaint and draw everything manually. Example:
void CMyTabCtrl::OnPaint()
{
CPaintDC dc(this);
dc.SelectObject(GetFont());
CPen pen, pen_active;
COLORREF color_off = RGB(240, 240, 240);
COLORREF color_active = RGB(200, 240, 240);
CBrush brush_off, brush_active;
brush_off.CreateSolidBrush(color_off);
brush_active.CreateSolidBrush(color_active);
pen.CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
pen_active.CreatePen(PS_SOLID, 1, color_active);
CRect rcitem;
GetItemRect(0, &rcitem);
CRect rc;
GetClientRect(&rc);
rc.bottom = rcitem.bottom;
dc.FillSolidRect(&rc, GetSysColor(COLOR_3DFACE));
GetClientRect(&rc);
rc.top = rcitem.bottom - 1;
dc.SelectObject(&pen);
dc.SelectObject(&brush_active);
dc.Rectangle(&rc);
for(int i = 0; i < GetItemCount(); i++)
{
dc.SelectObject(&pen);
if(i == GetCurSel())
{
dc.SelectObject(&brush_active);
dc.SetBkColor(color_active);
}
else
{
dc.SelectObject(&brush_off);
dc.SetBkColor(color_off);
}
GetItemRect(i, &rcitem);
rcitem.right++;
dc.Rectangle(&rcitem);
if(i == GetCurSel())
{
dc.SelectObject(pen_active);
dc.MoveTo(rcitem.left+1, rcitem.bottom - 1);
dc.LineTo(rcitem.right, rcitem.bottom - 1);
}
TCITEM item = { 0 };
wchar_t buf[32];
item.pszText = buf;
item.cchTextMax = 32;
item.mask = TCIF_TEXT;
GetItem(i, &item);
dc.DrawText(buf, &rcitem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
BOOL CMyTabCtrl::OnEraseBkgnd(CDC*)
{
return TRUE;
}

Changing background color for diasabled check Box in MFC

I am working in an MFC windows application. I am using check boxes in Check List Box control (CCheckListBox class) . While disabling a check box, its color remains gray. Is there any way to change the background color from gray to another color?
You can use the DrawItem method to control the rendering of the control and its list items. To do that, you’ll want to derive your own CCheckListBox class and implement that method. For example, I’ve changed the second item in the list to red.
The sample code to do that looks like…
void MyCheckListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
UINT index = lpDrawItemStruct->itemID;
CDC *pDC = CDC::FromHandle (lpDrawItemStruct->hDC);
if (index == 1)
{
CRect rect = lpDrawItemStruct->rcItem;
pDC->FillSolidRect(&rect, RGB(255, 0, 0));
}
CString str;
GetText(index, str);
pDC->DrawText(str, &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER);
}
The above sample only changes the item’s background color. I’ve left the rest of the processing and any extra rendering up to you.
#rrirower's implementation will work but his code needs some modifications.
(1) Changing the Back ground color of the disabled check Box
void CMyCheckListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct ->hDC);
RECT rect = lpDrawItemStruct ->rcItem;
UINT nId=lpDrawItemStruct->itemID;
CString strItemText;
GetText(lpDrawItemStruct ->itemID, strItemText);
if(nId==1 ||nId==3){
dc.FillSolidRect(&rect,RGB(255,0,0));
dc.DrawText(strItemText , &rect, DT_LEFT | DT_VCENTER);
}
else{
CCheckListBox::DrawItem(lpDrawItemStruct);
}
dc.Detach();
}
(2) Changing the Disabled check list box text color
replace dc.FillSolidRect(&rect,RGB(255,0,0)); with dc.SetTextColor(RGB(255,0,0));

Clipping region for OnEraseBkgnd differs from clipping region for OnPaint

I'm writing an mfc application.
I've a simple CWnd with OnEraseBkgnd and OnPaint. I'm experiencing some problems when another window covers partially my window.
So When the covering window is being moved out my CWnd gets WM_ERASEBKGND. I'm cleaning up dirty area and I return TRUE. What I can see here is that CDC I get has clipping box set and I use it so only a covered part is being erased. That's good.
But then WM_PAINT comes. CDC I get with GetDC does not have any clipping box so the whole window area is being repainted. This is a problem because in my paint event I use CDC::DrawText with a transparent background (CDC::SetBkMode(TRANSPARENT)) and painting the same text in the same not-erased place causes that text becomes 'bold'. Simply painting text over and over in the same place without wiping out background makes it look ugly.
Is it a normal behavior? Is my approach ok?
EDIT:
Here I attach more inforamtion about issue.
SSCCE:
class Foo : public CFrameWnd
{
public:
BOOL OnEraseBkgnd(CDC* pDC)
{
CRect rect;
pDC->GetClipBox(rect);
HBRUSH brush = ::GetSysColorBrush(COLOR_WINDOW);
HGDIOBJ pOld = pDC->SelectObject(brush);
const BOOL result = pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld);
return result;
}
void OnPaint()
{
CWnd::OnPaint();
CDC *dc = GetDC();
CRect clipBox;
dc->GetClipBox(clipBox);
CRect rect;
GetClientRect(rect);
CFont *font = &globalFont; // in my app here is the font I use but it doesn't matter
HFONT hFont = static_cast<HFONT>(font->GetSafeHandle());
auto oldFont = dc->SelectObject(hFont);
const int bkMode = dc->SetBkMode(TRANSPARENT);
dc->DrawText("AAAAAAAAA", -1, rect, 0);
dc->SetBkMode(bkMode);
dc->SelectObject(oldFont);
}
DECLARE_MESSAGE_MAP()
};
Creation:
Foo* f = new Foo;
f->Create( 0, "test", WS_VISIBLE| WS_OVERLAPPEDWINDOW);
Below how does the window look normally:
And below after moving the window so half of the text was out of monitor and then moved back:
So the part of window which was invisible was erased and then text was placed again. For the visible part of the window text was not erased and in OnPaint was redrawn again causing 'bold'.
You should be using CPaintDC, not just because it controls resources, as Barmak pointed out, but also because it retrieves clipping data. GetDC does not do that. (Barmak also mentioned PAINTSTRUCT, but it may not be clear that is the key to the clipping issue.)
This is unrelated issue, but GetDC is causing GDI resource leak in above code. ReleaseDC must be called before exiting the function:
void OnPaint()
{
CWnd::OnPaint();
CDC *dc = GetDC();
dc.DrawText(...);
...
ReleaseDC(dc);
}
Better yet, MFC has automatic cleanup with CClientDC
void myWnd::foo()
{
CClientDC dc(this);
dc.DrawText(...);
}
OnPaint can use special CPaintDC class which corresponds to PAINTSTRUCT:
void myWnd::OnPaint()
{
CPaintDC dc(this); //don't call CWnd::OnPaint
dc.DrawText(...);
}
Back to the problem:
It looks like part of background is not repainted, but part of the text is repainted. This makes it look ugly specially with clear type fonts (it looks like bold but it isn't).
You can fix the problem with this:
dc.SetBkMode(OPAQUE);
dc.SetBkColor(GetSysColor(COLOR_WINDOW));
dc.DrawText(L"AAAAAAAAA", -1, rect, 0);
Another option: override OnEraseBkgnd and force it to do nothing:
BOOL OnEraseBkgnd(CDC*)
{
return TRUE;
}
Do all of the painting in OnPaint()
void myWnd::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(rect);
dc.FillSolidRect(rect, ::GetSysColor(COLOR_WINDOW) );
CFont *font = &globalFont;
auto oldFont = dc.SelectObject(font->GetSafeHandle());
dc.SetBkMode(TRANSPARENT);
dc.DrawText(L"AAAAAAAAA", -1, rect, 0);
dc.SelectObject(oldFont);
}