类似 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 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 )实现逆过程。具体代码参看本文附带的工程文件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值