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
//只能載入bmpvoid COleRichEditCtrl::InsertBitmap(HBITMAP hBitmap){STGMEDIUM stgm;stgm.tymed = TYMED_GDI; // Storage medium = HBITMAP handlestgm.hBitmap = hBitmap;stgm.pUnkForRelease = NULL; // Use ReleaseStgMediumFORMATETC fm;fm.cfFormat = CF_BITMAP; // Clipboard format = CF_BITMAPfm.ptd = NULL; // Target Device = Screenfm.dwAspect = DVASPECT_CONTENT; // Level of detail = Full contentfm.lindex = -1; // Index = Not applicaplefm.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的OLEClientSiteLPOLECLIENTSITE 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