Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1852414
  • 博文数量: 909
  • 博客积分: 4000
  • 博客等级: 上校
  • 技术积分: 12260
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-06 20:50
文章分类

全部博文(909)

文章存档

2008年(909)

我的朋友

分类:

2008-05-06 22:06:23

一起学习
类似 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 )实现逆过程。具体代码参看本文附带的工程文件。

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


    类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)类似 MSN 信息发送框的制作(上)
    阅读(322) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~