原文:
http://221.199.150.103/jsj/Html/vc/wen/vcwen10.htm
用VC++6.0编程的时候,文本编辑控件Edit Box是一个经常用到的控件。如果你是用它输入一些简单的文字、数字等信息,直接拿来用就可以了,但如果你用它制作大文本的编辑软件,就会觉得不好控制,许多功能无法实现,即便用CEditView,也只会生成一个类似于记事本的东西,再想加入些自己编写的功能也很困难。下面我以CEdit为基类定义了一个CEditBox类,加入了许多文本编辑时经常要用到的接口函数,用它来控制Edit Box控件就很容易制作出具有较强文本编辑功能的编辑软件了。
在这个扩展类中主要增加了以下功能:
①增加控件的容量,使它能容纳大文本;
②可设置编辑控件文字颜色、背景色和字体;
③对控件内的文本和选择文本的访问;
④直接装入文件到控件和保存控件内容到文件;
⑤自定义的右键菜单;
⑥多重ReDo/UnDo功能。
这些功能基本上都是独立的,实际使用时可根据需要选用所需功能。
准备工作:用ClassWizard在工程中加入一个新类,基类选为CEdit,类名设置为CEditBox。
一、设置控件的容量:
EditBox控件默认情况下只能装入64K的文本,如果超出,多出部分会被自动截掉。利用CEdit类的SetLimitText()函数可重新设置控件容量。
函数原型为:
void SetLimitText(UINT nMax);
参数为nMax为控件可接收的文本最大字节数。
设置方法:用ClassWizard在CEditBox类中添加消息函数PreSubclassWindow(),把设置文本容量的语句放在里面即可。
void CEditBox::PreSubclassWindow()
{
SetLimitText( -1 ); //设置编辑控件可接收的最大字节数
CEdit::PreSubclassWindow();
}
|
因为nMax为无符号整型,-1是把它设置为可以取到的最大值。你也可以根据需要设置控件的容量。
注意:在不同操作系统下,控件可设置的最大容量也不同。如果是Windows98,这个值就是64K,无法再增大了,而在Windows2000和WindowsXP下这个值要大得多,才可以起到增加控件容量的目的。
二、设置编辑控件的前景色、背景色和字体:
在CEditBox的头文件中加入以下变量定义:
COLORREF m_ForeColor; //文本颜色
COLORREF m_BackColor; //背景色
CBrush m_BkBrush; //背景刷
CFont* p_Font; //字体指针
int m_FontSize; //字体大小(1/10点)
CString m_FontName; //字体名
|
在CEditBox的构造函数中设置它们的初值:
CEditBox::CEditBox()
{
m_ForeColor = RGB(0,0,0); //文字颜色(黑)
m_BackColor = RGB(255,255,255); //文字背景色(白)
m_BkBrush.CreateSolidBrush(m_BackColor); //背景刷
p_Font = NULL; //字体指针
}
|
在CEditBox的析构函数中回收创建的字体资源:
CEditBox::~CEditBox()
{
if( p_Font )
delete p_Font; //回收字体资源
}
|
这里只设置了前景色和背景色的默认值,如果想设置默认字体,可在上面的PreSubclassWindow()函数中进行设置:
void CEditBox::PreSubclassWindow()
{
SetLimitText( -1 ); //设置编辑控件可接收的最大字节数
m_FontSize = 100;
m_FontName = _T("宋体");
p_Font = new CFont; //生成字体对象
p_Font->CreatePointFont( m_FontSize, m_FontName ); //创建字体
SetFont( p_Font ); //设置控件默认字体
CEdit::PreSubclassWindow();
}
|
这里使用了比较简单的CreatePointFont()函数创建字体,它只需给出字体尺寸和字体名。如果想创建更复杂的字体,可以改用CreateFont()函数。本例中设置控件的初始字体为尺寸为100(0.1点)的“宋体”字。
如果你想用EditBox本身的默认字体作为初始字体,就不要在PreSubclassWindow()函数中加入这些语句。
用ClassWizard添加消息反射函数CtlColor()来修改控件的文本颜色和背景色。
注意:在ClassWizard下可看到有两个很相似的消息,一个是“=WM_CTLCOLOR”消息,另一个是“WM_CTLCOLOR”消息,这里必须用“=WM_CTLCOLOR”消息添加函数。如果误用了“WM_CTLCOLOR”消息将得不到想要的效果。
HBRUSH CEditBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
pDC->SetTextColor( m_ForeColor ); //设置控件文字颜色
pDC->SetBkColor( m_BackColor ); //设置文字的背景色
return (HBRUSH)m_BkBrush.GetSafeHandle(); //控件背景刷
}
|
PreSubclassWindow()和CtlColor()函数都是消息函数,为了设置控件颜色和字体,还需定义接口函数在使用时调用:
//设置文本颜色
void CEditBox::SetForeColor(COLORREF color)
{
m_ForeColor = color;
Invalidate();
}
//获取文本颜色
COLORREF CEditBox::GetForeColor()
{
return m_ForeColor;
}
//设置背景颜色
void CEditBox::SetBkColor(COLORREF color)
{
m_BackColor = color;
m_BkBrush.Detach();
m_BkBrush.CreateSolidBrush( m_BackColor );
Invalidate();
}
//获取背景色
COLORREF CEditBox::GetBkColor()
{
return m_BackColor;
}
//设置字体
void CEditBox::SetTextFont(int FontSize,LPCTSTR FontName)
{
if ( p_Font ) delete p_Font;
p_Font = new CFont;
p_Font->CreatePointFont( FontSize, FontName );
SetFont( p_Font );
m_FontSize = FontSize;
m_FontName = FontName;
}
//获取字体大小
int CEditBox::GetFontSize()
{
return m_FontSize;
}
//获取字体名
CString CEditBox::GetFontName()
{
return m_FontName;
}
|
至此,用CEditBox类可以定义出可设置颜色和字体的Edit Box控件了。使用时,先在对话框中加入一个Edit Box控件,用ClassWizard为定义一个控制变量m_Edit,类型设定为CEditBox。然后用m_Edit.SetForeColor(color)、m_Edit.SetBkColor()和m_Edit.SetTextFont(FontHight,FontName)为控件设置颜色和字体,这样就可以作出一个美观的文本框了。
说明:Edit Box控件只能放入纯文本,不支持对文本格式的设置,也就不能对局部的文字颜色和字体进行设置,所以,以上设置都是针对整个控件的。
三、访问编辑控件的内容:
Edit Box控件已经提供了几种访问控件内容的方法:
①定义一个与控件关联的变量,类型可设置为CString或其它类型,用UpdateData()函数来更新控件或变量。
②用GetWindowText()获取控件内文本,用SetWindowText()设置控件文本。
③用SetSel()设置控件内的选择区,用GetSel()获取控件中选择文本的位置,用ReplaceSel()替换选择的文本。
但只用这几种方法还是不太方便,所以在CEditBox类中又增加了几个访问接口函数。
1、读取控件文本ReadText()
int CEditBox::ReadText(CString& str)
{
GetWindowText( str ); //获取控件文本
return str.GetLength(); //文本长度
}
|
参数str是字符串的引用,用于接收读取的控件内容,返回值是控件中文本字节数。
2、用字符串设置控件内容SetText()
void CEditBox::SetText(LPCTSTR str)
{
SetSel( 0, -1, true ); //全选
ReplaceSel( str ); //替换
SetSel(0); //设置插入点为起始位置
}
|
参数str是准备设置控件的内容,要求是字符串。
3、读取当前选择的文本ReadSelText()
int CEditBox::ReadSelText(CString& str)
{
int selStart, selEnd;
GetSel( selStart, selEnd ); //获取当前选择的位置
int selLen = selEnd-selStart; //求选择区长度
if( selLen )
{
CString text;
GetWindowText( text ); //获取控件文本
str = text.Mid( selStart, selLen ); //获取选择的文本
}
else
str = _T("");
return selLen;
}
|
参数str是字符串的引用,用于接收读出的文本,返回值是读出的文本字节数。
如果当前控件中有内容被选择,则读出选择文本,并返回长度;如果没有选择的文本,读出的是空串,返回为0。
4、设置选择区SetSelText()
void CEditBox::SetSelText(int nStartChar,int nSelLen)
{
SetSel(nStartChar,nStartChar+nSelLen);
}
|
参数nStartChar为选择区起点(从0算起),nSelLen为选择区长度。
功能是把控件的指定区域设置为选择的状态。
5、当前是否有选择isSelect()
BOOL CEditBox::isSelect()
{
int selStart, selEnd;
GetSel( selStart, selEnd ); //获取当前选择的位置
return selEnd-selStart;
}
|
如果当前控件中有选择的文本,返回非0值,否则返回0。
以上是为了使控件访问更方便而增加的接口函数。再配合CEdit本身提供的访问函数,很多操作都可轻易实现了。
CEdit控件提供访问函数主要有:
int GetWindowText(LPCTSTR lpszStringBuf,int nMaxCount);
获取控件文本,与ReadText()功能相同。
void SetWindowText(LPCTSTR lpszString);
设置控件文本。
void GetSel(int& nStartChar,int& nEndChar);
获取选择区的位置
void SetSel(int nStartChar,int nEndChar,BOOL bNoScroll=FALSE);
设置选择区,参数为起点和终点,用SetSel(0,-1)可设置为全选
void ReplaceSel(LPCTSTR lpszNewText,BOOL bCanUndo=FALSE);
用字符串替换选择的文本
四、与文件的接口:
这部分接口函数供“打开文件”和“保存文件”等操作调用。把它们定义在CEditBox类中,增强了控件的封装性,也可以简化应用中“打开”和“保存”的操作。
1、文件内容装入Edit控件
void CEditBox::LoadFile(LPCTSTR PathName)
{
CFile file; //构造一个CFile类的对象
if( file.Open( PathName, CFile::modeRead )==0 ) //以读方式打开文件
return;
int len = file.GetLength(); //求文件长度
CString text = _T("");
file.Read( text.GetBufferSetLength(len), len ); //读文件
text.ReleaseBuffer();
file.Close(); //关闭文件
SetText( text ); //装入编辑控件
m_PathName = PathName;
SetModify( false ); //清除修改标志
}
|
参数PathName为文件路径名,调用该函数可以把指定文件装入编辑控件。如果文件不存在,直接返回。
2、保存编辑控件内容到文件
void CEditBox::SaveFile(LPCTSTR PathName)
{
CFile file;
if( file.Open( PathName, CFile::modeCreate | CFile::modeWrite )==0 )
return;
CString text;
int textLen = ReadText( text );
file.Write( (LPCTSTR)text, textLen ); //把字符串内容写入文件
file.Close(); //关闭文件
m_PathName = PathName;
SetModify( false ); //清除修改标志
}
|
参数PathName为文件路径名,调用该函数可以把控件内容写入指定文件。如果建立文件失败,直接返回。
3、新建文件
void CEditBox::NewFile()
{
SetSel( 0, -1, true ); //全选
Clear(); //清除
m_PathName = _T("");
SetModify( false ); //清除修改标志
}
|
供“新建”文件菜单消息调用,功能是清空控件。
4、是否有文件打开
BOOL CEditBox::isOpenFile()
{
return !(m_PathName.IsEmpty());
}
|
如果控件中已经有打开的文件,返回非0,否则返回0。
5、获取打开的文件名
CString CEditBox::GetPathName()
{
return m_PathName;
}
|
如果控件中已经有打开的文件,返回文件路径名,否则返回空串。
这5个函数中的m_PathName是在CEditBox中定义的字符串变量,并初始化为空串。
五、自定义右键菜单:
文本编辑框已经提供了一个默认右键菜单,如果你想重新定义一个来代替它,可以按下面的方法制作。
在VC++的Project菜单下选择Add To Project下的Components and Controls,在弹出的对话框中打开Visual C++ Components,找到Pop-up Menu,单击Insert按钮,选择加入的类为CEditBox,确定。关闭对话框。
这时,你在CEditBox类中会看到已经加入了下面的代码:
void CEditBox::OnContextMenu(CWnd*, CPoint point)
{
// CG: This block was added by the Pop-up Menu component
{
if (point.x == -1 && point.y == -1){
//keystroke invocation
CRect rect;
GetClientRect(rect);
ClientToScreen(rect);
point = rect.TopLeft();
point.Offset(5, 5);
}
CMenu menu;
VERIFY(menu.LoadMenu(CG_IDR_POPUP_EDIT_BOX));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
CWnd* pWndPopupOwner = this;
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
pWndPopupOwner);
}
}
|
再到资源的Menu下,你可以找到一个ID号为CG_IDR_POPUP_EDIT_BOX的新菜单,编辑它就可得到你想要的右键菜单了。这和其它菜单的做法没有区别,我就不再详细介绍了。