我的RichEdit控件可以包含可点击的链接吗?

4

我想在编辑控件或富文本2.0控件中显示一系列字符串。之后,我希望显示的一些文本被下划线并呈蓝色。这些下划线的文本可以点击以打开另一个对话框或某种形式的内容。

是否有方法实现此功能?

1个回答

11

富文本编辑器 2.0 仅支持 自动富文本超链接, 而富文本编辑器 4.1 及更新版本(msftedit.dll)支持 友好名称超链接

您可以通过使用组合 CFE_LINKCFE_HIDDEN 字符格式标志 来在富文本编辑器 2.0 中模拟友好名称超链接。使用 CFE_LINK 对文本进行标记,并通过应用 CFE_HIDDEN 隐藏 URL。 处理 EN_LINK 通知以响应单击事件。此时,您需要进行一些解析操作来从富文本中提取隐藏的 URL。

或者,只需使用 CFE_LINK 进行文本标记,并使用 std::map 将文本映射到 URL。 只要文本与 URL 的映射关系为 1:1,则可以正常工作。

编辑:我注意到你只是想在单击链接时“打开另一个对话框”,所以在你的情况下,只应用CFE_LINK就足够了。

编辑2:如果您不需要显示格式化文本,也不需要滚动条,我建议使用SysLink control。由SysLink控件显示的链接比RichEdit控件中的链接更易于访问。前者支持TAB键浏览各个链接,而后者则不支持。

实现友好名称超链接(Rich Edit 4.1+)

免责声明:以下代码已在Win 10上进行过测试,具有创作者更新。我还没有找到时间在旧的操作系统版本下测试它。

  • In the InitInstance() method of your CWinApp-derived class, call AfxInitRichEdit5() if your version of Visual Studio supports it. Otherwise call LoadLibraryW(L"msftedit.dll").
  • Make sure the richedit control uses the right window class. The resource editor creates a RichEdit 2.0 by default. You need to manually edit the .rc file using a text editor and replace RichEdit20A or RichEdit20W by RichEdit50W. The W stands for the Unicode version of the control.
  • Call CRichEditCtrl::StreamIn() to insert the RTF containing the hyperlink(s). In the following I provide a helper function StreamInRtf() that simplifies the task of streaming a string into the control:

    struct StreamInRtfCallbackData
    {
        char const* pRtf;
        size_t size;
    };
    
    DWORD CALLBACK StreamInRtfCallback( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
    {
        StreamInRtfCallbackData* pData = reinterpret_cast<StreamInRtfCallbackData*>( dwCookie );
    
        // Copy the number of bytes requested by the control or the number of remaining characters
        // of the source buffer, whichever is smaller.
        size_t sizeToCopy = std::min<size_t>( cb, pData->size );
        memcpy( pbBuff, pData->pRtf, sizeToCopy );
    
        *pcb = sizeToCopy;
    
        pData->pRtf += sizeToCopy;
        pData->size -= sizeToCopy;
    
        return 0;
    }
    
    DWORD StreamInRtf( CRichEditCtrl& richEdit, char const* pRtf, size_t size = -1, bool selection = false )
    {
        StreamInRtfCallbackData data;
        data.pRtf = pRtf;
        data.size = ( size == -1 ? strlen( pRtf ) : size );
    
        EDITSTREAM es;
        es.dwCookie    = reinterpret_cast<DWORD_PTR>( &data );
        es.dwError     = 0;
        es.pfnCallback = StreamInRtfCallback;
    
        int flags = SF_RTF | ( selection ? SFF_SELECTION : 0 );
    
        richEdit.StreamIn( flags, es );
    
        return es.dwError;
    }
    

    Example usage (using a raw string literal here to make the RTF more readable):

    StreamInRtf( m_richedit, 
    R"({\rtf1
    {\field{\*\fldinst {HYPERLINK "https://www.stackoverflow.com" }}{\fldrslt {stackoverflow}}}\par
    Some other text\par
    })" );
    
  • To handle clicks you need to enable EN_LINK notifications for the richedit control, e. g.:

    m_richedit.SetEventMask( m_richedit.GetEventMask() | ENM_LINK );
    

    Add a handler for EN_LINK to your message map:

    BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
        ON_NOTIFY( EN_LINK, IDC_RICHEDIT1, OnLink )
    END_MESSAGE_MAP()
    

    Define the event handler method to handle mouse clicks and the return key:

    void CMyDialog::OnLink( NMHDR* pnm, LRESULT* pResult )
    {
        ENLINK* pnml = reinterpret_cast<ENLINK*>( pnm );
    
        if(   pnml->msg == WM_LBUTTONDOWN || 
            ( pnml->msg == WM_KEYDOWN && pnml->wParam == VK_RETURN ) )
        {
            CString url;
            m_richedit.GetTextRange( pnml->chrg.cpMin, pnml->chrg.cpMax, url );
            AfxMessageBox( L"URL: \"" + url + L"\"" );
    
            *pResult = 1; // message handled
        }
    
        *pResult = 0;  // enable default processing
    }
    
  • Beginning with Windows 8, the control can show a tooltip that displays the URL of the link under the mouse cursor. This feature can be enabled by sending the EM_SETEDITSTYLE message to the control:

    DWORD style = SES_HYPERLINKTOOLTIPS | SES_NOFOCUSLINKNOTIFY;
    m_richedit.SendMessage( EM_SETEDITSTYLE, style, style );
    

    In case you are missing the defines, here they are:

    #ifndef SES_HYPERLINKTOOLTIPS
        #define SES_HYPERLINKTOOLTIPS   8
    #endif
    #ifndef SES_NOFOCUSLINKNOTIFY
        #define SES_NOFOCUSLINKNOTIFY   32
    #endif 
    

我已经在普通的WinAPI中基本实现了相同的功能。Win 10完美运行,包括超链接自动工具提示。Win 7有些奇怪:链接在文本中没有高亮显示,但它确实起作用!也就是说,光标会变成“手”的形状,并发送通知。不支持工具提示。 - lariona
确认。对于Win 7,您必须设置链接的格式,使其实际上看起来像一个链接。 - zett42
1
设置RTF字符串中的文本的更简单的替代方法是使用EM_SETTEXTEX消息(必须有MFC等效项,但我在这里不是专家)。它可以识别来自富文本的纯文本,并且不需要任何回调例程。EM_STREAMIN(CRichEditCtrl :: StreamIn)可能主要用于从文件中读取。 - lariona
@zett42:我正在做类似的事情,我有一个字符串中的URL(string str = "https://www.stackoverflow.com"),我想在StreamInRtf(m_richedit, R"({\rtf1 {\field{*\fldinst {HYPERLINK "https://www.stackoverflow.com" }}{\fldrslt {stackoverflow}}}\par Some other text\par })" );中使用我的str。我该怎么做? - Sandeep Kumar
@SandeepKumar 我相信你需要插入“https://”才能使它成为有效的URL。 - zett42
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接