Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Control within a control & Subclassing with a cool example

0.00/5 (No votes)
29 Aug 2005 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here