You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TRichEdit中自动将邮箱地址转为可交互超链接?

实现TRichEdit邮箱地址完整超链接功能

TRichEdit自带EnableURLs和ShowURLHint属性,其中EnableURLs可自动将http://www.example.com这类URL转换为超链接。我尝试通过代码给邮箱地址实现同样功能,但目前仅能实现视觉上的超链接效果(蓝色下划线),无法实现完整的超链接交互:包括鼠标悬停显示crHandPoint光标、点击触发对应事件。

邮箱超链接截图

我的当前代码如下:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Label1: TLabel;
    procedure RichEdit1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  System.RegularExpressions;

procedure DetectAndLinkifyEmailsAndURLs(RichEdit: Vcl.ComCtrls.TRichEdit);
var
  Match: System.RegularExpressions.TMatch;
  EmailPattern, URLPattern: string;
  LineIndex, LineStart, StartPos, SelLength: Integer;
  LineText: string;
  PrevSelStart, PrevSelLength: Integer;
begin
  EmailPattern := '\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b';
  URLPattern := '\b(http|https)://[^\s]+';

  // Disable redraw to avoid flicker
  SendMessage(RichEdit.Handle, WM_SETREDRAW, WPARAM(False), 0);

  try
    // Save user selection
    PrevSelStart := RichEdit.SelStart;
    PrevSelLength := RichEdit.SelLength;

    // Reset all formatting to default for the entire text
    RichEdit.SelectAll;
    RichEdit.SelAttributes.Color := Vcl.Graphics.clWindowText;
    RichEdit.SelAttributes.Style := [];

    // Process line by line
    for LineIndex := 0 to RichEdit.Lines.Count - 1 do
    begin
      LineText := RichEdit.Lines[LineIndex];
      LineStart := RichEdit.Perform(EM_LINEINDEX, LineIndex, 0); // Get line start position

      // Format email addresses in the line
      Match := System.RegularExpressions.TRegEx.Match(LineText, EmailPattern, [System.RegularExpressions.roIgnoreCase]);
      while Match.Success do
      begin
        StartPos := LineStart + Match.Index - 1;
        SelLength := Match.Length;

        RichEdit.SelStart := StartPos;
        RichEdit.SelLength := SelLength;
        RichEdit.SelAttributes.Color := Vcl.Graphics.clBlue;
        RichEdit.SelAttributes.Style := [Vcl.Graphics.fsUnderline];

        Match := Match.NextMatch;
      end;

      // Format URLs in the line
      Match := System.RegularExpressions.TRegEx.Match(LineText, URLPattern, [System.RegularExpressions.roIgnoreCase]);
      while Match.Success do
      begin
        StartPos := LineStart + Match.Index - 1;
        SelLength := Match.Length;

        RichEdit.SelStart := StartPos;
        RichEdit.SelLength := SelLength;
        RichEdit.SelAttributes.Color := Vcl.Graphics.clBlue;
        RichEdit.SelAttributes.Style := [Vcl.Graphics.fsUnderline];

        Match := Match.NextMatch;
      end;
    end;

    // Restore user selection
    RichEdit.SelStart := PrevSelStart;
    RichEdit.SelLength := PrevSelLength;

    // Ensure the current selection uses default formatting
    if (PrevSelStart >= 0) and (PrevSelLength = 0) then
    begin
      RichEdit.SelAttributes.Color := Vcl.Graphics.clWindowText;
      RichEdit.SelAttributes.Style := [];
    end;
  finally
    // Re-enable redraw
    SendMessage(RichEdit.Handle, WM_SETREDRAW, WPARAM(True), 0);
    RichEdit.Invalidate; // Refresh to reflect changes
  end;
end;

procedure TForm1.RichEdit1Click(Sender: TObject);
begin
  DetectAndLinkifyEmailsAndURLs(RichEdit1);
end;

end.

解决方案

要实现完整的邮箱超链接功能,需要从三个核心维度修改:标记文本为真正的超链接、处理鼠标光标切换、响应点击事件。

1. 标记邮箱为可交互超链接

原代码仅修改了文本样式,没有将邮箱标记为TRichEdit认可的超链接。需要通过EM_SETCHARFORMAT消息设置CFE_LINK标志,让控件识别这是可交互的超链接:

修改DetectAndLinkifyEmailsAndURLs过程中处理邮箱的代码块:

// Format email addresses in the line
Match := System.RegularExpressions.TRegEx.Match(LineText, EmailPattern, [System.RegularExpressions.roIgnoreCase]);
while Match.Success do
begin
  // 修正位置计算:LineStart是行起始索引,Match.Index是文本内索引,直接相加即可
  StartPos := LineStart + Match.Index;
  SelLength := Match.Length;

  RichEdit.SelStart := StartPos;
  RichEdit.SelLength := SelLength;
  
  // 设置视觉样式
  RichEdit.SelAttributes.Color := Vcl.Graphics.clBlue;
  RichEdit.SelAttributes.Style := [Vcl.Graphics.fsUnderline];
  
  // 标记为超链接
  var cf: TCharFormat2;
  ZeroMemory(@cf, SizeOf(cf));
  cf.cbSize := SizeOf(cf);
  cf.dwMask := CFM_LINK;
  cf.dwEffects := CFE_LINK;
  SendMessage(RichEdit.Handle, EM_SETCHARFORMAT, SCF_SELECTION, LPARAM(@cf));

  Match := Match.NextMatch;
end;

2. 实现鼠标光标切换

在Form类中添加消息处理逻辑,监听WM_SETCURSOR消息,判断鼠标是否在超链接上,自动切换为手型光标:

首先在Form的private区域添加声明:

private
  function IsOverLink(RichEdit: TRichEdit): Boolean;
  procedure WndProc(var Message: TMessage); override;

然后实现这两个方法:

function TForm1.IsOverLink(RichEdit: TRichEdit): Boolean;
var
  Pos: TPoint;
  CharPos: Integer;
  cf: TCharFormat2;
begin
  Result := False;
  GetCursorPos(Pos);
  Pos := RichEdit.ScreenToClient(Pos);
  
  // 获取鼠标所在的字符位置
  CharPos := RichEdit.Perform(EM_CHARFROMPOS, 0, LPARAM(@Pos));
  if CharPos = -1 then Exit;
  
  // 检查该字符是否是超链接
  ZeroMemory(@cf, SizeOf(cf));
  cf.cbSize := SizeOf(cf);
  cf.dwMask := CFM_LINK;
  RichEdit.SelStart := CharPos;
  RichEdit.SelLength := 1;
  SendMessage(RichEdit.Handle, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(@cf));
  
  Result := (cf.dwEffects and CFE_LINK) <> 0;
end;

procedure TForm1.WndProc(var Message: TMessage);
begin
  inherited;
  if (Message.Msg = WM_SETCURSOR) and (HWND(Message.WParam) = RichEdit1.Handle) then
  begin
    if IsOverLink(RichEdit1) then
    begin
      SetCursor(Screen.Cursors[crHandPoint]);
      Message.Result := 1;
    end;
  end;
end;

3. 响应超链接点击事件

修改RichEdit1Click事件,获取点击的邮箱地址并触发自定义逻辑(比如打开系统默认邮箱客户端):

procedure TForm1.RichEdit1Click(Sender: TObject);
var
  SelStart, SelLength: Integer;
  LinkText: string;
  cf: TCharFormat2;
begin
  // 先执行原有的格式化逻辑
  DetectAndLinkifyEmailsAndURLs(RichEdit1);
  
  // 检查点击位置是否是超链接
  ZeroMemory(@cf, SizeOf(cf));
  cf.cbSize := SizeOf(cf);
  cf.dwMask := CFM_LINK;
  SendMessage(RichEdit1.Handle, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(@cf));
  
  if (cf.dwEffects and CFE_LINK) <> 0 then
  begin
    // 获取整个超链接文本范围
    SelStart := RichEdit1.SelStart;
    SelLength := 0;
    
    // 向左查找超链接起始位置
    while SelStart > 0 do
    begin
      RichEdit1.SelStart := SelStart - 1;
      RichEdit1.SelLength := 1;
      SendMessage(RichEdit1.Handle, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(@cf));
      if (cf.dwEffects and CFE_LINK) = 0 then Break;
      Dec(SelStart);
      Inc(SelLength);
    end;
    
    // 向右查找超链接结束位置
    while SelStart + SelLength < RichEdit1.GetTextLen do
    begin
      RichEdit1.SelStart := SelStart + SelLength;
      RichEdit1.SelLength := 1;
      SendMessage(RichEdit1.Handle, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(@cf));
      if (cf.dwEffects and CFE_LINK) = 0 then Break;
      Inc(SelLength);
    end;
    
    // 获取超链接文本并触发逻辑
    RichEdit1.SelStart := SelStart;
    RichEdit1.SelLength := SelLength;
    LinkText := RichEdit1.SelText;
    
    // 打开系统默认邮箱客户端
    if Pos('@', LinkText) > 0 then
    begin
      ShellExecute(Handle, 'open', PChar('mailto:' + LinkText), nil, nil, SW_SHOWNORMAL);
    end;
  end;
end;

最终效果

修改完成后,邮箱地址会具备完整的超链接能力:

  • 保持蓝色下划线的视觉样式
  • 鼠标悬停时自动切换为手型光标
  • 点击时自动打开系统默认邮箱客户端(或执行你自定义的处理逻辑)

内容的提问来源于stack exchange,提问作者user1580348

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:15:56