類似 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 )實現逆過程。具體代碼參看本文附帶的工程文件。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章