CRichEditCtrl 超文本编辑 常见问题

本文详细介绍了使用CRichEditCtrl进行超文本编辑时遇到的问题及其解决方法,包括:加载RICHED20.DLL、字数限制、换行切换、自动滚动、设置只读、响应onChange、插入位图、链接功能、GIF动画显示及IRichEditOleCallback的使用等。还提供了一些实用的函数应用示例,如设置字体、行间距、背景透明等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CRichEditCtrl 超文本编辑

一.常见问题
a.可以编译,不能执行的

AfxInitRichEdit();

b.升级默认的Riched版本(默认的有一些bug)
可在InitInstance中添加
LoadLibrary("RICHED20.DLL")
最后注意 FreeLibrary

如果是CRichEditView基类的可用
BOOL CXXXXXXView::PreCreateWindow(CREATESTRUCT& cs)
{
//装入rich edit version 2.0
    if (LoadLibraryA("RICHED20.DLL") == NULL)
    {
        AfxMessageBox(_T("Fail to load /"riched20.dll/"."),MB_OK | MB_ICONERROR);
        PostMessage(WM_QUIT,0,0);
        return FALSE;
    }

    m_str 2.0 class

    return CRichEditView::PreCreateWindow(cs);
}

c.最后追加行
richeditctrl.SetSel(-1, -1);
richeditctrl.ReplaceSel( (LPCTSTR)str );

d.字数限制
CRichEditCtrl::LimitText(long nChars)

e.换行切换
CRichEditView的OnInitialUpdate()函数中加入下面两句:
m_nWordWrap = WrapNone;
WrapChanged();
WrapChanged实际上也是调用
ctrl.SetTargetDevice(NULL, 1); //m_nWordWrap == WrapNone
ctrl.SetTargetDevice(NULL, 0); //m_nWordWrap == WrapToWindow
还有不常用的 m_nWordWrap == WrapToTargetDevice
ctrl.SetTargetDevice(m_dcTarget, GetPrintWidth());
如果是在Dialog中,可使用SetTargetDevice,注意在属性里面加上want return

f.有时候不希望带格式的数据粘贴,可通过PasteSpecial选择性粘贴
pmyRichEditCtrl->PasteSpecial(CF_TEXT);

g.随着输入随着自动滚动条滚动到最后一行
int nFirstVisible = pmyRichEditCtrl->GetFirstVisibleLine();
if (nFirstVisible > 0)
{
   pmyRichEditCtrl->LineScroll(-nFirstVisible, 0);
}

m_cRichEdit.PostMessage(WM_VSCROLL, SB_BOTTOM,0);


h.设置UNDO的次数(只能用在RICHED20以上,即默认不支持,必须升级)
SendMessage(EM_SETTEXTMODE,TM_MULTILEVELUNDO,0);
TM_MULTILEVELUNDO 支持多取消(默认值).可通过EM_SETUNDOLIMIT设置最大次数
SendMessage(EM_SETUNDOLIMIT,100,0);

i.响应OnChange
EM_SETEVENTMASK 设置 ENM_CHANGE
long lMask = GetEventMask();
lMask |= ENM_CHANGE;
lMask &= ~ENM_PROTECTED;
SetEventMask(lMask);

j.设置只读
CRichEditCtrl::SetReadOnly( BOOL bReadOnly = TRUE );
通过设置PROTECTED实现选中的文本只读,参见
http://www.codeguru.com/Cpp/controls/richedit/article.php/c2401/

 

二.函数应用
a.设置字体(主要是通过SetSelectionCharFormat)

CHARFORMAT cf;
ZeroMemory(&cf, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask|=CFM_BOLD;
cf.dwEffects|=CFE_BOLD;//设置粗体,取消用cf.dwEffects&=~CFE_BOLD;
cf.dwMask|=CFM_ITALIC;
cf.dwEffects|=CFE_ITALIC;//设置斜体,取消用cf.dwEffects&=~CFE_ITALIC;
cf.dwMask|=CFM_UNDERLINE;
cf.dwEffects|=CFE_UNDERLINE;//设置斜体,取消用cf.dwEffects&=~CFE_UNDERLINE;
cf.dwMask|=CFM_COLOR;
cf.crTextColor = RGB(255,0,0);//设置颜色
cf.dwMask|=CFM_SIZE;
cf.yHeight =200;//设置高度
cf.dwMask|=CFM_FACE;
strcpy(cf.szFaceName ,_T("隶书"));//设置字体
rich.SetSelectionCharFormat(cf);

b.设置字体的行间距
要用richedit2.0以上
试试
PARAFORMAT2 pf;
pf.cbSize = sizeof(PARAFORMAT2);
pf.dwMask = PFM_NUMBERING | PFM_OFFSET;
pf.wNumbering = PFN_BULLET;//注意PFM_NUMBERING
pf.dxOffset = 10;
VERIFY(SetParaFormat(pf));
常用的dwMask有
PFM_NUMBERING 成员 wNumbering 才起作用,项目符号,默认用PFN_BULLET
2 使用阿拉伯数字 (1, 2, 3, ...).
3 使用小写字母 (a, b, c, ...).
4 使用大写字母 (A, B, C, ...).
5 使用小写罗马数字 (i, ii, iii, ...).
6 使用大写罗马数字 (I, II, III, ...).
7 自定义,字符见成员 wNumberingStart.
PFM_OFFSET 成员 dxOffset 才起作用,缩进,单位twips
PFM_STARTINDENT 成员 dxStartIndent 才起作用,首行缩进
PFM_SPACEAFTER 成员 dySpaceAfter 才起作用,段间距
PFM_LINESPACING 成员 dyLineSpacing 才起作用,行间距

c.设置CRichEditCtrl(2.0)背景透明
long style = ::GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
style &= WS_EX_TRANSPARENT;
::SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, style);
或 CreateEx,然后把WS_EX_TRANSPARENT样式加上

e.得到内容有三种
1)GetWindowText
2)使用EM_GETTEXTEX
GETTEXTEX gt;
gt.cb = 200;
gt.flags = GT_DEFAULT;
gt.codepage = CP_ACP ;
gt.lpDefaultChar = NULL;
gt.lpUsedDefChar = NULL;
SendMessage(EM_GETTEXTEX,(WPARAM)>,(LPARAM)text);
3)StreamOut(主要用于RTF等格式输出)
static DWORD CALLBACK
    MyStreamOutCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
   CFile* pFile = (CFile*) dwCookie;

   pFile->Write(pbBuff, cb);
   *pcb = cb;

   return 0;
}


   CFile cFile(TEXT("myfile.rtf"), CFile::modeCreate|CFile::modeWrite);
   EDITSTREAM es;
   es.dwCookie = (DWORD) &cFile;//设置用例参数,以便回调函数调用
   es.pfnCallback = MyStreamOutCallback;
   pmyRichEditCtrl->StreamOut(SF_RTF, es);
读入可以此类推,SetWindowText,EM_SETTEXTEX,StreamIn

f.查找字符串
FINDTEXTEX ft;
ft.chrg.cpMin = 0;
ft.chrg.cpMax = -1;
ft.lpstrText = "|";
long lPos = FindText(0, &ft);

如果要继续查找,修改cpMin,如
int nCount = 0;
do
{
long lPos = GetRichEditCtrl().FindText(0, &ft);
if( -1 == lPos) break;
ft.chrg.cpMin = lPos + strlen(ft.lpstrText);
++nCount;
}while(TRUE);

g.以Html格式保存
目前做法可先转为RTF格式,再通过RTF-to-HTML Converter
http://www.codeguru.com/Cpp/controls/richedit/conversions/article.php/c5377/

h.重载OnProtected函数得到对应的消息,如粘贴等
void CMYichEditorView::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
    ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;

    switch (pEP->msg)

    {
        case WM_KEYDOWN://按键,判断pEP->wParam
        case WM_PASTE://粘贴
        case WM_CUT://剪切
        case EM_SETCHARFORMAT:
       default:
             break;
    };

   *pResult = FALSE;
}

 

三.聊天常用
a.LINK 链接功能

步骤:

 1. 建立基于对话框的mfc工程,名字就叫showlink吧。

 2. 将richedit控件拖入对话框内,打开CShowlinkApp类,在InitInstance中添加AfxInitRichEdit()。

 3. 用classwizard新建一个CRichEditCtrl的派生类CMyEdit,并添加两个函数:

void CMyEdit::SetLink()
{
  CHARFORMAT2   cf2;  
  ZeroMemory(&cf2,   sizeof(CHARFORMAT2));//  
  cf2.cbSize   =   sizeof(CHARFORMAT2);  
  cf2.dwMask   =   CFM_LINK;  
   
  cf2.dwEffects   |=   CFE_LINK;  
  SendMessage(EM_SETCHARFORMAT,   SCF_SELECTION,   (LPARAM)&cf2);  

}

void CMyEdit::OnURLClick(NMHDR *pNmhdr, LRESULT *pResult)
{
 TCHAR   LinkChar[512];  
 ENLINK   *penLink   =   (ENLINK   *)pNmhdr;  
 if   (penLink->msg   ==   WM_LBUTTONUP)  
 {  
  SetSel(penLink->chrg);  
  long   Res   =   GetSelText((char   *)LinkChar);  
  LinkChar[Res]   =   0;   
  ShellExecute(NULL, "open", LinkChar, NULL, NULL, SW_SHOWNORMAL);  
  }  
  pResult   =   FALSE;  

}

4.在MyEdit.cpp的BEGIN_MESSAGE_MAP(CMyEdit, CRichEditCtrl)里添加一句话:ON_NOTIFY_REFLECT(EN_LINK,OnURLClick)

5.在showlinkdlg.h中添加#include "MyEdit.h",在类中添加一个成员变量CMyedit  m_cRich.

6.在CShowlinkDlg的OnInitDialog中添加两句:

 m_cRich.SetEventMask(ENM_LINK);
m_cRich.SendMessage(EM_AUTOURLDETECT, (WPARAM)true,0);

编译一下,输入个网址,点击就可以打开了

 

b.插入位图
http://www.codeguru.com/Cpp/controls/richedit/article.php/c2417/
http://www.codeguru.com/Cpp/controls/richedit/article.php/c5383/

自定义在RichEdit中插入对象的图标
http://www.blogcn.com/user3/jiangsheng/blog/1319738.html
方法基本同Knowledge Base文章Q220844 HOWTO: Insert a Bitmap Into an RTF Document Using the RichEdit Control
只是在最后插入之前调用一下IOleCache::SetData,用一个HGLOBAL作为参数,HGLOBAL里面的数据是一个METAFILEPICT结构,包含自己提供的图片

使用CRichEditView::InsertFileAsObject就可以插入图像。VC++带有一个例子WordPad。
另外可以参考“Insert any HBITMAP (Bitmap) in your RichEdit Control”(http://www.codeguru.com/richedit/richeditrc.html)。

c.显示GIF动画

//使用了QQ的图像处理控件
#import  "ImageOle.dll" named_guids

//只能加载bmp
void COleRichEditCtrl::InsertBitmap(HBITMAP hBitmap)
{
	STGMEDIUM stgm;
	stgm.tymed = TYMED_GDI;    // Storage medium = HBITMAP handle
	stgm.hBitmap = hBitmap;
	stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium

	FORMATETC fm;
	fm.cfFormat = CF_BITMAP;    // Clipboard format = CF_BITMAP
	fm.ptd = NULL;       // Target Device = Screen
	fm.dwAspect = DVASPECT_CONTENT;   // Level of detail = Full content
	fm.lindex = -1;       // Index = Not applicaple
	fm.tymed = TYMED_GDI;  

	创建输入数据源
	IStorage *pStorage; 

	///分配内存
	LPLOCKBYTES lpLockBytes = NULL;
	SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
	if (sc != S_OK)
		AfxThrowOleException(sc);
	ASSERT(lpLockBytes != NULL);

	sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
		STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &pStorage);
	if (sc != S_OK)
	{
		VERIFY(lpLockBytes->Release() == 0);
		lpLockBytes = NULL;
		AfxThrowOleException(sc);
	}
	ASSERT(pStorage != NULL);

	COleDataSource *pDataSource = new COleDataSource;
	pDataSource->CacheData(CF_BITMAP, &stgm);
	LPDATAOBJECT lpDataObject = 
		(LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);

	///获取RichEdit的OLEClientSite
	LPOLECLIENTSITE lpClientSite;
	this->GetIRichEditOle()->GetClientSite( &lpClientSite );


	///创建OLE对象
	IOleObject *pOleObject;
	sc = OleCreateStaticFromData(lpDataObject,IID_IOleObject,OLERENDER_FORMAT,
		&fm,lpClientSite,pStorage,(void **)&pOleObject);
	if(sc!=S_OK)
		AfxThrowOleException(sc);

	///插入OLE对象

	REOBJECT reobject;
	ZeroMemory(&reobject, sizeof(REOBJECT));
	reobject.cbStruct = sizeof(REOBJECT);

	CLSID clsid;
	sc = pOleObject->GetUserClassID(&clsid);
	if (sc != S_OK)
		AfxThrowOleException(sc);

	reobject.clsid = clsid;
	reobject.cp = REO_CP_SELECTION;
	reobject.dvaspect = DVASPECT_CONTENT;
	reobject.poleobj = pOleObject;
	reobject.polesite = lpClientSite;
	reobject.pstg = pStorage;

	HRESULT hr = this->GetIRichEditOle()->InsertObject( &reobject );

	delete pDataSource;

}

//加载gif、bmp等图片
void COleRichEditCtrl::insertGif(CString strFilePath)  //strFilePath   gif文件路径
{
	LPLOCKBYTES  lpLockBytes  =  NULL;  
	SCODE  sc;  
	HRESULT  hr;                      
	//print  to  RichEdit'  s  IClientSite  
	LPOLECLIENTSITE  m_lpClientSite;  
	//A  smart  point  to  IAnimator  
	ImageOleLib::IGifAnimatorPtr  m_lpAnimator;  
	//ptr  2  storage                      
	LPSTORAGE  m_lpStorage;  
	//the  object  2  b  insert  2  
	LPOLEOBJECT            m_lpObject;  
	//Create  lockbytes  
	sc  =  ::CreateILockBytesOnHGlobal(NULL,  TRUE,  &lpLockBytes);  
	if  (sc  !=  S_OK)  
		AfxThrowOleException(sc);  
	ASSERT(lpLockBytes  !=  NULL);  
	//use  lockbytes  to  create  storage  
	sc  =  ::StgCreateDocfileOnILockBytes(lpLockBytes,  
		STGM_SHARE_EXCLUSIVE  |STGM_CREATE  |STGM_READWRITE,  0,  &m_lpStorage);  
	if  (sc  !=  S_OK)  
	{  
		VERIFY(lpLockBytes->Release()  ==  0);  
		lpLockBytes  =  NULL;  
		AfxThrowOleException(sc);  
	}  
	ASSERT(m_lpStorage  !=  NULL);  
	//get  the  ClientSite  of  the  very  RichEditCtrl  
	GetIRichEditOle()->GetClientSite(&m_lpClientSite);  
	ASSERT(m_lpClientSite  !=  NULL);  
	try  
	{  
		//Initlize  COM  interface  

		hr  =  ::CoInitialize(NULL)  ;//(  NULL,  COINIT_APARTMENTTHREADED  );  
		if(  FAILED(hr)  )  
			_com_issue_error(hr);  

		//Get  GifAnimator  object  
		//here,  I  used  a  smart  point,  so  I  do  not  need  to  free  it  
		hr  =  m_lpAnimator.CreateInstance(ImageOleLib::CLSID_GifAnimator);              
		if(  FAILED(hr)  )  
			_com_issue_error(hr);  
		//COM  operation  need  BSTR,  so  get  a  BSTR  
		BSTR  path  =  strFilePath.AllocSysString();  

		//Load  the  gif  
		hr  =  m_lpAnimator->LoadFromFile(path);
		if(  FAILED(hr)  )  
			_com_issue_error(hr);  

//		TRACE0(  m_lpAnimator->GetFilePath()  );  

		//get  the  IOleObject  
		hr  =  m_lpAnimator.QueryInterface(IID_IOleObject,  (void**)&m_lpObject);  
		if(  FAILED(hr)  )  
			_com_issue_error(hr);  

		//Set  it  2  b  inserted  
		OleSetContainedObject(m_lpObject,  TRUE);  

		//2  insert  in  2  richedit,  you  need  a  struct  of  REOBJECT  
		REOBJECT  reobject;  
		ZeroMemory(&reobject,  sizeof(REOBJECT));  

		reobject.cbStruct  =  sizeof(REOBJECT);              
		CLSID  clsid;  
		sc  =  m_lpObject->GetUserClassID(&clsid);  
		if  (sc  !=  S_OK)  
			AfxThrowOleException(sc);  
		//set  clsid  
		reobject.clsid  =  clsid;  
		//can  be  selected  
		reobject.cp  =  REO_CP_SELECTION;  
		//content,  but  not  static  
		reobject.dvaspect  =  DVASPECT_CONTENT;  
		//goes  in  the  same  line  of  text  line  
		reobject.dwFlags  =  REO_BELOWBASELINE;  //REO_RESIZABLE    |  
		reobject.dwUser  =  0;  
		//the  very  object  
		reobject.poleobj  =  m_lpObject;  
		//client  site  contain  the  object  
		reobject.polesite  =  m_lpClientSite;  
		//the  storage    
		reobject.pstg  =  m_lpStorage;  

		SIZEL  sizel;  
		sizel.cx  =  sizel.cy  =  0;  
		reobject.sizel  =  sizel;  
		HWND  hWndRT  =  this->m_hWnd;  
		//Sel  all  text  
		//                        ::SendMessage(hWndRT,  EM_SETSEL,  0,  -1);  
		//                        DWORD  dwStart,  dwEnd;  
		//                        ::SendMessage(hWndRT,  EM_GETSEL,  (WPARAM)&dwStart,  (LPARAM)&dwEnd);  
		//                        ::SendMessage(hWndRT,  EM_SETSEL,  dwEnd+1,  dwEnd+1);  
		//Insert  after  the  line  of  text  
		GetIRichEditOle()->InsertObject(&reobject);  
		::SendMessage(hWndRT,  EM_SCROLLCARET,  (WPARAM)0,  (LPARAM)0);  
		VARIANT_BOOL  ret;  
		//do  frame  changing  
		ret  =  m_lpAnimator->TriggerFrameChange();  
		//show  it  
		m_lpObject->DoVerb(OLEIVERB_UIACTIVATE,  NULL,  m_lpClientSite,  0,  m_hWnd,  NULL);  
		m_lpObject->DoVerb(OLEIVERB_SHOW,  NULL,  m_lpClientSite,  0,  m_hWnd,  NULL);  

		//redraw  the  window  to  show  animation  
		RedrawWindow();  

		if  (m_lpClientSite)  
		{  
			m_lpClientSite->Release();  
			m_lpClientSite  =  NULL;  
		}  
		if  (m_lpObject)  
		{  
			m_lpObject->Release();  
			m_lpObject  =  NULL;  
		}  
		if  (m_lpStorage)  
		{  
			m_lpStorage->Release();  
			m_lpStorage  =  NULL;  
		}  

		SysFreeString(path);  
	}  
	catch(  _com_error  e  )  
	{  
		AfxMessageBox(e.ErrorMessage());  
		::CoUninitialize();              
	} 

}



 

d.IRichEditOleCallback的使用
http://61.186.252.131/Expert/topic/905/905844.xml?temp=.8379022

类似 MSN 信息发送框的制作(上)
http://www.vckbase.com/document/viewdoc/?id=1087
内容包含:实现右键菜单,图片插入,读取/写入RTF格式字符串

自定义 CRichEditCtrl 控件
http://www.vckbase.com/document/viewdoc/?id=328
内容包含:鼠标右键消息,消息映射,字体变换

PS.richedit控件升级到2.0后,先把字体设为楷体,输入汉字没有问题,但输入字母时,字母自动跳转为Arial字体,而1.0却没有这个文题,仍然是用楷体显示字母
是一个专门的设计 Dual-font, Smart font apply, 参见 http://61.186.252.131/Expert/topic/913/913807.xml?temp=.3753778

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值