RichEdit不处理超链接 [英] RichEdit does not process hyperlinks

查看:173
本文介绍了RichEdit不处理超链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望我的RichEdit处理超链接,所以我按照以下说明操作: http ://delphi.about.com/od/vclusing/l/aa111803a.htm

I want my RichEdit to process hyperlinks, so I followed the instructions on: http://delphi.about.com/od/vclusing/l/aa111803a.htm

以下是我对代码所做的更改:

Here are the changes I made to the code:

interface

type
  TProgCorner = class(TForm)
    RichEdit2: TRichEdit;
    RichEdit1: TRichEdit;
    RichEdit3: TRichEdit;
    RichEdit4: TRichEdit;
    procedure FormCreate(Sender: TObject);
  private
    procedure InitRichEditURLDetection(RE: TRichEdit);
  protected
    procedure WndProc(var Msg: TMessage); override;
  end;

implementation

{$R *.DFM}

uses
  ShellAPI, RichEdit;

const
  AURL_ENABLEURL = 1;
  AURL_ENABLEEAURLS = 8;

procedure TProgCorner.InitRichEditURLDetection(RE: TRichEdit);
var
  mask: LResult;
begin
  mask := SendMessage(RE.Handle, EM_GETEVENTMASK, 0, 0);
  //In the debugger mask is always 1, for all 4 Richedits.
  SendMessage(RE.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK); 
  //returns 67108865
  SendMessage(RE.Handle, EM_AUTOURLDETECT, AURL_ENABLEURL, 0);
  //Returns 0 = success (according to MSDN), but no joy.
  //SendMessage(RE.Handle, EM_AUTOURLDETECT, AURL_ENABLEEAURLS, 0); 
  //When uncommented returns -2147024809
  //I don't think the registration works, but don't know how to fix this.
end;

procedure TProgCorner.WndProc(var Msg: TMessage);
var
  p: TENLink;
  sURL: string;
  CE: TRichEdit;
begin
  //'normal' messages do get through here, but...
  if (Msg.Msg = WM_NOTIFY) then begin
    //...the following line is never reached.
    if (PNMHDR(Msg.lParam).code = EN_LINK) then begin
      p:= TENLink(Pointer(TWMNotify(Msg).NMHdr)^);
      if (p.Msg = WM_LBUTTONDOWN) then begin
        try
          CE:= TRichEdit(ProgCorner.ActiveControl);
          SendMessage(CE.Handle, EM_EXSETSEL, 0, LPARAM(@(p.chrg)));
          sURL:= CE.SelText;
          ShellExecute(Handle, 'open', PChar(sURL), 0, 0, SW_SHOWNORMAL);
        except
          {ignore}
        end;
      end;
    end;
  end;

 inherited;
end;

procedure TProgCorner.FormCreate(Sender: TObject);
begin
  InitRichEditURLDetection(RichEdit1);
  InitRichEditURLDetection(RichEdit2);
  InitRichEditURLDetection(RichEdit3);
  InitRichEditURLDetection(RichEdit4);
  //If I set the text here (and not in the object inspector) 
  //the richedit shows a hyperlink with the 'hand' cursor.
  //but still no WM_notify message gets received in WndProc.
  RichEdit1.Text:= 'http://www.example.com';

end;

end.

然而,我嵌入到我的 RichEditx.Lines 使用对象检查器显示为纯文本(而不是链接)并单击它们不起作用。

However the hyperlinks that I embedded into my RichEditx.Lines using the object inspector show up as plain text (not links) and clicking on them does not work.

我正在使用在Win32模式下在Windows 7上运行的Delphi Seattle。

I'm using Delphi Seattle running on Windows 7 in Win32 mode.

我做错了什么?

更新

使用发布已弃用的$
的组合SendMessage(RE.Handle,EM_AUTOURLDETECT,AURL_ENABLEURL,0); 并设置 RichEditx.Text:='http://www.example.com'手动在 FormCreate 我可以让Richedit显示超链接和手柄。

但是WndProc仍然没有收到 WM_Notify 消息。

WndProc确实收到其他消息。

UPDATE
Using a combination of issuing the deprecated
SendMessage(RE.Handle, EM_AUTOURLDETECT, AURL_ENABLEURL, 0); and setting the RichEditx.Text:= 'http://www.example.com' manually in FormCreate I am able to have the Richedit display a hyperlink and handcursor.
However the WndProc still does not receive a WM_Notify message.
The WndProc does receive other messages.

UPDATE2

由于我急于简化问题,我忽略了的事实RichEdit 位于 Panel 之上。该面板使用 WM_Notify 消息,因此他们无法访问下面的表单。

UPDATE2
In my eagerness to simplify the issue I left out the fact that the RichEdit sits on top of a Panel. The panel eats the WM_Notify messages so they don't reach the form underneigh.

推荐答案

您的问题中显示的代码非常适合我 as-is 。尽管您提出索赔,但表格的 WndProc()确实会收到 EN_LINK 通知,并按预期启动点击后的网址。

The code shown in your question works perfect for me as-is. Despite your claim, the Form's WndProc() does receive the EN_LINK notifications and launches the clicked URLs, as expected.

但是,如果您将RichEdit放在另一个父控件上,例如 TPanel ,那么表单将不会收到再次 WM_NOTIFY 消息。父控件将接收它们,因此您必须继承该父控件的子类。

However, if you place a RichEdit on another parent control, like a TPanel, then the Form will not receive the WM_NOTIFY message anymore. The parent control will receive them, and as such you will have to subclass that parent control instead.

话虽如此,但可以对其进行一些改进。代码显示:

That being said, there are a few improvements that can be made to the code shown:


  1. 在您的 EN_LINK 处理中,您可以替换此:

  1. in your EN_LINK handling, you can replace this:

CE := TRichEdit(ProgCorner.ActiveControl);

改为:

CE := TRichEdit(FindControl(TWMNotify(Msg).NMHdr.hwndFrom));

通知告诉你 HWND 的发送它的RichEdit控件,以及VCL知道如何从 HWND 中检索 TWinControl

The notification tells you the HWND of the RichEdit control that is sending it, and the VCL knows how to retrieve a TWinControl from an HWND.

使用 EM_GETTEXTRANGE 检索点击的网址,而不是使用 EM_EXSETSEL SelText (这是 EM_EXGETSEL EM_GETTEXTEX 的组合)。这样,您使用的消息更少,并且根本不必操纵RichEdit的选定文本。该通知会告诉您URL的确切字符范围,因此您可以直接获取这些字符。

use EM_GETTEXTRANGE to retrieve the clicked URL, instead of using EM_EXSETSEL and SelText (which is a combination of EM_EXGETSEL and EM_GETTEXTEX). This way, you are using fewer messages, and don't have to manipulate the RichEdit's selected text at all. The notification tells you the exact range of characters for the URL, so you can just grab those characters directly.

您需要处理 HWND 娱乐。 VCL 可以随时重新创建RichEdit的 HWND 。每次创建新的 HWND 时,您必须发送 EM_SETEVENTMASK EM_AUTOURLDETECT 消息,否则您将失去自动检测功能。处理此问题的最佳方法是从 TRichEdit 派生一个类,并覆盖其 CreateWnd()方法。

you need to handle HWND recreation. The VCL may recreate a RichEdit's HWND at any time. Every time a new HWND is created, you have to send your EM_SETEVENTMASK and EM_AUTOURLDETECT messages again, otherwise you will lose your auto-detection. The best way to handle this is to derive a class from TRichEdit and override its CreateWnd() method.

由于你必须派生一个类,你可以让它处理VCL的 CN_NOTIFY 消息,而不是处理原始的 WM_NOTIFY 消息直接在父的 WndProc 中。 VCL知道如何将 WM_NOTIFY 消息重定向到发送它的VCL控件。这允许VCL控件处理自己的通知。因此,无论RichEdit放置在哪个父控件上,您的 EN_LINK 处理程序都能正常工作,您不必子类化/覆盖父级的 WndProc( ),你可以使用在访问RichEdit成员时处理消息的RichEdit的 Self 指针,例如处理属性。

Since you have to derive a class anyway, you can have it handle the VCL's CN_NOTIFY message, instead of handling the original WM_NOTIFY message directly in the parent's WndProc. The VCL knows how to redirect a WM_NOTIFY message to the VCL control that sent it. This allows VCL controls to handle their own notifications. Thus, your EN_LINK handler will work no matter what parent control the RichEdit is placed on, you don't have to subclass/override the parent's WndProc() at all, and you can use the Self pointer of the RichEdit that is processing the message when accessing members of the RichEdit, such as its Handle property.

所有这些都说明如下代码适用于我:

With all of that said, the following code works for me:

unit RichEditUrlTest;

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
  TRichEdit = class(Vcl.ComCtrls.TRichEdit)
  private
    procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
  protected
    procedure CreateWnd; override;
  end;

  TProgCorner = class(TForm)
    RichEdit2: TRichEdit;
    RichEdit1: TRichEdit;
    RichEdit3: TRichEdit;
    RichEdit4: TRichEdit;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ProgCorner: TProgCorner;

implementation

{$R *.dfm}

uses
  Winapi.ShellAPI, Winapi.RichEdit;

const
  AURL_ENABLEURL = 1;
  AURL_ENABLEEAURLS = 8;

procedure TRichEdit.CreateWnd;
var
  mask: LResult;
begin
  inherited;
  mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
  SendMessage(Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
  SendMessage(Handle, EM_AUTOURLDETECT, AURL_ENABLEURL, 0);
end;

procedure TRichEdit.CNNotify(var Message: TWMNotify);
type
  PENLink = ^TENLink;
var
  p: PENLink;
  tr: TEXTRANGE;
  url: array of Char;
begin
  if (Message.NMHdr.code = EN_LINK) then begin
    p := PENLink(Message.NMHdr);
    if (p.Msg = WM_LBUTTONDOWN) then begin
      { optionally, enable this:
      if CheckWin32Version(6, 2) then begin
        // on Windows 8+, returning EN_LINK_DO_DEFAULT directs
        // the RichEdit to perform the default action...
        Message.Result :=  EN_LINK_DO_DEFAULT;
        Exit;
      end;
      }
      try
        SetLength(url, p.chrg.cpMax - p.chrg.cpMin + 1);
        tr.chrg := p.chrg;
        tr.lpstrText := PChar(url);
        SendMessage(Handle, EM_GETTEXTRANGE, 0, LPARAM(@tr));
        ShellExecute(Handle, nil, PChar(url), 0, 0, SW_SHOWNORMAL);
      except
        {ignore}
      end;
      Exit;
    end;
  end;
  inherited;
end;

procedure TProgCorner.FormCreate(Sender: TObject);
begin
  RichEdit1.Text:= 'http://www.example.com';
end;

end.

这篇关于RichEdit不处理超链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆