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






4.85/5 (24投票s)
一个列表控件,用于显示目录和文件,就像在 Rich Edit 控件中键入路径时一样,以及一个子类化的 256 色对话框。
引言
控件内的控件,带有一个酷炫的例子——“请记住,控件只是子窗口。它们可以接收窗口可以接收的所有消息。” 本文将通过一个 RichEdit 控件的例子。在该例子中,一个 rich edit 控件被子类化,并且一个列表框被用作子控件,即 RichEdit 控件内的 ListBox。
子类化控件
子类化控件只不过是创建您自己的类,该类派生自现有控件类(如 CButton
、CRichEditCtrl
、CEdit
…)。通过子类化控件,您可以根据需要为该控件添加自己的功能。例如,您可以在 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
控件。
检测驱动器号
在编辑控件中键入时,我们需要检查是否键入了格式为“驱动器号:\”的驱动器号,如果是,则创建一个列表框,显示其中可用目录和文件的列表,如果不是有效的路径/驱动器号,则不执行任何操作。为此,请为您的控件类添加 OnKeyDown
或 OnKeyUp
消息处理程序。在按键时检查每个字符以及之前键入的字符,如下所示
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+"*.*"));
子类化示例
如果您在示例中单击 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
并且每个控件都可以拥有自己的子控件,并且可以接收我们为父窗口接收的所有消息。
结论
本文可能没有那么详细。没有一篇文章能完全满足您的期望。但是,每篇文章都应该是您技术成长的一颗种子。因此,我相信这会是一颗种子。谢谢大家。