分类: C/C++
2008-04-23 21:47:03
类似 MSN 信息发送框的制作(上)
作者:北方工业大学
一、引言
用 MSN 和 QQ 等聊天的时候,当用户输入特定意义的字符串时,系统回自动用一张小图片替代.比如输入" : ) "系统
会用一个小笑脸代替。我要实现的就是这样一个信息输入框
。这个信息输入框由两部分组成:图案选择器和多功能文本框。本篇介绍多功能文本框。
二、原理简介
1、主要功能用CRichEditCtrl实现,像设置字体,设置字体颜色,字号等等CRichEditCtrl都提供了很完善的支持,我就不一一赘述了。
CRichEditCtrl 主要的不足在于以下几个方面:
我扩展了CRichEditCtrl类CRichEditCtrlEx实现了上述功能.参考了很多网上的文章,对所有公开源码的开发人员表示崇高的敬意!!
2、实现右键菜单:
///生成右键菜单
void CRichEditCtrlEx::OnRButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //设置为焦点 SetFocus(); //创建一个弹出式菜单 CMenu popmenu; popmenu.CreatePopupMenu(); //添加菜单项目 popmenu.AppendMenu(0, ID_RICH_UNDO, "&Undo"); popmenu.AppendMenu(0, MF_SEPARATOR); popmenu.AppendMenu(0, ID_RICH_CUT, "&Cut"); popmenu.AppendMenu(0, ID_RICH_COPY, "C&opy"); popmenu.AppendMenu(0, ID_RICH_PASTE, "&Paste"); popmenu.AppendMenu(0, ID_RICH_CLEAR, "C&lear"); popmenu.AppendMenu(0, MF_SEPARATOR); popmenu.AppendMenu(0, ID_RICH_SELECTALL, "Select &All"); popmenu.AppendMenu(0, MF_SEPARATOR); popmenu.AppendMenu(0, ID_RICH_SETFONT, "Select &Font"); //初始化菜单项 UINT nUndo=(CanUndo() ? 0 : MF_GRAYED ); popmenu.EnableMenuItem(ID_RICH_UNDO, MF_BYCOMMAND|nUndo); UINT nSel=((GetSelectionType()!=SEL_EMPTY) ? 0 : MF_GRAYED) ; popmenu.EnableMenuItem(ID_RICH_CUT, MF_BYCOMMAND|nSel); popmenu.EnableMenuItem(ID_RICH_COPY, MF_BYCOMMAND|nSel); popmenu.EnableMenuItem(ID_RICH_CLEAR, MF_BYCOMMAND|nSel); UINT nPaste=(CanPaste() ? 0 : MF_GRAYED) ; popmenu.EnableMenuItem(ID_RICH_PASTE, MF_BYCOMMAND|nPaste); //显示菜单 CPoint pt; GetCursorPos(&pt); popmenu.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this); popmenu.DestroyMenu(); CRichEditCtrl::OnRButtonDown(nFlags, point); CRichEditCtrl::OnRButtonUp(nFlags, point); }3、关于如何把图片插入到RichEdit中,国外由很多文章介绍,都是(我看到的都是)通过插入OLE对象来实现.主要用两个函数,还涉及到了和多接口的调用。
void CRichEditCtrlEx::InsertBitmap(CString szFileName) { USES_CONVERSION; SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &m_lpLockBytes); if (sc != S_OK) AfxThrowOleException(sc); ASSERT(m_lpLockBytes != NULL); sc = ::StgCreateDocfileOnILockBytes(m_lpLockBytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage); if (sc != S_OK) { VERIFY(m_lpLockBytes->Release() == 0); m_lpLockBytes = NULL; AfxThrowOleException(sc); } // attempt to create the object sc = ::OleCreateFromFile(CLSID_NULL, T2COLE(szFileName), IID_IUnknown, OLERENDER_DRAW, NULL, NULL, m_lpStorage, (void **)&m_lpObject); if ( sc != S_OK ) { TCHAR * lpMsgBuf; ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); CString msg( lpMsgBuf ); msg = _T("\n\n\nThe following file, created in\n" "Simulation->Plot, may be missing due\n" "to not doing a File->Save Workspace:\n\n" ); msg = szFileName; AfxMessageBox( msg, MB_OK ); ::LocalFree( lpMsgBuf ); return; } // m_lpObject is currently an IUnknown, convert to IOleObject if (m_lpObject != NULL) { LPUNKNOWN lpUnk = m_lpObject; m_lpObject = QUERYINTERFACE(lpUnk, IOleObject); lpUnk->Release(); if (m_lpObject == NULL) AfxThrowOleException(E_OUTOFMEMORY); } // cache the IViewObject interface m_lpViewObject = QUERYINTERFACE(m_lpObject, IViewObject2); if (m_lpViewObject == NULL) return; // setup for advises; we assume that OLE cleans them up properly LPADVISESINK lpAdviseSink = (LPADVISESINK)GetInterface(&IID_IAdviseSink); // set up view advise VERIFY(m_lpViewObject->SetAdvise(DVASPECT_CONTENT, 0, lpAdviseSink) == S_OK); // the server shows these in its user-interface // (as document title and in File Exit menu) m_lpObject->SetHostNames(T2COLE(AfxGetAppName()), T2COLE(_T("Test"))); // all items are "contained" -- this makes our reference to this object // weak -- which is needed for links to embedding silent update. OleSetContainedObject(m_lpObject, TRUE); CHARRANGE cr; this->GetSel( cr ); cr.cpMin = cr.cpMax -1; this->SetSel( cr ); REOBJECT reo; memset( &reo, 0, sizeof( reo ) ); reo.cbStruct = sizeof( reo ); CLSID classID; if ( m_lpObject->GetUserClassID( &classID ) != S_OK) classID = CLSID_NULL; reo.clsid = classID; reo.cp = REO_CP_SELECTION; reo.poleobj = m_lpObject; reo.pstg = m_lpStorage; LPOLECLIENTSITE lpClientSite; this->GetIRichEditOle()->GetClientSite( &lpClientSite ); reo.polesite = lpClientSite; SIZEL sizel; sizel.cx = sizel.cy = 0; // let richedit determine initial size reo.sizel = sizel; reo.dvaspect = DVASPECT_CONTENT; reo.dwFlags = REO_RESIZABLE; reo.dwUser = 0; HRESULT hr = this->GetIRichEditOle()->InsertObject( &reo ); }(2)根据位图句柄创建OleCreateStaticFromData();用这个函数可以把资源中的图片插入到文本框中
void CRichEditCtrlEx::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 ); }4、读取/写入RTF格式字符串