程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 類似MSN信息發送框的制作(上)

類似MSN信息發送框的制作(上)

編輯:關於VC++

一、引言

用 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 )實現逆過程。具體代碼參看本文附帶的工程文件。

    三、到此,這個多功能文本框就已經基本能滿足我的要求了。但是如何選擇表情符號? 如何自動替換? 還是個問題。(待續)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved