一、引言 用 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
|