龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

CRichEditCtrl实现MSN背景,字体,超链接[组图]

时间:2009-12-22 15:42来源:未知 作者:admin 点击:
分享到:
相关链接:CRichEditCtrl实现MSN/QQ动画表情 首先对标题说明一下,在MSN中,聊天的窗口可能是一个自定义的类。大家用Spy ++可以看看。 对与自定义窗口,可以使用CreateWindow, SetWindowLong或者
  相关链接:CRichEditCtrl实现MSN/QQ动画表情
  
  首先对标题说明一下,在MSN中,聊天的窗口可能是一个自定义的类。大家用Spy ++可以看看。  对与自定义窗口,可以使用CreateWindow, SetWindowLong或者是SubclassWindow实现,不过这不是我现在讨论的话题。
  
  好, 先看看效果再说(如图1):
  

  图1:实现效果图一、MSN超链接的实现
  关于HyperLink的是实现,我找到了一种自绘的实现方式,自己维护,效果也很不错(如图2)。
  
  图2:超链接  就在我觉得实现OLE的插入有难度时,就想过了直接自己绘制,自己维护,但是觉得做法不是很正统,对不起Microsoft给我留的IRichEditOle接口,就没有使用这种解决方法。我们大致讲一下实现方式:
  
  定义一个strUCt描述每个hyperlink对象:
  1typedef struct _HYPERLINKINFO
  2{
  3 CRect rectDimension;
  4 CString csLinkText;
  5 UINT unLinkDlgID;
  6 inline operator = (struct _HYPERLINKINFO linkInfo){ rectDimension = linkInfo.rectDimension;csLinkText = linkInfo.csLinkText;unLinkDlgID = linkInfo.unLinkDlgID;}
  7}HYPERLINKINFO,*LPHYPERLINKINFO;
  这里有一个UINT unLinkDlgID,是来描述点击这个link后弹出的dialog的。
  
  CRect rectDimension;用来保存这个link的rect,以检查鼠标是否在其中,以显示鼠标光标;对于多links,他使用了一个CList来维护(或者可以选择使用std::list):
  1CList m_lsLinks;
  其中很要害的就是判定鼠标是不是在一个link的rect内。这种实现的确很不错,但是我要说的是另一个更现代的实现方式,实现起来也简单得多,要害的就是有个Style =>。ENM_LINK 这个style在RichEdit20中才有,所以我们应该使用RichEdit20A!我们维护这样一个类:
  1class _AFX_RICHEDITEX_STATE
  2{
  3public:
  4 _AFX_RICHEDITEX_STATE();
  5 virtual ~_AFX_RICHEDITEX_STATE();
  6 HINSTANCE m_hInstRichEdit20 ;
  7};
更多内容请看MSN专题专题,或
  实现代码就一点,就是用这个类来Load Richedit20
  1_AFX_RICHEDITEX_STATE::_AFX_RICHEDITEX_STATE()
  2{
  3 m_hInstRichEdit20 = NULL ;
  4}
  5
  6
  7
  8_AFX_RICHEDITEX_STATE::~_AFX_RICHEDITEX_STATE()
  9{
   <!-- frame contents -->
<!-- /frame contents --> 10 if( m_hInstRichEdit20 != NULL )
  11 {
  12 ::FreeLibrary( m_hInstRichEdit20 ) ;
  13 }
  14}
  15
  16
  17
  18_AFX_RICHEDITEX_STATE _afxRichEditStateEx ;
  19
  20
  21
  
   22BOOL PASCAL AfxInitRichEditEx()
  23{
  24 if( ! ::AfxInitRichEdit() )
  25 {
  26 return FALSE ;
  27 }
  28 _AFX_RICHEDITEX_STATE* l_pState = &_afxRichEditStateEx ;
  29 if( l_pState->m_hInstRichEdit20 == NULL )
  30 {
  31 l_pState->m_hInstRichEdit20 = LoadLibraryA("RICHED20.DLL") ;
  32 }
  33 return l_pState->m_hInstRichEdit20 != NULL ;
  34}
  然后在我们创建RichEdit的时候,就使用 RichEdit20A 作为ClassName;
  1CreateEx(dwExStyle, _T( "RichEdit20A" ), NULL, dwStyle, rect, pParentWnd, nID, NULL );
  这样一来,你就使用先进的RichEdit20A了, 可以简单的实现你的要的功能了。先给你的RichEditCtrl设置EN_LINK Style, 代码量也是少得可怜:
  1unsigned mask = ::SendMessage(m_hWnd, EM_GETEVENTMASK, 0, 0);
  2::SendMessage(m_hWnd, EM_SETEVENTMASK, 0, mask ENM_LINK ENM_MOUSEEVENTS ENM_SCROLLEVENTS ENM_KEYEVENTS);
  3::SendMessage(m_hWnd, EM_AUTOURLDETECT, true, 0);
  假如你觉得使用CRichEditCtrl的Member Function更方便的话,当然可以使用GetEventMak和SetEventMask,同样方便。为了扩展的需要,RichEdit20并没有定义为,当你点击links的时候用浏览器打开这个页面,而是让你处理这个点击,你就可以任意的扩展!
  ON_NOTIFY(EN_LINK, IDC_SENDMSG, OnRichEditExLink )
  知道在哪加吧。这个IDC_SENGMSG就是RichEdit的Resource ID, OnRichEditExLink就是处理这个消息的函数咯,然后看最后的代码。
  1void CMsgerDlg::OnRichEditExLink( NMHDR* in_pNotifyHeader, LRESULT* out_pResult )
  2{
  3 ENLINK* l_pENLink = ( ENLINK* )in_pNotifyHeader ;
  4 *out_pResult = 0 ;
  5 switch( l_pENLink->msg )
  6 {
  7 default:{}
  8 break ;
  9
  10 case WM_LBUTTONDOWN:
  11 {
  12 CString l_URL ;
  13 CHARRANGE l_CharRange ;
  14 CExtRichEdit *m_TempEdit;
  15 m_TempEdit = (CExtRichEdit*)CExtRichEdit::FromHandle(l_pENLink->nmhdr.hwndFrom);
  16 m_TempEdit->GetSel( l_CharRange ) ;
  17 m_TempEdit->SetSel( l_pENLink->chrg ) ;
  18 l_URL = m_TempEdit->GetSelText() ;
  19 m_TempEdit->SetSel( l_CharRange ) ;
  20 CWaitCursor l_WaitCursor ;
  21 ShellExecute( this->GetSafeHwnd(), _T( "open" ), l_URL, NULL, NULL, SW_SHOWNORMAL ) ;
  22 *out_pResult = 1 ;
  23 }
  24 break ;
  25
  26 case WM_LBUTTONUP:
  27 {
  28 *out_pResult = 1 ;
  29 }
  30 break ;
  31 }
  超链接就说到这里,下面是背景跟字体。
  
更多内容请看MSN专题专题,或
  
  二、MSN背景的实现
  
  关于背景的实现,有人认为不能直接处理WM_ERASEBKGND,还有需要Hook才行,或者是重写WM_PAINT处理函数。
  
   <!-- frame contents -->
<!-- /frame contents -->   但其实很简单的,要害的一点就是RICHEDIT20A的WS_EX_TRANSPARENT这个Style。有了它,我们就可以Create一个透明的CRichEditCtrl了,然后就直接在Dialog上绘图片!这样看起来就是RichEdit的背景了。首先:
  
   1m_Chat.CreateEx(WS_EX_TRANSPARENT, "RICHEDIT20A", "", WS_VISIBLE WS_CHILD
  2 WS_CLIPCHILDREN WS_VSCROLL ES_MULTILINE ES_READONLY
  3 ES_AUTOVSCROLL ES_LEFT ES_WANTRETURN, rt, this, IDC_CHAT, NULL);
  这样就得到了一个透明的RichEdit了。
  
  然后就可以使用两种方法绘制窗体背景。
  (1)响应WM_ERASEBKGND
  
  (2)
  OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  还有就是透明的代价,RichEdit不会刷新背景了,得自己做。算最小刷新Rect就是讨论的主要问题了。这里就不作具体讨论了。
  
  
  三、MSN字体的实现
  
  字体的实现是很简单的,这里没有使用rtf,觉得太烦琐了,而且也不需要那么复杂的控制,仅仅是一个结构CHARFORMAT:
  1typedef struct _charformat
  2{
  3 UINT cbSize;
  4 _WPAD _wPad1;
  5 DWord dwMask;
  6 DWORD dwEffects;
  7 LONG yHeight;
  8 LONG yOffset;
  9 COLORREF crTextColor;
  10 BYTE bCharSet;
  11 BYTE bPitchAndFamily;
  12 char szFaceName[LF_FACESIZE];
  13 _WPAD _wPad2;
  14} CHARFORMATA;
  相当的明白了,就不用多说;对于CRichEditCtrl,它提供的关于格式的接口有:
  SetDefaultCharFormat(CHARFORMAT &cf);
  SetSel(CHARFORMAT &cf);
  大家一看便知,不多说,那么如何使用CFontDialog选择字体呢?其实很简单咯,因为有这个东西 => CFontDialog::GetCharFormat(CHARFORMAT &cf);所以,总的实现可能就是这么简单的几句代码:
  1CFontDialog fontdlg;
  2
  3 int ret = fontdlg.DoModal();
  4 if(IDOK == ret)
  5 {
  6 CHARFORMAT cf;
  7 fontdlg.GetCharFormat(cf);
  8 m_SendMsg.SetDefaultCharFormat(cf);
  9 }
更多内容请看MSN专题专题,或
  
精彩图集

赞助商链接