A Journey 2 Eternity

Archive for the ‘MFC’ Category

Here’s a sample which draws background of a dialog with gradient colors.

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);

TRIVERTEX vert[4] = {
{ rect.left, rect.top, 0xff00, 0xff00, 0xff00, 0 },
{ rect.right, rect.top, 0, 0xff00, 0xff00, 0 },
{ rect.right, rect.bottom, 0xff00, 0, 0xff00, 0 },
{ rect.left, rect.bottom, 0xff00, 0xff00, 0, 0 }
};

GRADIENT_TRIANGLE grad[2] = {
{ 0, 1, 2 },
{ 0, 2, 3 }
};

::GradientFill( pDC->m_hDC, vert, 4, grad, 2, GRADIENT_FILL_TRIANGLE );

return TRUE;
}

void CMyDlg::OnSize(UINT nType, int cx, int cy)
{
// To ensure the background of the whole dialog is re-drawn
Invalidate();
CDialog::OnSize(nType, cx, cy);
}
//! Replaces a color with a fill color in a given area of a
//! Device Context
//!
//! \param hDC                   : Specifies the Device Context
//! \param rcReplaceArea     : Specifies the portion of the DC area to apply the replace operation
//! \param clrColorReplace    : Specifies the color to be replaced
//! \param clrColorFill : Specifies the fill color

void ReplaceColor(HDC hDC, CRect rcReplaceArea, COLORREF clrColorReplace, COLORREF clrColorFill)
{
	CDC* pDC = CDC::FromHandle(hDC);
	CPoint pt = rcReplaceArea.TopLeft();

	CDC memDCMonoChrome;
	memDCMonoChrome.CreateCompatibleDC(pDC);

	CBitmap bmpMonoChrome;
	bmpMonoChrome.CreateCompatibleBitmap(&memDCMonoChrome, rcReplaceArea.Width(), rcReplaceArea.Height());

	CBitmap* pOldMonoBitmap = memDCMonoChrome.SelectObject(&bmpMonoChrome);

	COLORREF nOldBkColor = pDC->SetBkColor(clrColorReplace);

	// BLT to mono dc so that mask color will have 1 set and the other colors 0
	memDCMonoChrome.BitBlt(0, 0, rcReplaceArea.Width(), rcReplaceArea.Height(), pDC, pt.x, pt.y, SRCCOPY);

	CDC memDC;
	memDC.CreateCompatibleDC(pDC);

	CBitmap bmp;
	bmp.CreateCompatibleBitmap(pDC, rcReplaceArea.Width(),

	rcReplaceArea.Height());

	CBitmap* pOldBitmap = memDC.SelectObject(&bmp);
	COLORREF nOldMemDCBkColor = memDC.SetBkColor(clrColorFill);
	COLORREF nOldMemDCTextColor =

	memDC.SetTextColor(RGB(255, 255, 255));

	// BLT to mem DC so that the monochrome white is set to fill color and the monochrome black is set to white
	memDC.BitBlt(0, 0, rcReplaceArea.Width(), rcReplaceArea.Height(), &memDCMonoChrome, 0, 0, SRCCOPY);

	// AND pDC with mem dc so that the replace color part is blackened out and all other colors remains same
	pDC->BitBlt(pt.x, pt.y, rcReplaceArea.Width(), rcReplaceArea.Height(), &memDC, 0, 0, SRCAND);
	memDC.SetTextColor(RGB(0, 0, 0));

	// BLT to mem DC so that the monochrome white is set to fill color and the monochrome black is set to black
	memDC.BitBlt(0, 0, rcReplaceArea.Width(), rcReplaceArea.Height(), &memDCMonoChrome, 0, 0, SRCCOPY);

	// OR pDC with mem dc so that all colors remains as they where except the blackened out (replace color) part receives the fill color
	pDC->BitBlt(pt.x, pt.y, rcReplaceArea.Width(), rcReplaceArea.Height(), &memDC, 0, 0, SRCPAINT);

	// Set the original values back
	memDC.SetTextColor(nOldMemDCTextColor);
	memDC.SetBkColor(nOldMemDCBkColor);
	pDC->SetBkColor(nOldBkColor);

	// Set the original bitmaps back
	memDCMonoChrome.SelectObject(pOldMonoBitmap);
	memDC.SelectObject(pOldBitmap);
}

1. LButtonDown

2. LButtonUp

3. LButtonDblClick

4. LButtonUp

Tags: ,

DT_WORDBREAK and DT_VCENTER flag never work together. So here is an alternative.

void CEMCHotButton::DrawCaption (CDC* pDC, LPDRAWITEMSTRUCT lpDrawItemStruct, CRect& rectCaption, DWORD dwFormat) 
{
	// Get caption text
	CString strCaption;
	GetWindowText (strCaption);

	if(!strCaption.IsEmpty()) {
		if((GetStyle() & BS_MULTILINE) == BS_MULTILINE) {
			dwFormat &= ~DT_SINGLELINE;
			dwFormat &= ~DT_VCENTER;
			dwFormat |= DT_WORDBREAK;

			RECT rct = rectCaption;
			int height = ::DrawText(pDC->m_hDC, (LPCTSTR) strCaption, strCaption.GetLength(), &rct, DT_CALCRECT | DT_WORDBREAK);
			rectCaption.top = rectCaption.CenterPoint().y – height / 2;

			::DrawTextEx (pDC->m_hDC, strCaption.GetBuffer(0), strCaption.GetLength(), &rectCaption, dwFormat, NULL);
		}
	}
}

A topmost window is one which stays on top of other windows even while it’s not in focus. Normally all application popup windows will go to background once they lose focus but a topmost window doesn’t.

void SetTopMost( HWND hWnd, const BOOL TopMost )
{
	ASSERT( ::IsWindow( hWnd ));
	HWND hWndInsertAfter = ( TopMost ? HWND_TOPMOST : HWND_NOTOPMOST );

	::SetWindowPos( hWnd, hWndInsertAfter, 0, 0 , 0 , 0, SWP_NOMOVE | SWP_NOSIZE );
}

The second parameter passed to ::SetWindowPos is the one that does the trick. It’ called hwndInsertAfter, if we specify the after window as HWND_TOPMOST then we get a topmost window, if we specify after window as HWND_NOTOPMOST then topmost setting is unset and our window becomes a normal window.

When working in MFC/ATL and then using a resource dll can at times cause bugs because resource handle is not properly set. For e.g. if you have a dialog resource in a dll and you instantiate a CDialog which results in an error because resource cannot be located in this application or dll since it’s in the resource dll. So solution for above problem will be to set correct resource handle.

MFC provides two functions…

1. AfxGetResourceHandle

2. AfxSetResourceHandle

So we should first save our current resource handle and then set new resource handle before loading such a resource. Also don’t forget to set our old handle since its good practice.

Internally MFC calls FindResource and LoadResource using this handle, so if it’s not properly set this will cause failure in loading resources.

In ATL it’s quite similar except that we’ve got a new function called AddResourceInstance which adds our new resource handle to existing list of handles. So when a look up is done given resource handle is also used. Following functions are provided in ATL to work with resource handles…

1. AddResourceInstance

2. GetResourceInstance – Similar to AfxGetResourceHandle

3. SetResourceInstance – Similar to AfxGetSetResourceHandle

For newbies this is always a painful bug to resolve as they don’t know what went wrong since they expect this to be automagically done.

Sample code snippet:

class CEMCResourceHandle
{
public:
	CEMCResourceHandle() {
		m_hPrevInst = AfxGetResourceHandle();
		m_hDLLInstance = ::LoadLibrary(szESIMFCControlsDLLName);
		if (m_hDLLInstance) {
			AfxSetResourceHandle(m_hDLLInstance);
		}
	}

	virtual ~CEMCResourceHandle() {
		AfxSetResourceHandle(m_hPrevInst);
		if(m_hDLLInstance != NULL) {
			FreeLibrary(m_hDLLInstance);
		}
	}

	//! Retrieves the current resource instance
	//!
	//! \return Return the current resource instance
	HINSTANCE GetCurrentInstance() const { return m_hDLLInstance; }

	//! Retrieves the previous resource instance
	//!
	//! \return Return the previous resource instance
	HINSTANCE GetPrevInstance() const { return m_hPrevInst; }

private:
	//! Handle of previous resource instance
	HMODULE m_hPrevInst;

	//! Handle of loaded DLL resource instance
	HMODULE m_hDLLInstance;
};

// Just made an temporary instance of CEMCResourceHandle class
// Load resource from the DLL
CEMCResourceHandle resHandle;

// Set bitmap to the MoveUp button
m_btnUp.SetBitmap(::LoadBitmap(resHandle.GetCurrentInstance(), MAKEINTRESOURCE(IDB_BITMAP_UPARROW)));
BOOL IsPathShared( LPCTSTR lpctszPath_i )
{
	SHFILEINFO shFileInfo = { 0 };

	return ( SHGetFileInfo( lpctszPath_i, 0, &shFileInfo, sizeof( shFileInfo ), SHGFI_ATTRIBUTES ) && 
				( shFileInfo.dwAttributes & SFGAO_SHARE ) == SFGAO_SHARE );
}

Pages

Categories

April 2017
M T W T F S S
« Aug    
 12
3456789
10111213141516
17181920212223
24252627282930

Blog Stats

  • 26,880 hits