65.9K
CodeProject 正在变化。 阅读更多。
Home

控件中的控件 & 子类化(带酷炫示例)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (24投票s)

2005年8月29日

CPOL

4分钟阅读

viewsIcon

74255

downloadIcon

1406

一个列表控件,用于显示目录和文件,就像在 Rich Edit 控件中键入路径时一样,以及一个子类化的 256 色对话框。

Sample Image - cwc.jpg

引言

控件内的控件,带有一个酷炫的例子——“请记住,控件只是子窗口。它们可以接收窗口可以接收的所有消息。” 本文将通过一个 RichEdit 控件的例子。在该例子中,一个 rich edit 控件被子类化,并且一个列表框被用作子控件,即 RichEdit 控件内的 ListBox。

子类化控件

子类化控件只不过是创建您自己的类,该类派生自现有控件类(如 CButtonCRichEditCtrlCEdit…)。通过子类化控件,您可以根据需要为该控件添加自己的功能。例如,您可以在 OnEraseBackground 处理程序中通过绘制背景来更改控件的背景颜色,并返回 true。唯一简单的事情是让控件变量(对象)从您的类(子类化)创建。例如,不是 CEdit m_eEdit,而是 CMyEdit m_eEdit,其中 CMyEdit 派生自 CEdit。或者,您可以使用 CWnd 类的 SubclassDlgItem 方法,如 m_eEdit.SubclassDlgItem(IDC_EDIT1,this)。(有关子类化的更多信息,请参阅 MSDN)。

示例详情

在我们的例子中,CRichEditCtrl 被子类化为我自己的类 CMyRichEditCtrl。这项工作的核心是创建一个 Rich Edit 控件,当我在系统中键入任何驱动器号时,它会在控件内识别并显示一个列表框中的目录/文件路径。例如,如果我键入“c:\”,它应该列出该路径下的所有文件和目录。这就像我们的 Visual C++ IDE 中的列表一样——输入一个点 (.) 会显示对象的所有方法和变量,或者键入作用域解析运算符 (::) 以列出该作用域中可用的函数、API 和变量。

我在类中使用了字符串解析函数。我不确定它们是否都定义得很好。也许它们是。但是,核心不同,我们暂时将其放在第二位。

为什么选择 RichEditCtrl?

没有特别的原因。我用 CRichEditCtrl 做了一个示例编辑器项目,并从中提取了代码片段用于文章,仅此而已。您也可以使用 CEdit 控件。

检测驱动器号

在编辑控件中键入时,我们需要检查是否键入了格式为“驱动器号:\”的驱动器号,如果是,则创建一个列表框,显示其中可用目录和文件的列表,如果不是有效的路径/驱动器号,则不执行任何操作。为此,请为您的控件类添加 OnKeyDownOnKeyUp 消息处理程序。在按键时检查每个字符以及之前键入的字符,如下所示

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);
}

在控件内创建控件

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);
}

以下代码将在 rich edit 控件内创建一个子控件

m_dirlist= new CListBox();
m_dirlist->Create(WS_CHILD|WS_THICKFRAME|....................);

如果我们想自定义列表框,可以子类化 CListBox 控件类,创建自己的 CMyListBox 类,然后做任何你想做的事情(例如,您可以像 VC++ IDE 的列表框一样为列表项添加图标)。

让列表框列出指定目录和文件

m_dirlist->Dir(DDL_READWRITE|DDL_DIRECTORY, _T(sDir+"*.*"));

子类化示例

Sample Image - colordlg.jpg

如果您在示例中单击 ShowDlg 按钮,将显示上述对话框。此对话框仅用颜色重新绘制,用于从中选择 256 种颜色之一。此对话框经过定制,并用于我的一个项目中。CColorDlg_256 派生自 CDialog 类。OnEraseBackground 处理程序用于重新绘制颜色。OnMouseMove 处理程序可以检测到选择了哪种颜色。

以下代码是 256 色生成器

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
}

所有内容都已绘制……颜色矩形、边框、框架标题。所有这些都在 OnEraseBackground 处理程序中绘制。为了防止处理程序本身再次绘制原始背景,其 return 语句已被注释掉,并返回 TRUE

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);
}

因此,子类化不过是扩展类的功能,这就是我们所说的继承。

CRichEditCtrl --------Subclassed -----> CMyRichEditCtrl
CDialog       --------Subclassed -----> CColorDlg_256

并且每个控件都可以拥有自己的子控件,并且可以接收我们为父窗口接收的所有消息。

结论

本文可能没有那么详细。没有一篇文章能完全满足您的期望。但是,每篇文章都应该是您技术成长的一颗种子。因此,我相信这会是一颗种子。谢谢大家。

© . All rights reserved.