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

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的新菜单,编辑它就可得到你想要的右键菜单了。这和其它菜单的做法没有区别,我就不再详细介绍了。

 

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

六、多重UnDo/ReDo功能:

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

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

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

1、数据结构

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

#define UNDOMAX        30    //栈深度(最大Undo次数)

//可撤销的操作名
#define OP_DELSEL     1     //删除选择(剪切)
#define OP_REPLACE    2     //替换选择
#define OP_DELETE     3     //删除
#define OP_BACK       4     //Backspace
#define OP_INPUT      5     //输入

//Undo/Redo栈结构
typedef struct
{
    int   op;        //操作名
    int   pos;       //操作的位置
    CString str1;    //旧内容
    CString str2;    //新内容
}STACKNODE;

private:
    STACKNODE m_Stack[UNDOMAX];     //工作栈
    int utop;          //Undo栈顶指针
    int ubottom;       //Undo栈底指针
    int rtop;          //Redo栈顶指针
    int rbottom;       //Redo栈底指针
    BOOL b_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、栈操作

①初始化工作栈

void CEditBox::InitStack()
{
    for( int i=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;
}

②入栈

void CEditBox::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 )        //栈空
        return NULL;
    STACKNODE *p = &m_Stack[utop];
    rtop = utop;              //Redo入栈
    utop = utop-1;            //退栈
    if( utop<0 )
        utop = UNDOMAX-1;
    return p;                //返回退栈节点
}

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

④ReDo出栈

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

当进行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 )
        return NULL;
    return &m_Stack[utop];
}

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

3、UnDo/ReDo操作

①保存可撤销的操作

void CEditBox::SetUndo(int op,int pos,LPCTSTR str1,LPCTSTR str2)
{
    STACKNODE *ptop = GetTop();
    if( op==OP_DELETE && ptop && ptop->op==OP_DELETE && ptop->pos==pos )
    {
        ptop->str1+=str1;
        return;
    }
    STACKNODE node;
    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键操作,并进行相应处理。

void CEditBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    CString Text, SelStr;
    int pos, 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;
    }
    else if( 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("") );
            else if( 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键只能用它来处理。

void CEditBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    CString SelStr;
    ReadSelText( SelStr );
    int pos1, end, pos2;
    GetSel( pos1, end );    //获取输入前的光标位置
    CEdit::OnChar(nChar, nRepCnt, nFlags);    //处理键盘输入
    if( !b_DelFlag )
    {
        CString Text, 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菜单。

//判断能否撤销
BOOL CEditBox::isCanUndo()
{
    return utop!=ubottom;
}

//判断能否重做
BOOL CEditBox::isCanRedo()
{
    return utop!=rtop;
}

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

②EditUndo()和EditRedo()

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

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

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

③EditReplace()和RepAll()

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

//替换选择
void CEditBox::EditReplace(LPCTSTR str)
{
    CString Text, SelStr;
    int pos, SelLen;
    GetText( pos, Text, SelStr, SelLen );
    SetUndo( OP_REPLACE, pos, SelStr, str );    //保存到Undo
    ReplaceSel( str );    //替换选择
}

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

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

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

④剪切和粘贴

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

//剪切(代替Cut())
void CEditBox::EditCut()
{
    CString Text, SelStr;
    int pos, SelLen;
    GetText( pos, Text, SelStr, SelLen );
    SetUndo( OP_DELSEL, pos, SelStr, _T("") );    //保存到Undo
    Cut();        //剪切
}

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

    HANDLE StrHandle;
    StrHandle = ::GetClipboardData(CF_TEXT);

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

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

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

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

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

//全部删除
void CEditBox::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输入
void CEditTestView::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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1,WINAPI_OCX.zip封装了部分Windows API的控件(92KB)2,HeaderCtrl.zip多行标题的CListCtrl(19KB)3,RoundBut.zip你需要圆形的按钮吗?这个类已经替你做好了,它可是有正常、平面、下推几种风格的,快使用它吧(25KB)4,TransBut.zip实现背景透明的按钮类(306KB)5,AviButton.zip这个类库可以在按钮上显示AVI动画,很酷的(68KB)6,cirbutton.zip一个圆形的可下推按钮,还不错,可以试试看(50KB)7,anibutton.zip这是一个可以使用DIB显示动画的按钮类库,值得一试(186KB)8,bitbutton.zip这个类库允许你在按钮上使用位图和文字(9KB)9,CButtonST.zip只要你的程序中使用按钮,这个类库就使你可以轻松做出图文并茂的按钮来(133KB)10,hoverbut.zip这个类库是对鼠标敏感的按纽,你用它可以做出象Office助手提示选项那样的东东来(24KB)11,menubut.zip当用户单击一个按钮时弹出一个菜单(5KB)12,TrackBut.zip也是一个位图按钮。不过可比MFC提供的那个好多了!(222KB)13,Stranbut.zip你需要不规则形状的按钮吗?这个类已经替你做好了,使用它吧(67KB)14,tributton.zip你需要三角形的按钮吗?这个类已经替你做好了,使用它吧(31KB)15,butpicker.zip你想有一个选择颜色的下拉列表吗?胜至你想有一个选择图形的下拉列表吗?这个类是你所需的,下载一个回去试试,分析分析一定会有收获(85KB)16,flat_comb.zip你有没有想过在你的应用程序中加入"浮动"的组合框,就象Microsoft Office中的那样?用这个类就能轻松搞定(21KB)17,fontcombo.zip这是一个用于选择字体的组合框类库,而且直接可以预览(46KB)18,icon_comb.zip这是一个选择图标的组合框的类(2KB)19,mrucombo.zip这个聪明的组合框具有IE那样的自动纪录历史的功能,你最近使用过的文件它会个个记在帐上。需要设计"History"功能的朋友赶快下载一个吧!(21KB)20,mulcombo2.zip这是一个具有多列功能的组合框,如果你的选择项需要多列显示的话一定需要这个东东(44KB)21,autocomp.zip这个聪明的组合框可以根据你的部分输入和可选项替你自动完成,就像IE的地址栏一样。是不是很好?快下载一个吧(24KB)22,ColorSel.zip一个用于颜色选择的组合框的例子(41KB)23,DriveBox.zip一个用于选择驱动器的组合框,告诉你如何实现自画控件以及如何通过Windows Shell得到驱动器的图标(128KB)24,mccombobox.zip这是一个多列的组合框类库(22KB)25,mlistbox.zip这个例子讨论了列表框的单/复选问题,值得一看(22KB)26,checklist.zip这是一个多列且具有检查框的列表框。使用它,你可以制作诸如安装程序中的自定义安装明细表等等。(50KB)27,ListBoxEx.zip你知道怎么让列表框水平滚动吗?这个类会向你解释一切(85KB)28,FlatBox.zip浮动效果的列表框,很酷的!(3KB)29,ColorBox.zip这是一个可以以不同颜色显示列表项目的列表框类库(17KB)30,iconpick.zip一个图标选择的列表框(32KB)31,hexedit.zip这是一个从Cedit派生的十六进制的编辑框类库(30KB)32,histedit.zip注意过Visual C++的Output窗口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值