科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网网络频道类似 MSN 信息发送框的制作(上)

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

用 MSN和 QQ 等聊天的时候,当用户输入特定意义的字符串时,系统回自动用一张小图片替代.比如输入" : ) "系统会用一个小笑脸代替。我要实现的就是这样一个信息输入框 。

作者:zdnet安全频道 来源:论坛整理 2008年8月5日

关键字: 即时通信 msn

  • 评论
  • 分享微博
  • 分享邮件

  一、引言

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

  收藏 http://www.qqread.com/msn/k352008.html 更多内容请看MSN专题、MSN设置专题,或进入讨论组讨论。

  CRichEditCtrl 提供了两个函数StreamIn()和StreamOut()来实现这个功能,输出的内容包含文本信息和字体信息。我把这两个函数重新包装了一下 ,用GetRTF()把格式文本返回到一个CString变量中SetRTF(CString )实现逆过程。具体代码参看本文附带的工程文件。

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

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章