Introduction
Controls within a control with a cool example � �Do remember, controls are nothing but child windows. They can get all messages that a window can get.� This article will go along with a RichEdit control example. In the example, a rich edit control is subclassed and a list box is used as a child control, i.e., a ListBox within a RichEdit control.
Subclassing a control:
Subclassing a control is nothing but having your own class derived from an existing control class (like CButton
, CRichEditCtrl
, CEdit
�). By subclassing a control, you can add your own functionality to that control, as you need. For example, you can change the background color of a control as you paint the background on OnEraseBackground
handler and return true
. Only, simple thing to make it happen is, let the control variable (object) be created from your class (subclassed). For example, instead of CEdit m_eEdit
, let it be CMyEdit m_eEdit
, here CMyEdit
is derived from CEdit
. Or you can use the SubclassDlgItem
method of the CWnd
class like m_eEdit.SubclassDlgItem(IDC_EDIT1,this)
. (Refer MSDN for more info on subclassing).
Example Details:
In our example, CRichEditCtrl
has been subclassed into my own class CMyRichEditCtrl
. The theme of the work is to have a rich edit control that has to identify and display the directory/file path in a list box within it, if I am typing any drive letters in the system. For example, if I am typing �c:\� it has to list all the files and directories in that path in a listbox. It is just like the list we are having in our Visual C++ IDE � putting a period (dot) displays all the methods and variables of an object, or typing scope resolution operator (::
) to make a list of available functions, APIs and variables in that scope in a list box.
I used some string parsing functions inside the class. I am not sure that they are all well defined. May be they are. But, as the core is different let us keep it as a second thing.
Why RichEditCtrl?
No special reasons. I have done a sample Editor project using CRichEditCtrl
, and taken the code snap from there for the article, thatzaal. You can have the CEdit
control instead.
Detecting a drive letter
While typing in the edit control, we have to check whether any drive letter is typed or not in the format �driveletter:\�, if yes, then create a list box, show it with a list of available directories and files in that path, or if it is not a valid path/drive letter then do nothing. To do so, add OnKeyDown
or OnKeyUp
message handlers to your control class. Check each character on key down and the previous characters typed as follows:
void CMyRichEditCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CString str;
GetWindowText(str);
long sl,el;
GetSel(sl,el);
long rpos=str.Find("\r\n",0);
if(sl>rpos && rpos!=-1) SetSel(rpos,rpos);
if(str.Mid(sl-1,1)=="\\" && nChar!=VK_ESCAPE && nChar!=VK_BACK)
{
long spos=ReverseFind(str,":",sl); // Find a : in back
long bpos=ReverseFind(str,"\r\n",sl);
if(spos!=-1 && spos>bpos)
{
long pos=ReverseFind(str," ",spos); // Find a space in back
if(pos==-1 || pos<bpos) pos=bpos;
CString path=str.Mid(spos-2,sl-(spos-2));
if(path.Find(":\\",0)!=-1) // if drive letter
{
ShowDirList(path); // show directory list
}
}
}
CRichEditCtrl::OnKeyUp(nChar, nRepCnt, nFlags);
}
Creating a control inside a control:
void CMyRichEditCtrl::ShowDirList(CString sDir)
{
CPoint point=GetCaretPos();
if(!m_dirlist)
{
m_dirlist= new CListBox();
m_dirlist->Create(WS_CHILD|WS_THICKFRAME|
WS_VISIBLE|WS_HSCROLL|
WS_VSCROLL|LBS_SORT|
WS_BORDER|LBS_STANDARD,
CRect(CPoint(point.x+5,point.y),
CPoint(point.x+200,point.y+100)),this,1000);
}
else
m_dirlist->SetWindowPos(&wndTop,point.x+5,point.y,
200,100,SWP_SHOWWINDOW);
m_dirlist->ShowWindow(SW_SHOW);
m_dirlist->SetFocus();
m_dirlist->ResetContent();
m_dirlist->Dir(DDL_READWRITE|DDL_DIRECTORY, _T(sDir+"*.*"));
if(m_dirlist->GetCount()==0)
m_dirlist->ShowWindow(SW_HIDE);
else
m_dirlist->SetCurSel(0);
}
The following code will create a child control inside the rich edit control:
m_dirlist= new CListBox();
m_dirlist->Create(WS_CHILD|WS_THICKFRAME|....................);
If we want to customize the listbox, subclass the CListBox
control class and make your own CMyListBox
class and do whatever you want (like, you can add icons to the list items as in VC++ IDE listboxes).
To make a list box to list the directories and files in a specified path:
m_dirlist->Dir(DDL_READWRITE|DDL_DIRECTORY, _T(sDir+"*.*"));
Subclassing Example:
The above dialog will be shown if you click the ShowDlg button in the example. This dialog is just repainted with colors and is used to pick a color from 256 colors in it. This dialog is customized and used in a project of mine. CColorDlg_256
is derived from the CDialog
class. OnEraseBackground
handler is used to repaint colors. OnMouseMove
handler can detect which color has been selected.
The following code is the 256 color generator:
COLORREF CColorDlg256::GetRGBColor(int nColor256)
{
switch(nColor256)
{
case 0:return RGB(255,0,0);
case 1:return RGB(255,0,0);
case 2:return RGB(255,255,0);
case 3:return RGB(0,255,0);
case 4:return RGB(0,255,255);
case 5:return RGB(0,0,255);
case 6:return RGB(255,0,255);
case 7:return RGB(255,255,255);
case 8:return RGB(125,125,125);
case 9:return RGB(192,192,192);
case 250:return RGB(85,85,85);
case 251:return RGB(125,125,125);
case 252:return RGB(155,155,155);
case 253:return RGB(192,192,192);
case 254:return RGB(220,220,220);
case 255:return RGB(255,255,255);
}
int red=240,green=0,blue=0;
for(int i=10;i<250;i++)
{
if(i==nColor256) return RGB(red,green,blue);
if(red==240 && green<240 && blue==0 )
green+=6;
else if(red>0 && green==240 && blue==0)
red-=6;
else if(red==0 && green==240 && blue<240)
blue+=6;
else if(red==0 && green>0 && blue==240)
green-=6;
else if(red<240 && green==0 && blue==240)
red+=6;
else if(red==240 && green==0 && blue>0)
blue-=6;
}
return RGB(0,0,0);
}
Everything is drawn... the color rects, frame like borders, frame captions. All are drawn in the OnEraseBackground
handler. To prevent the handler itself from drawing the original background again, its return
statement has been commented and TRUE
returned.
BOOL CColorDlg256::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(rect);
CBrush bkbr;bkbr.CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
pDC->FillRect(rect,&bkbr);
CRect sqrRect(rect.left+8,rect.top+10,0,0);
CFont font;font.CreateFont(13,0,0,0,0,0,0,0,0,0,0,0,0,"small font");
pDC->SelectObject(&font);
for(int i=1;i<=255;i++)
{
CRect frect(rect.left+20,rect.top+20,rect.left+30,rect.top+30);
COLORREF rgb=GetRGBColor(i);
CBrush br;br.CreateSolidBrush(rgb);
CRect border(frect.left-1,frect.top-1,frect.right+1,frect.bottom+1);
CPen pen;
if(rgb==m_cSelectedColor)
{
pen.CreatePen(PS_DOT,3,RGB(0,0,0));
pDC->SelectObject(&pen);
}
else
{
pen.CreatePen(0,0,RGB(0,0,0));
pDC->SelectObject(&pen);
}
pDC->Rectangle(border);
pDC->FillRect(frect,&br);
rect.left+=13;
if(i==249)
{
sqrRect.right= frect.right+10;
sqrRect.bottom =frect.bottom+10;
CBrush *brush=CBrush::FromHandle((HBRUSH)GetStockObject(HOLLOW_BRUSH));
pDC->SelectObject(brush);
pDC->Rectangle(sqrRect);
pDC->SetBkColor(GetSysColor(COLOR_BTNFACE));
CString ts ="Pallette";
pDC->TextOut(sqrRect.left+10,sqrRect.top-7,ts);
GetClientRect(rect);
rect.left=(9*20)+55;
sqrRect=CRect(rect.left+8,rect.top+10,0,0);
}
else if(i==255)
{
sqrRect.right= frect.right+10;
sqrRect.bottom =frect.bottom+5;
CBrush *brush=CBrush::FromHandle((HBRUSH)GetStockObject(HOLLOW_BRUSH));
pDC->SelectObject(brush);
pDC->Rectangle(sqrRect);
pDC->SetBkColor(GetSysColor(COLOR_BTNFACE));
CString ts ="Gray Scale";
pDC->TextOut(sqrRect.left+10,sqrRect.top-7,ts);
}
else if(i==9)
{
sqrRect.right= frect.right+10;
sqrRect.bottom =frect.bottom+5;
CBrush *brush=CBrush::FromHandle((HBRUSH)GetStockObject(HOLLOW_BRUSH));
pDC->SelectObject(brush);
pDC->Rectangle(sqrRect);
pDC->SetBkColor(GetSysColor(COLOR_BTNFACE));
CString ts ="Basic Colors";
pDC->TextOut(sqrRect.left+10,sqrRect.top-7,ts);
rect.top+=35;
rect.left=0;
sqrRect.top=frect.bottom+15;
}
else if((i-9)%24==0 && i>9)
{
rect.top+=13;
rect.left=0;
}
}
return 1;
}
Hence subclassing is nothing but extending the functionality of a class, that is what we are calling as Inheritance.
CRichEditCtrl --------Subclassed -----> CMyRichEditCtrl
CDialog --------Subclassed -----> CColorDlg_256
And every control can have its own child control and can get all the messages that we are getting for a parent window.
Conclusion
This article may not be that much detailed. None of the articles can satisfy one's expectations. But, each article should be a seed for your technical growth. Thus, I believe that this would be a seed. Thank you all.