类似 MSN 信息发送框的制作(上)

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

类似 MSN 信息发送框的制作(上)

作者:北方工业大学 阙荣文 (querw)

下载源代码

一、引言

用 MSN 和 QQ 等聊天的时候,当用户输入特定意义的字符串时,系统回自动用一张小图片替代.比如输入" : ) "系统

会用一个小笑脸代替。我要实现的就是这样一个信息输入框

。这个信息输入框由两部分组成:图案选择器和多功能文本框。本篇介绍多功能文本框。

二、原理简介

1、主要功能用CRichEditCtrl实现,像设置字体,设置字体颜色,字号等等CRichEditCtrl都提供了很完善的支持,我就不一一赘述了。

CRichEditCtrl 主要的不足在于以下几个方面:

(1).没有右键菜单

(2).不能插入图片(这是实现转义字符显示的关键)

(3).RTF格式输入输出不够方便(涉及到回调函数的递归调用)

我扩展了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对象来实现.主要用两个函数,还涉及到了和多接口的调用。

(1)从文件创建OLE对象OleCreateFromFile();

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格式字符串

CRichEditCtrl 提供了两个函数StreamIn()和StreamOut()来实现这个功能,输出的内容包含文本信息和字体信息。我把这两个函数重新包装了一下

,用GetRTF()把格式文本返回到一个CString变量中SetRTF(CString )实现逆过程。具体代码参看本文附带的工程文件。

三、到此,这个多功能文本框就已经基本能满足我的要求了。但是如何选择表情符号? 如何自动替换? 还是个问题。(待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有 導航