`
huozheleisi
  • 浏览: 1234289 次
文章分类
社区版块
存档分类
最新评论

扩展Edit Box控件的功能

 
阅读更多

用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(),把设置文本容量的语句放在里面即可。

voidCEditBox::PreSubclassWindow()
{
SetLimitText(-1);//设置编辑控件可接收的最大字节数
CEdit::PreSubclassWindow();
}

因为nMax为无符号整型,-1是把它设置为可以取到的最大值。你也可以根据需要设置控件的容量。

注意:在不同操作系统下,控件可设置的最大容量也不同。如果是Windows98,这个值就是64K,无法再增大了,而在Windows2000和WindowsXP下这个值要大得多,才可以起到增加控件容量的目的。

二、设置编辑控件的前景色、背景色和字体:

在CEditBox的头文件中加入以下变量定义:

COLORREFm_ForeColor;//文本颜色
COLORREFm_BackColor;//背景色
CBrushm_BkBrush;//背景刷
CFont*p_Font;//字体指针
intm_FontSize;//字体大小(1/10点)
CStringm_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)
deletep_Font;//回收字体资源
}

这里只设置了前景色和背景色的默认值,如果想设置默认字体,可在上面的PreSubclassWindow()函数中进行设置:

voidCEditBox::PreSubclassWindow()
{
SetLimitText(-1);//设置编辑控件可接收的最大字节数

m_FontSize=100;
m_FontName=_T("宋体");
p_Font=newCFont;//生成字体对象
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”消息将得不到想要的效果。

HBRUSHCEditBox::CtlColor(CDC*pDC,UINTnCtlColor)
{
pDC->SetTextColor(m_ForeColor);//设置控件文字颜色
pDC->SetBkColor(m_BackColor);//设置文字的背景色
return(HBRUSH)m_BkBrush.GetSafeHandle();//控件背景刷
}

PreSubclassWindow()和CtlColor()函数都是消息函数,为了设置控件颜色和字体,还需定义接口函数在使用时调用:

//设置文本颜色
voidCEditBox::SetForeColor(COLORREFcolor)
{
m_ForeColor=color;
Invalidate();
}

//获取文本颜色
COLORREFCEditBox::GetForeColor()
{
returnm_ForeColor;
}

//设置背景颜色
voidCEditBox::SetBkColor(COLORREFcolor)
{
m_BackColor=color;
m_BkBrush.Detach();
m_BkBrush.CreateSolidBrush(m_BackColor);
Invalidate();
}

//获取背景色
COLORREFCEditBox::GetBkColor()
{
returnm_BackColor;
}

//设置字体
voidCEditBox::SetTextFont(intFontSize,LPCTSTRFontName)
{
if(p_Font)deletep_Font;
p_Font=newCFont;
p_Font->CreatePointFont(FontSize,FontName);
SetFont(p_Font);
m_FontSize=FontSize;
m_FontName=FontName;
}

//获取字体大小
intCEditBox::GetFontSize()
{
returnm_FontSize;
}

//获取字体名
CStringCEditBox::GetFontName()
{
returnm_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()

intCEditBox::ReadText(CString&str)
{
GetWindowText(str);//获取控件文本
returnstr.GetLength();//文本长度
}

参数str是字符串的引用,用于接收读取的控件内容,返回值是控件中文本字节数。

2、用字符串设置控件内容SetText()

voidCEditBox::SetText(LPCTSTRstr)
{
SetSel(0,-1,true);//全选
ReplaceSel(str);//替换
SetSel(0);//设置插入点为起始位置
}

参数str是准备设置控件的内容,要求是字符串。

3、读取当前选择的文本ReadSelText()

intCEditBox::ReadSelText(CString&str)
{
intselStart,selEnd;
GetSel(selStart,selEnd);//获取当前选择的位置
intselLen=selEnd-selStart;//求选择区长度
if(selLen)
{
CStringtext;
GetWindowText(text);//获取控件文本
str=text.Mid(selStart,selLen);//获取选择的文本
}
else
str=_T("");
returnselLen;
}

参数str是字符串的引用,用于接收读出的文本,返回值是读出的文本字节数。

如果当前控件中有内容被选择,则读出选择文本,并返回长度;如果没有选择的文本,读出的是空串,返回为0。

4、设置选择区SetSelText()

voidCEditBox::SetSelText(intnStartChar,intnSelLen)
{
SetSel(nStartChar,nStartChar+nSelLen);
}

参数nStartChar为选择区起点(从0算起),nSelLen为选择区长度。

功能是把控件的指定区域设置为选择的状态。

5、当前是否有选择isSelect()

BOOLCEditBox::isSelect()
{
intselStart,selEnd;
GetSel(selStart,selEnd);//获取当前选择的位置
returnselEnd-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控件

voidCEditBox::LoadFile(LPCTSTRPathName)
{
CFilefile;//构造一个CFile类的对象
if(file.Open(PathName,CFile::modeRead)==0)//以读方式打开文件
return;
intlen=file.GetLength();//求文件长度
CStringtext=_T("");
file.Read(text.GetBufferSetLength(len),len);//读文件
text.ReleaseBuffer();
file.Close();//关闭文件
SetText(text);//装入编辑控件
m_PathName=PathName;
SetModify(false);//清除修改标志
}

参数PathName为文件路径名,调用该函数可以把指定文件装入编辑控件。如果文件不存在,直接返回。

2、保存编辑控件内容到文件

voidCEditBox::SaveFile(LPCTSTRPathName)
{
CFilefile;
if(file.Open(PathName,CFile::modeCreate|CFile::modeWrite)==0)
return;
CStringtext;
inttextLen=ReadText(text);
file.Write((LPCTSTR)text,textLen);//把字符串内容写入文件
file.Close();//关闭文件
m_PathName=PathName;
SetModify(false);//清除修改标志
}

参数PathName为文件路径名,调用该函数可以把控件内容写入指定文件。如果建立文件失败,直接返回。

3、新建文件

voidCEditBox::NewFile()
{
SetSel(0,-1,true);//全选
Clear();//清除
m_PathName=_T("");
SetModify(false);//清除修改标志
}

供“新建”文件菜单消息调用,功能是清空控件。

4、是否有文件打开

BOOLCEditBox::isOpenFile()
{
return!(m_PathName.IsEmpty());
}

如果控件中已经有打开的文件,返回非0,否则返回0。

5、获取打开的文件名

CStringCEditBox::GetPathName()
{
returnm_PathName;
}

如果控件中已经有打开的文件,返回文件路径名,否则返回空串。

这5个函数中的m_PathName是在CEditBox中定义的字符串变量,并初始化为空串。

五、自定义右键菜单:

文本编辑框已经提供了一个默认右键菜单,如果你想重新定义一个来代替它,可以按下面的方法制作。

在VC++的Project菜单下选择Add To Project下的Components and Controls,在弹出的对话框中打开Visual C++ Components,找到Pop-up Menu,单击Insert按钮,选择加入的类为CEditBox,确定。关闭对话框。

这时,你在CEditBox类中会看到已经加入了下面的代码:

voidCEditBox::OnContextMenu(CWnd*,CPointpoint)
{
//CG:ThisblockwasaddedbythePop-upMenucomponent
{
if(point.x==-1&&point.y==-1){
//keystrokeinvocation
CRectrect;
GetClientRect(rect);
ClientToScreen(rect);
point=rect.TopLeft();
point.Offset(5,5);
}
CMenumenu;
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的新菜单,编辑它就可得到你想要的右键菜单了。这和其它菜单的做法没有区别,我就不再详细介绍了。

http://221.199.150.103/jsj/Html/vc/wen/vcwen10.htm

六、多重UnDo/ReDo功能:

Edit Box控件提供了UnDo功能,但只能撤销一次操作,要想实现多重UnDo/ReDo功能需要自己设计。

UnDo:撤销上一次修改操作,实现时应保存最近几次修改。

ReDo:重做上一次撤销的操作,如果你撤销后后悔了,就ReDo吧。

1、数据结构

利用一个结构数组作为栈保存最近几次修改操作,定义如下:

#defineUNDOMAX30//栈深度(最大Undo次数)

//可撤销的操作名
#defineOP_DELSEL1//删除选择(剪切)
#defineOP_REPLACE2//替换选择
#defineOP_DELETE3//删除
#defineOP_BACK4//Backspace
#defineOP_INPUT5//输入

//Undo/Redo栈结构
typedefstruct
{
intop;//操作名
intpos;//操作的位置
CStringstr1;//旧内容
CStringstr2;//新内容
}STACKNODE;

private:
STACKNODEm_Stack[UNDOMAX];//工作栈
intutop;//Undo栈顶指针
intubottom;//Undo栈底指针
intrtop;//Redo栈顶指针
intrbottom;//Redo栈底指针
BOOLb_DelFlag;//删除标志

UNDOMAX是预定义的栈深度,这里定义为30,表示可撤销最近的30步操作。

STACKNODE结构用来定义栈节点,对每一次修改操作,需要纪录修改的位置,修改前的内容和修改后的内容。而且不同的修改操作在撤销时会略有不同,所以还需纪录修改操作名。

所有修改可归结为5种:

OP_DELSEL:删除选择的文本,此时str1保存被删除的文本,str2为空;
OP_REPLACE:替换选择的文本,str1为被换掉的文本,str2为新文本;
OP_DELETE:用Del键删除文本,str1保存被删除的文本,str2为空;
OP_BACK:用BackSpace键删除文本,str1保存被删除的文本,str2为空;
OP_INPUT:键盘输入新文本,str1为空,str2为新输入的文本。

其它的操作都可归纳到这5种之内,如剪切就是OP_DELSEL,粘贴就是OP_REPLACE。

m_Stack是长度为UNDOMAX的栈,它既是UnDo栈,也是ReDo栈,栈指针utop、ubottom确定UnDo栈位置,rtop、rbottom确定ReDo栈位置。

2、栈操作

①初始化工作栈

voidCEditBox::InitStack()
{
for(inti=0;i<UNDOMAX;i++)//栈空间
{
m_Stack[i].op=-1;
m_Stack[i].str1=_T("");
m_Stack[i].str2=_T("");
}
utop=0;//栈指针
ubottom=0;
rtop=0;
rbottom=0;
}

②入栈

voidCEditBox::Push(STACKNODE*pNode)
{
utop=(utop+1)%UNDOMAX;//修改栈顶指针
rtop=utop;//清空Redo栈
rbottom=utop;
if(utop==ubottom)//如果栈满
ubottom=(ubottom+1)%UNDOMAX;//修改栈底指针
m_Stack[utop]=*pNode;//入栈
}

每次修改操作时,把纪录修改的节点推入栈中。栈采用环形结构,当栈满时,新入栈的节点覆盖栈底节点,也就淘汰了最早进入栈内节点。

③UnDo出栈

STACKNODE*CEditBox::UnDoPop()
{
if(utop==ubottom)//栈空
returnNULL;
STACKNODE*p=&m_Stack[utop];
rtop=utop;//Redo入栈
utop=utop-1;//退栈
if(utop<0)
utop=UNDOMAX-1;
returnp;//返回退栈节点
}

当进行UnDo操作时,从utop指示的栈顶弹出记录最近一次修改的节点,但这个节点并不删除,通过修改rtop使它进入ReDo栈,供ReDo操作时重做被撤销的操作。所以,这个操作既是UnDo出栈,也是ReDo进栈。

④ReDo出栈

STACKNODE*CEditBox::RedoPop()
{
if(rtop==utop)
returnNULL;
STACKNODE*p=&m_Stack[rtop];
utop=(utop+1)%UNDOMAX;//Undo入栈
if(rtop==rbottom)//如果Redo栈满
{
rtop=utop;//修改栈底指针
rbottom=utop;
}
else
rtop=(rtop+1)%UNDOMAX;//修改栈顶指针
returnp;//返回出栈的节点
}

当进行ReDo操作时,从rtop指示的栈顶弹出记录去恢复被撤销的操作,同时通过修改utop使它重新进入UnDo栈,供UnDo操作使用。所以,这个操作执行了ReDo出栈,也执行了UnDo进栈。

这个栈的变化方式如图所示:

UnDo栈和ReDo栈共享一个数组空间,ub和ut是UnDo栈的栈底指针和栈顶指针,rb和rt是ReDo栈的栈底指针和栈顶指针。初始时它们均相等,这时为空栈(图a)。当进行修改操作时,修改结构进栈,修改ut到新的栈顶,同时rb和rt同步移动,ReDo栈仍为空(图b)。当进行撤销操作时,把ut指示的修改结构取出恢复内容,修改ut表示结构从UnDo栈退出,同时修改rt,使结构进入ReDo栈(图c)。恢复撤销时,用rt指示的结构进行恢复。如果不恢复,新的修改进入UnDo栈,同时让rt和rb跟踪ut,使ReDo栈清空。

这个空间是个环形空间,栈满时修改栈底指针淘汰陈旧内容。

⑤取栈顶

STACKNODE*CEditBox::GetTop()
{
if(utop==ubottom)
returnNULL;
return&m_Stack[utop];
}

取UnDo栈的栈顶元素,但不修改栈。

3、UnDo/ReDo操作

①保存可撤销的操作

voidCEditBox::SetUndo(intop,intpos,LPCTSTRstr1,LPCTSTRstr2)
{
STACKNODE*ptop=GetTop();
if(op==OP_DELETE&&ptop&&ptop->op==OP_DELETE&&ptop->pos==pos)
{
ptop->str1+=str1;
return;
}
STACKNODEnode;
node.op=op;
node.pos=pos;
node.str1=str1;
node.str2=str2;
Push(&node);//入栈
}

每做一次修改操作,通过调用SetUndo()函数把修改操作保存到栈中。参数依次为操作名、修改位置、旧内容和新内容。

函数中先根据参数填写好STACKNODE节点,再把它推入栈内即可。

这里对OP_DELETE操作做了一个特殊处理,当我们用Del键在同一位置连续删除多个字符时,应该记录为一个操作,在撤销时一次性恢复。所以当操作为OP_DELETE时,如果发现上一次操作也是OP_DELETE,且位置相同,则把新删除的字符连接到上次删除的字符串中就行了。

②处理Del键和BackSpace键

用按键消息检测是否进行了Del和BackSpace键操作,并进行相应处理。

voidCEditBox::OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags)
{
CStringText,SelStr;
intpos,SelLen;
GetText(pos,Text,SelStr,SelLen);
if(nChar==VK_DELETE&&!Text.IsEmpty())//Del键
{
if(SelLen)
SetUndo(OP_DELSEL,pos,SelStr,_T(""));//保存删除内容
else
{
if(Text[pos]<0||Text[pos]==0x0D||Text[pos]==0x0A)
SetUndo(OP_DELETE,pos,Text.Mid(pos,2),_T(""));//删除的是汉字或回车
else
SetUndo(OP_DELETE,pos,Text.Mid(pos,1),_T(""));//删除的是字符
}
b_DelFlag=true;
}
elseif(nChar==VK_BACK&&pos>=0)//BackSpace键
{
if(SelLen)
SetUndo(OP_DELSEL,pos,SelStr,_T(""));//保存删除的内容
else
{
if(pos>0&&Text[pos-1]<127&&Text[pos-1]>=0x20)
SetUndo(OP_BACK,pos-1,Text.Mid(pos-1,1),_T(""));
elseif(pos>1&&(Text[pos-2]<0||Text[pos-1]==0x0D||Text[pos-1]==0x0A))
SetUndo(OP_BACK,pos-2,Text.Mid(pos-2,2),_T(""));
}
b_DelFlag=true;
}

CEdit::OnKeyDown(nChar,nRepCnt,nFlags);
}

OnKeyDown()函数用ClassWizard添加到CEditBox类中。当检测到Del键或BackSpace键时,如果此时有选择的文本,就保存为“删除选择OP_DELSEL”操作,如果没有选择的文本再保存为“OP_DELETE”操作或“OP_BACK”操作。另外,删除字符和删除汉字也有所区别,如果删除位置为半角字符,就取一个字符进入UnDo栈,如果删除位置处为汉字,就取两个字节进入UnDo栈。

③处理键盘输入

用ClassWizard添加OnChar()函数处理键盘输入,与OnKeyDown()函数不同,OnChar()函数只在输入可打印字符时才响应,用它可过滤掉各种控制键,而OnKeyDown()函数对任何按键都要响应,所以Del键只能用它来处理。

voidCEditBox::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
CStringSelStr;
ReadSelText(SelStr);
intpos1,end,pos2;
GetSel(pos1,end);//获取输入前的光标位置
CEdit::OnChar(nChar,nRepCnt,nFlags);//处理键盘输入
if(!b_DelFlag)
{
CStringText,str;
GetWindowText(Text);
GetSel(pos2,end);//获取输入后的光标位置
str=Text.Mid(pos1,pos2-pos1);
SetUndo(OP_INPUT,pos1,SelStr,str);//保存到UnDo栈
}
b_DelFlag=false;
}

为了处理汉字的输入,在处理按键函数CEdit::OnChar前纪录光标位置,这个位置是输入文字前的位置,在CEdit::OnChar函数后再求得的光标位置是输入文字后的位置,两者之间的符号就是应该保存的撤销内容。

这里有个缺陷,就是当输入汉字词组后,撤销时只能一个字一个字的撤销,不能一下撤销整个数组,不知该如何改进。

b_DelFlag用于防止Del键和BackSpace键被重复处理。

4、接口函数

CEditBox增加了UnDo/ReDo功能后,原来的那些涉及修改控件内容的操作就需要重新设计,使它们能够被撤销,所以设计了新的接口函数。

①isCanUndo()和isCanRedo()

判断当前是否有可撤销和可重做的操作。用于激活或禁止UnDo/ReDo菜单。

//判断能否撤销
BOOLCEditBox::isCanUndo()
{
returnutop!=ubottom;
}

//判断能否重做
BOOLCEditBox::isCanRedo()
{
returnutop!=rtop;
}

当存在可撤销或可重做的操作(栈非空)时返回TRUE,否则返回FALSE。

②EditUndo()和EditRedo()

EditUndo()函数执行撤销操作,EditRedo()函数执行重做操作。供UnDo/ReDo菜单调用。

//撤销
voidCEditBox::EditUndo()
{
STACKNODE*p=UnDoPop();//出栈
if(p)
{
SetSelText(p->pos,p->str2.GetLength());//选择文本
ReplaceSel(p->str1);//恢复内容
}
}

//重做
voidCEditBox::EditRedo()
{
STACKNODE*p=RedoPop();//出栈
if(p)
{
SetSelText(p->pos,p->str1.GetLength());//选择文本
ReplaceSel(p->str2);//恢复内容
}
}

③EditReplace()和RepAll()

EditReplace()函数是用指定文本替换选择的文本。RepAll()是用指定文本重置编辑控件内容。

//替换选择
voidCEditBox::EditReplace(LPCTSTRstr)
{
CStringText,SelStr;
intpos,SelLen;
GetText(pos,Text,SelStr,SelLen);
SetUndo(OP_REPLACE,pos,SelStr,str);//保存到Undo
ReplaceSel(str);//替换选择
}

//替换全部
voidCEditBox::RepAll(LPCTSTRstr)
{
SetSel(0,-1);//全选
EditReplace(str);//替换
SetSel(0);//设置插入点为起始位置
}

EditReplace()函数用来代替CEdit类的ReplaceSel()函数,两者功能一致,但用EditReplace()函数做的操作可以撤销,而ReplaceSel()函数做的操作不能撤销。

RepAll()函数用来代替SetWindowText()和前面定义的SetText(),用它做的操作可以撤销。

④剪切和粘贴

CEdit类的Cut()和Paste()函数都需要重新设计,而复制操作Copy()由于不修改文本,可照常使用。

//剪切(代替Cut())
voidCEditBox::EditCut()
{
CStringText,SelStr;
intpos,SelLen;
GetText(pos,Text,SelStr,SelLen);
SetUndo(OP_DELSEL,pos,SelStr,_T(""));//保存到Undo
Cut();//剪切
}

//粘贴(代替Paste())
voidCEditBox::EditPaste()
{
OpenClipboard();//打开剪贴板

HANDLEStrHandle;
StrHandle=::GetClipboardData(CF_TEXT);

char*pMem;
pMem=(char*)::GlobalLock(StrHandle);

CStringstr;
str=pMem;
::GlobalUnlock(StrHandle);
CloseClipboard();//关闭剪贴板

if(!str.IsEmpty())
EditReplace(str);//粘贴
}

//全部剪切
voidCEditBox::EditCutAll()
{
SetSel(0,-1);//全选
EditCut();//剪切
}

//全部复制
voidCEditBox::EditCopyAll()
{
SetSel(0,-1);//全选
Copy();//复制
}

//全部删除
voidCEditBox::EditClearAll()
{
SetSel(0,-1);//全选
EditReplace(_T(""));//清空
}

七、CEditBox类的使用:

CEditBox类与CEdit类的用法基本相同。你可以在对话框里加入一个Edit Box控件,设置属性Multiline(多行文本)、Vertical scroll(垂直滚动条)、Want return(接收回车),并取消Auto HScroll属性(自动换行);用ClassWizard为控件添加变量,类型设置为CEditBox;再加入头文件#include "EditBox.h";之后就可以根据需要编程控制这个编辑控件了。

再解决一个小问题,就是Tab键的输入问题。

在界面上,Tab键起到选择控件的功能,这导致无法在编辑控件中输入Tab符。

解决方法是:

先在资源中定义一个Tab快捷键:打开资源的Accelerator下的IDR_MAINFRAME,在快捷键表中添加一个VK_TAB的快捷键,假设ID设置为ID_KEY_TAB;

回到视类,用ClassWizard为ID_KEY_TAD添加消息函数OnKeyTab(),在其中加入代码:

//处理Tab输入
voidCEditTestView::OnKeyTab()
{
m_EditBox.EditReplace(_T("\t"));
}

这里的m_EditBox就是编辑控件的控制变量。

这样CEditBox类就做好了,它的完整代码见示例程序中的EditBox.h和EditBox.cpp。

示例程序是利用CEditBox类制作的一个文本编辑器,它具有了记事本的多数功能。它还具有以下特征:可设置编辑区的字体、颜色;利用ini文件保存设置;有字数统计功能,支持查找/替换操作等。它本身就是一个可代替记事本的好用的编辑器。在此基础上,你可以根据需要添加更多的功能。

示例程序界面图:

示例程序界面


http://221.199.150.103/jsj/Html/vc/wen/vcwen11.htm


分享到:
评论

相关推荐

    史上最详细的扩展List控件制作代码

    VC2010实现扩展list控件,可对list控件进行编辑写入,编辑功能包含edit控件、Combo Box控件、Radio控件、DataTime Piker 控件。具有完整的开发代码。

    史上最详细的VC2010实现扩展列表控件,包含编辑控件、日历控件、组合框控件、单选控件的制作方法

    VC2010实现扩展list控件,可对list控件进行编辑写入,编辑功能包含edit控件、Combo Box控件、Radio控件、DataTime Piker 控件。代码见网站内其他资源,请搜索:《史上最详细的扩展List控件制作代码》。

    VC2010实现扩展list控件

    VC2010实现扩展list控件,可对list控件进行编辑写入,编辑功能包含edit控件、Combo Box控件、Radio控件、DataTime Piker 控件。

    Visual C++2010开发权威指南.part02

    5.27 Visual C++ 2010 Check Box控件 5.27 简介与开发 262 5.28 Visual C++ 2010 Radio Button 5.28 控件简介与开发 264 5.28.1 为单选按钮控件分组 264 5.28.2 获得被选中的单选按钮的 5.28.2 文本 264 5.29 Visual...

    VC++2010权威开发指南+源代码

     5.27 Visual C++ 2010 Check Box控件简介与开发 262  5.28 Visual C++ 2010 Radio Button控件简介与开发 264  5.28.1 为单选按钮控件分组 264  5.28.2 获得被选中的单选按钮的文本 264  5.29 Visual C++ 2010 ...

    VC++2010权威开发指南+源代码.part2

     5.27 Visual C++ 2010 Check Box控件简介与开发 262  5.28 Visual C++ 2010 Radio Button控件简介与开发 264  5.28.1 为单选按钮控件分组 264  5.28.2 获得被选中的单选按钮的文本 264  5.29 Visual C++ 2010 ...

    Visual C++2010开发权威指南(共三部分).part1.rar

    5.2 Visual C++ 2010 List Box控件简介与开发 193 5.3 Visual C++ 2010 Com boBox控件简介与开发 195 5.3.1 创建扩展组合框控件 196 5.3.2 在扩展组合框控件中使用 5.3.2 图像列表 197 5.3.3 设置各项的图像 197 ...

    VC++常用功能实例

    问:有RichEdit控件的对话框无法正常显示? 79 问:DLL中的模板成员函数? 79 问:CFormView中的上下文帮助? 80 问:CArchive类的WriteObject函数问题? 80 问:RegisterWindowMessage中的BroadcastSystemMessage如何...

    Visual C++ 编程资源大全(源码 控件)

    (28KB)&lt;END&gt;&lt;br&gt;52,treelist.zip 这个控件类库结合了树状控件与列表视图控件,使用起来自然功能更加强大(60KB)&lt;END&gt;&lt;br&gt;53,Wizard.zip 在安装程序中,向导是必不可少的。你有没有对现在那些通用的界面感到平淡...

    VC++常用的共用函数100多页

    问:有RichEdit控件的对话框无法正常显示? 79 问:DLL中的模板成员函数? 79 问:CFormView中的上下文帮助? 80 问:CArchive类的WriteObject函数问题? 80 问:RegisterWindowMessage中的BroadcastSystemMessage如何...

    vc++ 应用源码包_1

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    vc++ 应用源码包_2

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    vc++ 应用源码包_3

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    vc++ 应用源码包_6

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    vc++ 应用源码包_5

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    vc++ 开发实例源码包

    Skin_Combo_Box_demo 自绘Combox控件的实例。 SkinList_Demo 自绘List控件的实例。 Smile 自绘List控件的实例。 SOCK登录SSL加密网站 调用ie接口。主要的代码在IEComCtrlSink中。 sqlite3使用 引用了Splayer中的...

    window32 API大全 win32编程

    API函数是构筑整个Windows框架的基石,只有充分理解和利用API函数,才能深入到Windows的内部,充分发挥各种32位平台的强大功能和灵活性,才能成功地扩展和突破类库、控件和可视开发环境的限制。 Win32 API即为...

Global site tag (gtag.js) - Google Analytics