Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / MFC
Article

Control within a control & Subclassing with a cool example

Rate me:
Please Sign up or sign in to vote.
4.85/5 (26 votes)
29 Aug 2005CPOL4 min read 73K   1.4K   63   5
A list control displaying directories and files as on typing the path in your Rich Edit control and a subclassed Color256 dialog.

Sample Image - cwc.jpg

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);  // get rich control text
    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(); // To position the list control
    if(!m_dirlist)      // Check whether already created or not
    {
        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:

Sample Image - colordlg.jpg

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);       //black
  case 1:return RGB(255,0,0);       //Red
  case 2:return RGB(255,255,0);     //Yellow
  case 3:return RGB(0,255,0);       //Green
  case 4:return RGB(0,255,255);     //Cyan
  case 5:return RGB(0,0,255);       //Blue
  case 6:return RGB(255,0,255);     //Pink
  case 7:return RGB(255,255,255);   //White
  case 8:return RGB(125,125,125);   //Dark Gray
  case 9:return RGB(192,192,192);   //Light Gray
  case 250:return RGB(85,85,85);    // Dark Gray
  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); // White
 }
 int red=240,green=0,blue=0;      // Start with red
 for(int i=10;i<250;i++)
 {
  if(i==nColor256) return RGB(red,green,blue);    
  if(red==240 && green<240 && blue==0 )
  // Green incrementaion towards Yellow
   green+=6;
  else if(red>0 && green==240 && blue==0)
  // Red decrementation  towards Green
   red-=6;
  else if(red==0 && green==240 && blue<240)
  // Blue incrementation towards Cyan
   blue+=6;
  else if(red==0 && green>0 && blue==240)
  // Green decrementation towards Blue
   green-=6;
  else if(red<240 && green==0 && blue==240)
  // Red incrementation towards Pink
   red+=6;
  else if(red==240 && green==0 && blue>0)
  // Blue decrementation towards Red
   blue-=6;        
 }
 return RGB(0,0,0); // Return Black
}

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;
 //return CDialog::OnEraseBkgnd(pDC);
}

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.

License

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


Written By
Software Developer
India India
Naren started coding during 1999 with FORTRAN, then COBOL, PASCAL, C, C++, VC++ ..... C#, Java, ASP so on, till today. He claims himself as techie who loves coding, but he is not even sure which technology or platform he loves, most of the time Windows, some time WinCE, some time Linux, nowadays Android and embedded platforms. He can do some query stuffs with Oracle, SQL Server, MySQL. He strongly believes that C/C++ is a must for all programmers, "if you know C/C++, you can do programming on any language". He is an electronic gadget guy who likes to buy small gadgets all the time, at least he will do window shopping on a gadget shop. His interest always have been with Automation in any form, call it a little automated program sitting in his Laptop or a home automation program runs on his mobile. Apart from coding, he likes to do...???

Comments and Discussions

 
Generalsearching a word in rich edit control Pin
VinayCool24-May-06 21:00
VinayCool24-May-06 21:00 
Generalvery nice Pin
T1TAN2-Sep-05 6:15
T1TAN2-Sep-05 6:15 
GeneralNice Job Pin
Member 103059929-Aug-05 19:52
Member 103059929-Aug-05 19:52 
GeneralCool!! Pin
andyj11529-Aug-05 2:29
andyj11529-Aug-05 2:29 
GeneralRe: Cool!! Pin
Naren Neelamegam29-Aug-05 2:34
Naren Neelamegam29-Aug-05 2:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.