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

一、引言

用 MSN 和 QQ 等聊天的时候,当用户输入特定意义的字符串时,系统回自动用一张小图片替代.比如输入" : ) "系统
会用一个小笑脸代替。我要实现的就是这样一个信息输入框 。这个信息输入框由两部分组成:图案选择器和多功能文本框。本篇介绍多功能文本框。

二、原理简介

1、主要功能用CRichEditCtrl实现,像设置字体,设置字体颜色,字号等等CRichEditCtrl都提供了很完善的支持,我就不一一赘述了。

CRichEditCtrl 主要的不足在于以下几个方面

(1).没有右键菜单

(2).不能插入图片(这是实现转义字符显示的关键)

(3).RTF格式输入输出不够方便(涉及到回调函数的递归调用)

我扩展了CRichEditCtrl类CRichEditCtrlEx实现了上述功能.参考了很多网上的文章,对所有公开源码的开发人员表示崇高的敬意!!

2、实现右键菜单:

///生成右键菜单

01.void CRichEditCtrlEx::OnRButtonUp(UINT nFlags, CPoint point)  
02.{ 
03.    // TODO: Add your message handler code here and/or call default 
04.    //设置为焦点 
05.    SetFocus(); 
06.    //创建一个弹出式菜单 
07.    CMenu popmenu; 
08.    popmenu.CreatePopupMenu(); 
09.    //添加菜单项目 
10.    popmenu.AppendMenu(0, ID_RICH_UNDO, "&Undo"); 
11.    popmenu.AppendMenu(0, MF_SEPARATOR); 
12.    popmenu.AppendMenu(0, ID_RICH_CUT, "&Cut"); 
13.    popmenu.AppendMenu(0, ID_RICH_COPY, "C&opy"); 
14.    popmenu.AppendMenu(0, ID_RICH_PASTE, "&Paste"); 
15.    popmenu.AppendMenu(0, ID_RICH_CLEAR, "C&lear"); 
16.    popmenu.AppendMenu(0, MF_SEPARATOR); 
17.    popmenu.AppendMenu(0, ID_RICH_SELECTALL, "Select &All"); 
18.    popmenu.AppendMenu(0, MF_SEPARATOR); 
19.    popmenu.AppendMenu(0, ID_RICH_SETFONT, "Select &Font"); 
20.      
21.    //初始化菜单项 
22.    UINT nUndo=(CanUndo() ? 0 : MF_GRAYED ); 
23.    popmenu.EnableMenuItem(ID_RICH_UNDO, MF_BYCOMMAND|nUndo); 
24.      
25.    UINT nSel=((GetSelectionType()!=SEL_EMPTY) ? 0 : MF_GRAYED) ; 
26.    popmenu.EnableMenuItem(ID_RICH_CUT, MF_BYCOMMAND|nSel); 
27.    popmenu.EnableMenuItem(ID_RICH_COPY, MF_BYCOMMAND|nSel); 
28.    popmenu.EnableMenuItem(ID_RICH_CLEAR, MF_BYCOMMAND|nSel); 
29.      
30.    UINT nPaste=(CanPaste() ? 0 : MF_GRAYED) ; 
31.    popmenu.EnableMenuItem(ID_RICH_PASTE, MF_BYCOMMAND|nPaste); 
32.      
33.    //显示菜单 
34.    CPoint pt; 
35.    GetCursorPos(&pt); 
36.    popmenu.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this); 
37.    popmenu.DestroyMenu(); 
38.    CRichEditCtrl::OnRButtonDown(nFlags, point); 
39.    CRichEditCtrl::OnRButtonUp(nFlags, point); 
40.}

3、关于如何把图片插入到RichEdit中,国外由很多文章介绍,都是(我看到的都是)通过插入OLE对象来实现.主要用两个函数,还涉及到了和多接口的调用。

(1)从文件创建OLE对象OleCreateFromFile();

01.void CRichEditCtrlEx::InsertBitmap(CString szFileName) 
02.{ 
03.    USES_CONVERSION; 
04.    SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &m_lpLockBytes); 
05.    if (sc != S_OK) 
06.        AfxThrowOleException(sc); 
07.    ASSERT(m_lpLockBytes != NULL); 
08.      
09.    sc = ::StgCreateDocfileOnILockBytes(m_lpLockBytes, 
10.        STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage); 
11.    if (sc != S_OK) 
12.    { 
13.        VERIFY(m_lpLockBytes->Release() == 0); 
14.        m_lpLockBytes = NULL; 
15.        AfxThrowOleException(sc); 
16.    } 
17.      
18.    // attempt to create the object 
19.    sc = ::OleCreateFromFile(CLSID_NULL, T2COLE(szFileName), 
20.        IID_IUnknown, OLERENDER_DRAW, NULL, NULL,  
21.        m_lpStorage, (void **)&m_lpObject); 
22.    if ( sc != S_OK ) 
23.    { 
24.        TCHAR * lpMsgBuf; 
25.        ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |  
26.            FORMAT_MESSAGE_FROM_SYSTEM, NULL,  
27.            ::GetLastError(), 
28.            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
29.            (LPTSTR) &lpMsgBuf, 0, NULL ); 
30.        CString msg( lpMsgBuf ); 
31.        msg += _T("\n\n\nThe following file, created in\n"
32.            "Simulation->Plot, may be missing due\n"
33.            "to not doing a File->Save Workspace:\n\n" ); 
34.        msg += szFileName; 
35.        AfxMessageBox( msg, MB_OK ); 
36.        ::LocalFree( lpMsgBuf ); 
37.        return; 
38.    } 
39.      
40.    // m_lpObject is currently an IUnknown, convert to IOleObject 
41.    if (m_lpObject != NULL) 
42.    { 
43.        LPUNKNOWN lpUnk = m_lpObject; 
44.        m_lpObject = QUERYINTERFACE(lpUnk, IOleObject); 
45.        lpUnk->Release(); 
46.        if (m_lpObject == NULL) 
47.            AfxThrowOleException(E_OUTOFMEMORY); 
48.    } 
49.      
50.    // cache the IViewObject interface 
51.    m_lpViewObject = QUERYINTERFACE(m_lpObject, IViewObject2); 
52.    if (m_lpViewObject == NULL) 
53.        return; 
54.      
55.    // setup for advises; we assume that OLE cleans them up properly 
56.    LPADVISESINK lpAdviseSink = 
57.        (LPADVISESINK)GetInterface(&IID_IAdviseSink); 
58.      
59.    // set up view advise 
60.    VERIFY(m_lpViewObject->SetAdvise(DVASPECT_CONTENT, 0, lpAdviseSink) 
61.        == S_OK); 
62.      
63.    // the server shows these in its user-interface 
64.    //  (as document title and in File Exit menu) 
65.    m_lpObject->SetHostNames(T2COLE(AfxGetAppName()), 
66.        T2COLE(_T("Test"))); 
67.      
68.    // all items are "contained" -- this makes our reference to this object 
69.    //  weak -- which is needed for links to embedding silent update. 
70.    OleSetContainedObject(m_lpObject, TRUE); 
71.      
72.    CHARRANGE cr; 
73.    this->GetSel( cr ); 
74.    cr.cpMin = cr.cpMax -1; 
75.    this->SetSel( cr ); 
76.      
77.    REOBJECT reo; 
78.    memset( &reo, 0, sizeof( reo ) ); 
79.    reo.cbStruct = sizeof( reo ); 
80.    CLSID classID; 
81.    if ( m_lpObject->GetUserClassID( &classID ) != S_OK) 
82.        classID = CLSID_NULL; 
83.    reo.clsid = classID; 
84.    reo.cp = REO_CP_SELECTION; 
85.    reo.poleobj = m_lpObject; 
86.    reo.pstg = m_lpStorage; 
87.    LPOLECLIENTSITE lpClientSite; 
88.    this->GetIRichEditOle()->GetClientSite( &lpClientSite ); 
89.    reo.polesite = lpClientSite; 
90.    SIZEL sizel; 
91.    sizel.cx = sizel.cy = 0; // let richedit determine initial size 
92.    reo.sizel = sizel; 
93.    reo.dvaspect = DVASPECT_CONTENT; 
94.    reo.dwFlags = REO_RESIZABLE; 
95.    reo.dwUser = 0; 
96.    HRESULT hr = this->GetIRichEditOle()->InsertObject( &reo ); 
97.      
98.}


(2)根据位图句柄创建OleCreateStaticFromData();用这个函数可以把资源中的图片插入到文本框中

01.void CRichEditCtrlEx::InsertBitmap(HBITMAP hBitmap) 
02.{ 
03.    STGMEDIUM stgm; 
04.    stgm.tymed = TYMED_GDI;    // Storage medium = HBITMAP handle 
05.    stgm.hBitmap = hBitmap; 
06.    stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium 
07.      
08.    FORMATETC fm; 
09.    fm.cfFormat = CF_BITMAP;    // Clipboard format = CF_BITMAP 
10.    fm.ptd = NULL;       // Target Device = Screen 
11.    fm.dwAspect = DVASPECT_CONTENT;   // Level of detail = Full content 
12.    fm.lindex = -1;       // Index = Not applicaple 
13.    fm.tymed = TYMED_GDI;   
14.      
15.    创建输入数据源 
16.    IStorage *pStorage;  
17.      
18.    ///分配内存 
19.    LPLOCKBYTES lpLockBytes = NULL; 
20.    SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes); 
21.    if (sc != S_OK) 
22.        AfxThrowOleException(sc); 
23.    ASSERT(lpLockBytes != NULL); 
24.      
25.    sc = ::StgCreateDocfileOnILockBytes(lpLockBytes, 
26.        STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &pStorage); 
27.    if (sc != S_OK) 
28.    { 
29.        VERIFY(lpLockBytes->Release() == 0); 
30.        lpLockBytes = NULL; 
31.        AfxThrowOleException(sc); 
32.    } 
33.    ASSERT(pStorage != NULL); 
34.      
35.    COleDataSource *pDataSource = new COleDataSource; 
36.    pDataSource->CacheData(CF_BITMAP, &stgm); 
37.    LPDATAOBJECT lpDataObject =  
38.        (LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject); 
39.      
40.    ///获取RichEdit的OLEClientSite 
41.    LPOLECLIENTSITE lpClientSite; 
42.    this->GetIRichEditOle()->GetClientSite( &lpClientSite ); 
43.      
44.      
45.    ///创建OLE对象 
46.    IOleObject *pOleObject; 
47.    sc = OleCreateStaticFromData(lpDataObject,IID_IOleObject,OLERENDER_FORMAT, 
48.        &fm,lpClientSite,pStorage,(void **)&pOleObject); 
49.    if(sc!=S_OK) 
50.        AfxThrowOleException(sc); 
51.      
52.    ///插入OLE对象 
53.      
54.    REOBJECT reobject; 
55.    ZeroMemory(&reobject, sizeof(REOBJECT)); 
56.    reobject.cbStruct = sizeof(REOBJECT); 
57.      
58.    CLSID clsid; 
59.    sc = pOleObject->GetUserClassID(&clsid); 
60.    if (sc != S_OK) 
61.        AfxThrowOleException(sc); 
62.      
63.    reobject.clsid = clsid; 
64.    reobject.cp = REO_CP_SELECTION; 
65.    reobject.dvaspect = DVASPECT_CONTENT; 
66.    reobject.poleobj = pOleObject; 
67.    reobject.polesite = lpClientSite; 
68.    reobject.pstg = pStorage; 
69.      
70.    HRESULT hr = this->GetIRichEditOle()->InsertObject( &reobject ); 
71.      
72.}

4、读取/写入RTF格式字符串

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

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

 

http://www.vckbase.com/index.php/wv/821.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值