Click here to Skip to main content
Click here to Skip to main content

Control within a control & Subclassing with a cool example

, 29 Aug 2005
Rate this:
Please Sign up or sign in to vote.
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)

Share

About the Author

Naren Neelamegam
Software Developer
India India
E-Mail : loveablevirus@yahoo.com
Web : http://www.narenn.com

Comments and Discussions

 
Generalsearching a word in rich edit control PinmemberVinayCool24-May-06 21:00 
Generalvery nice PinmemberT1TAN2-Sep-05 6:15 
GeneralNice Job PinmemberNMos29-Aug-05 19:52 
GeneralCool!! PinmemberJohn A. Johnson29-Aug-05 2:29 
GeneralRe: Cool!! PinmemberNareen Neelamegam29-Aug-05 2:34 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140814.1 | Last Updated 29 Aug 2005
Article Copyright 2005 by Naren Neelamegam
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid