简体   繁体   English

如何增强Delphi中的默认备忘录控件,并能够为文本加下划线

[英]How to enhance the default memo control in Delphi with the ability to underline text

I'm trying to build a simple script editor with the ability to show errors. 我正在尝试构建一个能够显示错误的简单脚本编辑器。 I've searched the web for a component that can show/underline the errors for me, but i couldn't found one. 我在网上搜索了一个可以为我显示/下划线错误的组件,但我找不到一个。 So i've decided to build one myself based on the memo control that's included in Delphi. 所以我决定根据Delphi中包含的备忘录控件自己构建一个。

I was planning to add the following function to the memo control: 我打算在备忘录控件中添加以下功能:

function Underline(startline, startchar, endline, endchar : integer);

Being the first time for me to enhance a visual control like this i'm asking if someone could broadly outline for me how to do this. 这是我第一次加强像这样的视觉控制,我在问是否有人可以大致勾勒出如何做到这一点。 No need to go into specific details :) 无需深入了解具体细节:)

ps: I don't want to use a richedit control. ps:我不想使用richedit控件。

Below is some D2007 code sample using regular winapi, that would show you how to find where to draw in a scrollable memo and how to draw a simple underline. 下面是一些使用常规winapi的D2007代码示例,它将向您展示如何在可滚动的备忘录中找到绘制位置以及如何绘制简单的下划线。 For brevity it has no error catching/handling. 为简洁起见,它没有错误捕获/处理。 Also lets only one underline scope, since usability as a component is not the purpose of the sample. 还只允许一个下划线范围,因为作为组件的可用性不是样本的目的。 Tried with a vertical-scrolling memo but if you want you should be able to fine tune details if problems arise otherwise. 尝试使用垂直滚动备忘录,但如果您愿意,如果出现问题,您应该能够微调细节。

Tested on 2K, XP and 7, the look on XP is like this: 在2K,XP和7上测试,XP上的外观如下:

memo with underlined text http://img687.imageshack.us/img687/8176/20101210061602.png 带下划线文字的备忘录http://img687.imageshack.us/img687/8176/20101210061602.png


And the code: 和代码:

type
  TMemo = class(stdctrls.TMemo)
  private
    FStartChar, FEndChar: Integer;
    procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
  public
    procedure Underline(StartLine, StartChar, EndLine, EndChar: Integer);
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMemo }

procedure TMemo.Underline(StartLine, StartChar, EndLine, EndChar: Integer);
begin
  FStartChar := SendMessage(Handle, EM_LINEINDEX, StartLine, 0) + StartChar;
  FEndChar := SendMessage(Handle, EM_LINEINDEX, EndLine, 0) + EndChar;
  Invalidate;
end;

procedure TMemo.WMPaint(var Msg: TWMPaint);

  function GetLine(CharPos: Integer): Integer;
  begin
    Result := SendMessage(Handle, EM_LINEFROMCHAR, CharPos, 0);
  end;

  procedure DrawLine(First, Last: Integer);
  var
    LineHeight: Integer;
    Pt1, Pt2: TSmallPoint;
    DC: HDC;
    Rect: TRect;
    ClipRgn: HRGN;
  begin
    // font height approximation (compensate 1px for internal leading)
    LineHeight := Abs(Font.Height) - Abs(Font.Height) div Font.Height;

    // get logical top-left coordinates for line bound characters
    Integer(Pt1) := SendMessage(Handle, EM_POSFROMCHAR, First, 0);
    Integer(Pt2) := SendMessage(Handle, EM_POSFROMCHAR, Last, 0);

    DC := GetDC(Handle);

    // clip to not to draw to non-text area (internal margins)
    SendMessage(Handle, EM_GETRECT, 0, Integer(@Rect));
    ClipRgn := CreateRectRgn(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
    SelectClipRgn(DC, ClipRgn);
    DeleteObject(ClipRgn); // done with region

    // set pen color to red and draw line
    SelectObject(DC, GetStockObject(DC_PEN));
    SetDCPenColor(DC, RGB(255, 0 ,0));
    MoveToEx(DC, Pt1.x, Pt1.y + LineHeight, nil);
    LineTo(DC, Pt2.x, Pt2.y + LineHeight);

    ReleaseDC(Handle, DC); // done with dc
  end;

var
  StartChar, CharPos, LinePos: Integer;
begin
  inherited;
  if FEndChar > FStartChar then begin

    // Find out where to draw.
    // Can probably optimized a bit by using EM_LINELENGTH
    StartChar := FStartChar;
    CharPos := StartChar;
    LinePos := GetLine(CharPos);
    while True do begin
      Inc(CharPos);
      if GetLine(CharPos) > LinePos then begin
        DrawLine(StartChar, CharPos - 1);
        StartChar := CharPos;
        Dec(CharPos);
        Inc(LinePos);
        Continue;
      end else
        if CharPos >= FEndChar then begin
          DrawLine(StartChar, FEndChar);
          Break;
        end;
    end;
  end;
end;

{  --end TMemo-- }

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Underline(7, 14, 8, 17);
end;

edit: Forgot to mention, when typing you would probably remove underlining. 编辑:忘记提及,在键入时你可能会删除下划线。 I don't have any idea how it should behave when typing, and probably it would be difficult to achieve that desired behavior. 我不知道它在键入时应该如何表现,并且可能很难实现所期望的行为。

The "default memo control" in Delphi is just a wrapper for a Windows standard text box control. Delphi中的“默认备忘录控件”只是Windows标准文本框控件的包装器。 As such, there is no way to implement custom behaviour in this control. 因此,无法在此控件中实现自定义行为。 (If you need really custom behaviour, you can always write your own text box control from scratch. I have done so in my text editor , which also supports syntax highlighting. Or, you could use a third-party control. There are plenty of advanced text editor controls for Delphi out there.) You can only use functions provided by the operating system when it comes to this control. (如果你需要真正的自定义行为,你总是可以从头开始编写自己的文本框控件。我在我的文本编辑器中也这样做了,它也支持语法高亮。或者,你可以使用第三方控件。有很多Delphi的高级文本编辑器控件。)在此控件中,您只能使用操作系统提供的功能。

You should really use a TRichEdit instead. 你应该真的使用TRichEdit This is a wrapper for the standard Windows Rich Edit control, which supports formatting such as underlining. 这是标准Windows Rich Edit控件的包装器,它支持下划线等格式。 (And, it also supports a lot of other stuff not presented by the Delphi wrapper, such as automatic URL highlighting, among other things, but that's another story.) (而且,它还支持Delphi包装器未提供的许多其他内容,例如自动URL突出显示等,但这是另一个故事。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM