Thursday, December 18, 2008

ComboBox with no borders

void CMyComboBox::OnPaint()

{

CPaintDC dc(this);

CRect rect;

GetClientRect(rect);

// draw borders

dc.Draw3dRect(rect,

::GetSysColor(COLOR_3DSHADOW),

::GetSysColor(COLOR_BTNHIGHLIGHT));

rect.DeflateRect(1,1);

dc.Draw3dRect(rect,

::GetSysColor(COLOR_3DDKSHADOW),

::GetSysColor(COLOR_BTNFACE));

rect.DeflateRect(1,1);

if( (GetStyle() & 0x0000000f) > CBS_SIMPLE)

{

CRect rectBtn=rect;

rectBtn.left = rectBtn.right - ::GetSystemMetrics(SM_CXHTHUMB);

BOOL bPressed=FALSE;

if(GetKeyState(VK_LBUTTON) <>

{

CPoint point;

::GetCursorPos(&point);

ScreenToClient(&point);

if(rectBtn.PtInRect(point))

{

bPressed=TRUE;

}

}

// draw button

UINT uState = DFCS_SCROLLDOWN;

uState |= (bPressed ? DFCS_FLAT | DFCS_PUSHED : 0);

dc.DrawFrameControl(rectBtn, DFC_SCROLL, uState );

rect.right=rectBtn.left;

}

if((GetStyle()&0x0000000f)==CBS_DROPDOWNLIST)

{

CString sText;

GetWindowText(sText);

if(sText.IsEmpty())

{

dc.FillSolidRect(rect, ::GetSysColor(COLOR_WINDOW));

}

}

else

{

// draw border around edit control

dc.Draw3dRect(rect,

::GetSysColor(COLOR_BTNHIGHLIGHT),

::GetSysColor(COLOR_BTNHIGHLIGHT));

}

}

Removing the Button of a CComboBox

int CMyComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

int nRet = rtn = CComboBox::OnCreate(lpCreateStruct);

COMBOBOXINFO pcb;

pcb.cbSize = sizeof(COMBOBOXINFO);

if(GetComboBoxInfo(&pcb))

{

CEdit* pEdit = (CEdit*)FromHandle(pcb.hwndItem);

if(pEdit != NULL)

{

m_rcComboEdit = pcb.rcItem;

m_rcComboEditUnionBtn.UnionRect(ceRect,&pcb.rcButton);

}

}

return nRet;

}

void CMyComboBox::HideButton(BOOL bHide)

{

COMBOBOXINFO pcb;

pcb.cbSize = sizeof(COMBOBOXINFO);

if(GetComboBoxInfo(&pcb))

{

CEdit* pEdit = (CEdit*)FromHandle(pcb.hwndItem);

if(pEdit != NULL)

{

if(bHide)

{

pEdit->MoveWindow(m_rcComboEdit.left,

m_rcComboEdit.top,

m_rcComboEditUnionBtn.Width(),

m_rcComboEdit.Height());

}

else

{

pEdit->MoveWindow(m_rcComboEdit.left,

m_rcComboEdit.top,

m_rcComboEdit.Width(),

m_rcComboEdit.Height());

}

}

}

RedrawWindow();

}

Modify the width and height of the drop down list in a combo box

In MFC, you can simply subclass the combo-box and override the WM_CTLCOLOR message handler. This handler is used because Windows sends a message to set the colors for each of the children of the combo (the edit and the list-box), and you can easily grab the list-box HWND at this time.

HBRUSH CMyComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)

{

if (nCtlColor == CTLCOLOR_LISTBOX)

{

HWND hwndList = pWnd->GetSafeHwnd() ;

if ( hwndList != NULL )

{

CRect rectList ;

::GetWindowRect( hwndList, rectList ) ;

::MoveWindow( hwndList, rectList.left, rectList.top,

(rectList.right - rectList.left + EXTRA_WIDTH),

rectList.bottom - rectList.top + EXTRA_HEIGHT, TRUE ) ;

}

}

return CComboBox::OnCtlColor( pDC, pWnd, nCtlColor ) ;

}

Resize a combo box at run-time

/*--------------------------------------------------------------------------------

* Purpose : Set the proper number of lines in a drop-down list or combo box.

* Description : Resizes the combo box window to fit the proper number of lines.

*-------------------------------------------------------------------------------*/

void Set_DropDownSize(CComboBox& box, UINT nLinesToDisplay)

{

CRect cbSize;

box.GetClientRect(cbSize);

int nHeight;

nHeight = box.GetItemHeight(-1); // start with size of the edit-box portion

nHeight += box.GetItemHeight(0) * nLinesToDisplay; // add height of lines of text

// Add the height of the border of the edit box

nHeight += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges

// Add the height of the border of the drop-down box

nHeight += GetSystemMetrics(SM_CYEDGE) * 2; // top & bottom edges

// Set the size of the window

box.SetWindowPos(NULL, 0, 0,

cbSize.right, nHeight, // existing width, new height

SWP_NOMOVE | SWP_NOZORDER // don't move box or change z-ordering.

);

}

Monday, December 1, 2008

How to add MFC support to a simple ATL project

Step 1:

Add the following:

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS

#include afxwin.h

#ifndef _AFX_NO_OLE_SUPPORT

#include afxdisp.h // MFC Automation classes

#endif // _AFX_NO_OLE_SUPPORT

This block should be added before #define _ATL_ALL_WARNINGS

Step 2:

In Configuration properties -> General -> Projects Defaults -> Use of MFC, Select "Use MFC in a Shared DLL"

Step 3:

In the Configuration properties -> C/C++ ->Preprocessor Options section of the Compiler Settings if you are using _USRDLL and _AFXDLL together, try to remove _USRDLL and rebuild again.

We can just add ATL dialog in our project. But there will be some problems such as we can't add Control variables.

Get the Application Directory

// With standard string

char szAppPath[MAX_PATH] = "";

char szAppDirectory[MAX_PATH] = "";

::GetModuleFileName(0, szAppPath, sizeof(szAppPath) - 1);

// Extract directory

strncpy(szAppDirectory, szAppPath, strrchr(szAppPath, '\\') - szAppPath);

szAppDirectory[strlen(szAppDirectory)] = '\0';

How to open a view in maximized mode

void CTabbedViewPrjView::OnInitialUpdate()

{

CView::OnInitialUpdate();

GetParentFrame()->ShowWindow(SW_MAXIMIZE);

}

Searching files or folder in a Directory

CFileFind finder;

BOOL bWorking = finder.FindFile(SearchPath); // SearchPath : "C:\MyFolder\*.*"

while(bWorking)

{

bWorking = finder.FindNextFile();

if(finder.IsDots()) continue;

if(finder.IsDirectory())

{

.....

}

}

4 Steps to Recreate a Control (when there is no other workaround)

To recreate a Control you must:

1. Store rectangle and previous window in Z order (to keep Tab work normally).

2. Destroy Control by DestroyWindow().

3. Create Control by CreateWindow(). // put here your styles.

4. Move window to proper Z order by -

SetWindowPos (&wndPrev, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZCTIVATE);

DestroyWindow must be called for tooltips in the OnDestroy Message Handler of the Dialog

It seems, there is an internal Windows Mechanizm which turns off the WS_CHILDWINDOW style even if it is set for the tooltip control (Traced by SPY). Therefore it is evident, like Jacques said, the tooltip control is not treated like a normal child window of a dialog and it doesn't get destroyed when the Dialog is destroyed.

Therefore, DestroyWindow must be called for tooltips in the OnDestroy Message Handler of the Dialog.

Handle minimize maximize event

Handle the WM_SYSCOMMAND message. The buttons and menu items on the title bar
are all "system commands" and will generate this message. You'll need to mask
the ID with 0xFFF0 to remove the lowest 4 bits before determining which command
was sent.

Example code:

void CMainFrame::OnSysCommand( UINT nID, LPARAM lParam )

{

UINT nCmd = (nID & 0xFFF0) ;

switch ( nCmd )

{

case SC_SIZE:

// Do something here when user chooses Size command

break ;

case SC_MOVE:

// Do something here when user chooses Move command

break ;

case SC_CLOSE:

// Do something here when user chooses Close or presses X button

break ;

case SC_MAXIMIZE:

// Do something here when user chooses Maximize or presses Maximize button

break ;

case SC_MINIMIZE:

// Do something here when user chooses Minimize or presses Minimize button

break ;

case SC_RESTORE:

// Do something here when user chooses Restore or presses Restore button

break ;

default:

;

}

CFrameWnd::OnSysCommand( nID, lParam ) ;

}

Some Interesting Cursors defined in afxres.h

// Standard cursors (0x7901->)

// AFX_IDC = Cursor resources

#define AFX_IDC_CONTEXTHELP 30977 // context sensitive help

#define AFX_IDC_MAGNIFY 30978 // print preview zoom

#define AFX_IDC_SMALLARROWS 30979 // splitter

#define AFX_IDC_HSPLITBAR 30980 // splitter

#define AFX_IDC_VSPLITBAR 30981 // splitter

#define AFX_IDC_NODROPCRSR 30982 // No Drop Cursor

#define AFX_IDC_TRACKNWSE 30983 // tracker

#define AFX_IDC_TRACKNESW 30984 // tracker

#define AFX_IDC_TRACKNS 30985 // tracker

#define AFX_IDC_TRACKWE 30986 // tracker

#define AFX_IDC_TRACK4WAY 30987 // tracker

#define AFX_IDC_MOVE4WAY 30988 // resize bar (server only)

// Wheel mouse cursors

// NOTE: values must be in this order! See CScrollView::OnTimer()

#define AFX_IDC_MOUSE_PAN_NW 30998 // pan east

#define AFX_IDC_MOUSE_PAN_N 30999 // pan northeast

#define AFX_IDC_MOUSE_PAN_NE 31000 // pan north

#define AFX_IDC_MOUSE_PAN_W 31001 // pan northwest

#define AFX_IDC_MOUSE_PAN_HV 31002 // pan both axis

#define AFX_IDC_MOUSE_PAN_E 31003 // pan west

#define AFX_IDC_MOUSE_PAN_SW 31004 // pan south-west

#define AFX_IDC_MOUSE_PAN_S 31005 // pan south

#define AFX_IDC_MOUSE_PAN_SE 31006 // pan south-east

#define AFX_IDC_MOUSE_PAN_HORZ 31007 // pan X-axis

#define AFX_IDC_MOUSE_PAN_VERT 31008 // pan Y-axis

A good technique to prevent unnecessary NULL Checking in virtual methods of an inherited class

Suppose you have a method submit in a class called BOOL CommandProcessor::Submit (CCommand* pCommand), which calls a method virtual BOOL CommandProcessor::DoCommand (CEMCCommand* cmd). The code in the Submit Method goes like this:

BOOL CCmdProcessor::Submit(CCommand* pCommand)

{

if (pCommand != NULL)

{

if(DoCommand(pCommand))

{

.....

}

}

.....

}

Now somebody when overriding the DoCommand method in his inherited class might write a code such as this

virtual CInheritedCmdProcessor::DoCommand(CCommand* pCommand)

{

if(pCommand != NULL) // -> line X

{

pCommand->Execute();

}

}

As you can see, the NULL checking in line X is totally unnecessary since we are already checking for NULL in the parent class's CCmdProcessor::Submit method.

But the developer cannot be blamed, since he is receiving a pointer, his first intention would be Safety Checking. Therefore, you can just modify your architecture a little to make sure, unnecessary checking is reduced. Just simply pass a reference instead of a pointer in the DoCommand. Therefore, the developer who is inheriting your class is certain that a valid object is coming and no need to check for NULL memory. Therefore your DoCommand method should be as follows:

virtual CCmdProcessor::DoCommand(CCommand& cmd) and the code in the Submit method should be like this:

BOOL CCmdProcessor::Submit(CCommand* pCommand)

{

if(pCommand != NULL)

{

if(DoCommand(*pCommand))

{

.....

}

}

.....

}

“errno” is never reset

The external int variable errno contains the number of the most recent error or warning condition detected by the run-time library. To use this value, include the header file .

If no error or warning condition is detected, the value of errno is 0. After program execution starts, errno is never reset to 0 by the library. Programs that use errno for information about unusual conditions must set it to 0 before calling a library routine that may detect such a condition.

Inheritance problem

When you made a class child who is inherit from class parent and if parent class has some forward declaration then you received a “Compiler Error C2512 - missing type specifier”.

The solution is removing the forward declaration from the parent class header.

How to convert a pointer to a two dimensional array

void PassAs2DArray(int a[][10])

{

}

typedef int (*Ptr2Dim) [10];

void main()

{

int* n = (int*)new int [1023];

//PassAs2DArray((int(*)[10])n); -> This also works

PassAS2DArray((Ptr2Dim)n);

}

Method that returns the size of any array declared in stack

template<typename T, size_t N>

size_t mysizeof(T(&)[N])

{

return N;

}

On this note, here is a way to pass an array by C++ reference style where A has no l-value

void PassArrayByRef(int(&A)[10])

{

// A++; // Illegal

}

The usual way to pass an array where “A” is a pointer

void PassArrayByPtr(int A[10])

{

A++;

}

Select Full Text without changing the Caret pos in CRichEditCtrl

CPoint pt = GetCaretPos();

int x = CharFromPos(pt);

SetSel(0, -1);

SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0);