Click here to Skip to main content
15,916,949 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
See more:
Hi everyone!

I have a mfc based dialog with a tab control. On this control I want to place a child dialog, that is half the size of the tab control. The page contains some buttons ans statics.
Now my problem:
I want the child dialog to have the background of the tab control, which in some XP-styles has a color gradient.

I tried
C++
EnableThemeDialogTexture(m_hWnd,ETDT_ENABLETAB);

But that only works if the page is as big as the tab control. Otherwise the gradient looks different.

I also tried:
C++
BOOL CDialogEx::OnEraseBkgnd(CDC* pDC) 
{
DWORD style = GetExStyle();
if ( style & WS_EX_TRANSPARENT )
{
    return TRUE;
}
return CDialog::OnEraseBkgnd(pDC);
}

This works fine for the page, but the controls on it look bad, the are drawn with a default color background.

I hope someone can help. Thanks ahead!
Posted

you must also handle WM_CTLCOLOR. Something like this:

HBRUSH CBaseView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = __super::OnCtlColor(pDC, pWnd, nCtlColor);

	if(nCtlColor == CTLCOLOR_STATIC)
	{
		pDC->SetBkMode(TRANSPARENT);
		return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
	}

	return hbr;
}


I noticed a problem with this only with read-only edit controls (because the system treats them as static controls), I fixed it like this: (but that wont work well for a gradient background)

HBRUSH CBaseView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = __super::OnCtlColor(pDC, pWnd, nCtlColor);

	if(nCtlColor == CTLCOLOR_STATIC)
	{
		pDC->SetBkMode(TRANSPARENT);
		pDC->SetBkColor(RGB(242,242,242));
		return (HBRUSH)m_hSTBackBrush;
	}

	return hbr;
} 


Good luck
 
Share this answer
 
Comments
JuleWst 30-Aug-12 11:19am    
Wow, thanks a lot! The first part works perfectly for my statics. Now I still need something for check and radiobuttons. Maybe you can help with those, too? Thanks again!
Good, glad to hear it. Theres a couple different ways you can handle the other controls. I dont believe you can differentiate between types of buttons with the nCtlColor value:
if((nCtlColor == CTLCOLOR_STATIC) || (nCtlColor == CTLCOLOR_BTN)) ... 
if you also have regular push buttons they may look wrong (but you can try it).

I believe what I would do is something like this:

HBRUSH CBaseView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    switch(pWnd->GetDlgCtrlId())
    {
        case IDC_CHECKBOX1:
        case IDC_CHECKBOX2:
        case IDC_RADIOBTN1:
        case IDC_RADIOBTN2:
            pDC->SetBkMode(TRANSPARENT);
            return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
        default:
            return __super::OnCtlColor(pDC, pWnd, nCtlColor);
    }
}


Good luck, glad to help
 
Share this answer
 
v2
Comments
JuleWst 31-Aug-12 3:09am    
This doesn't seem to work for me :( The check and radiobuttons are still black. I thought it was necessary to call pDC->SetBkColor() in some way for all kinds of buttons. Do you agree? But I don't know what color to give, since i have a gradient background.
JuleWst 31-Aug-12 4:11am    
I just found out that checkboxes and radiobuttons are handled as statics with CTLCOLOR_STATIC. So I guess it's the same problem you mentioned with the read-only edit-controls. Any idea to set the background, so it fits the gradient?
Yes I believe your correct, I would have to try it myself to be certain (since Im not handling any buttons in this way right now).

In a CDialogBar derived class I have I am painting a gradient background and for my static control I picked a color right in the middle of the gradient range and it doesnt look bad (I tried to put in a screenshot but it doesnt appear to be possible here).

If your controls are always in the same place (not moved/resized during an OnSize() message) the it shouldnt be too hard to make them look good with a little tweaking. I recommend taking a screen shot and putting it into a graphics program that makes it easy to see pixel color values (I love Paint Shop Pro 8). You may need to create a few different member variable brushes but the extra work will pay off when your dialog is pretty.

I hope this helps you, let me know if you have any additional questions.
 
Share this answer
 
Comments
JuleWst 31-Aug-12 4:42am    
I am really greatful for all your help. I can't start painting every single button though. What I'm doing happens in a super class, about 60 different dialogs work with it. So I have to keep it quite general. Resizing happens as well by the way.
I understand, I am doing something similar but in my view classes there are no gradient backgrounds, only in my control bars. The only thing I can thing of is to get the parent to paint the background for you. So far I have only done this with custom classes but it may be possible to handle it in a generic way without making custom classes for all your controls.

Heres a little code that may possibly help, it uses GDI+:
void CFlatButton::OnPaint()
{
	// Prepare to draw
	CPaintDC dc(this);
	CRect rClient;
	GetClientRect(&rClient);
	BOOL bPushed = (GetState() & BST_PUSHED);

	// Double buffer 
	CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
	CBitmap bmpMem;
	bmpMem.CreateDiscardableBitmap(&dc, 
		rClient.Width(), rClient.Height());
	dcMem.SelectObject(bmpMem);
	Graphics gfxMem(dcMem.GetSafeHdc());

	if(!m_pBackground)
	{
		// NOTE: m_pBackground will need to be deleted if control is moved or sized 
		CDC dcBkg;
		dcBkg.CreateCompatibleDC(&dc);
		CBitmap bmpBkg;
		bmpBkg.CreateCompatibleBitmap(&dc, rClient.Width(), rClient.Height());
		CBitmap* pOldBmpBkg = (CBitmap*)dcBkg.SelectObject(&bmpBkg);
		dcBkg.BitBlt(0, 0, rClient.Width(), rClient.Height(), 
			&dc, rClient.left, rClient.top, SRCCOPY);
		dcBkg.SelectObject(pOldBmpBkg);
		m_pBackground = Bitmap::FromHBITMAP((HBITMAP)bmpBkg.GetSafeHandle(), NULL);
	}

	// Draw background into back buffer
	gfxMem.DrawImage(m_pBackground, 0, 0, rClient.right, rClient.bottom);

.........


or you can have the parent of the control paint it for you:

// Get parent to paint background
CRect rParent;
GetWindowRect(&rParent);
GetParent()->ScreenToClient(&rParent);
CClientDC dcParent(GetParent());
CDC dcBkg;
dcBkg.CreateCompatibleDC(&dcParent);
CBitmap bmpBkg;
bmpBkg.CreateCompatibleBitmap(&dcParent,
    rClient.Width(), rClient.Height());
CBitmap* pOldBmpBkg = (CBitmap*)dcBkg.SelectObject(&bmpBkg);
dcBkg.BitBlt(0, 0, rClient.Width(), rClient.Height(),
    &dcParent, rParent.left, rParent.top, SRCCOPY);
dcBkg.SelectObject(pOldBmpBkg);
m_pBackground = Bitmap::FromHBITMAP((HBITMAP)bmpBkg.GetSafeHandle(), NULL);



I have used both methods in different classes. To handle it at the parent level may be possible, you get a CDC* in the WM_CTLCOLOR message. This is very interesting to me, I wish I had time to try it out right now and let you know.

Good luck
 
Share this answer
 
Comments
JuleWst 31-Aug-12 5:43am    
it sure sounds interesting :) I have one question concerning the parent though. The parent of my controls is a child dialog and has no background, so that the tab control of the main dialog is seen through. I mentioned this in my question, I returned TRUE in OnEraseBkgnd(). I think your method won't work, since it needs the parent background. Am I right? Otherwise I will try it out and tell you what happened ;)
Oh, Im sorry, I missed that part about the transparent parent. Yes you are correct, that makes it trickier. I really wish I had some time to set up a test project like yours and play with it.

I believe if I were you the first thing I would do is change the transparency method of tab control(s). You could place the above code in the EraseBkg() handler, I have done it that way before myself and I know that it works. When the tab control initializes it will grab and save a background image from the parent. You would also have to get it again if the tab was sized or moved. If your project isnt using GDI+ Im sure I could dig up some GDI code doing the same thing. If your not using GDI+ I highly recommend it, its really nice.

I can also offer you some words of encouragement, there is always a solution.
 
Share this answer
 
Comments
JuleWst 31-Aug-12 7:35am    
Thanks for offering help, when you have little time. I'm not sure I understood you 100%. To clear some things up, let me give you the dialog structure:
1. The main dialog (M)
2. The tab control with color gradient, child of M (T)
3. The page, on top of T, child of M (P)
4. Buttons, children of P (B)
Buttons and Page are supposed to have the color of the tab.
So I don't want the image of the parent, but the one of the tab, right?
And the code has to be placed in the EraseBkgd of the page?
Thanks for your encouragement :)
Oh I see, the page on to of the tab control would be the one to capture a background image then. Yes you are correct in: "And the code has to be placed in the EraseBkgd of the page?".

I will generate a little test app if I can get my work done quick enough today.

Good luck
 
Share this answer
 
I tried to do what you said without using GDI, but somehow it ended up quite unstable. For some pages it works, for some not. Sometimes the background of all controls is painted white. Maybe I did something wrong?
Here's what I did for the page:
C++
BOOL CMegaDlg::OnEraseBkgnd(CDC* pDC) 
{
	DWORD style = GetExStyle();
	if ( style & WS_EX_TRANSPARENT )
	{
		if ( !m_pBackground.GetSafeHandle() )
		{
			CRect rClient, rParent;
			GetClientRect(&rClient);
			GetWindowRect(&rParent);
			GetParent()->ScreenToClient(&rParent);
			CClientDC dcParent(GetParent());
			CDC dcBkg;
			dcBkg.CreateCompatibleDC(&dcParent);
			m_pBackground.CreateCompatibleBitmap(&dcParent, 
				rClient.Width(), rClient.Height());
			CBitmap* pOldBmpBkg = (CBitmap*)dcBkg.SelectObject(&m_pBackground);
			dcBkg.BitBlt(0, 0, rClient.Width(), rClient.Height(), 
				&dcParent, rParent.left, rParent.top, SRCCOPY);
			WriteBitmap(_T("c:\\bmps\\test.bmp"),m_pBackground,dcBkg);
			dcBkg.SelectObject(pOldBmpBkg);
		}
		return TRUE;
	}
	return CDLG_BAS::OnEraseBkgnd(pDC);
}

m_pBackground is a CBitmap. If I leave out the GetSafeHandle() quey the program crashes when trying to select m_pBackground. At this point the Bitmap looks exactly the way I want it.

I did this for the controls in OnCtlColor():
C++
HBRUSH hbr = CreatePatternBrush(((CMegaDlg*)pDD->m_pDlg)->m_pBackground);
CRect rc;
pCtrl->GetWindowRect(&rc);
MapWindowPoints(NULL, pDD->m_pDlg->GetParent()->GetSafeHwnd(), (LPPOINT)(&rc), 2);
pDC->SetBrushOrg(-rc.left, -rc.top);
return hbr;
 
Share this answer
 
v2
Comments
JJMatthews 4-Sep-12 10:12am    
Thats good, it sounds like your getting it. That what it takes for things like this, trying different things until everything is working. I seem to learn the most from projects like yours. If you have any other questions in the future I will try my best to help. Good luck.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900