Delphi:通过滚动在 TRichEdit 中居中特定行

发布于 2024-09-01 03:46:51 字数 657 浏览 15 评论 0原文

我有一个 Delphi 2007 TRichEdit,里面有几行。我想垂直滚动 richedit,以便特定行号大约位于 richedit 的可见/显示区域的中心。例如,我想在此示例中编写 CenterLineInRichEdit 的代码:

procedure CenterLineInRichEdit(Edit: TRichEdit; LineNum: Integer);
begin
  ...
  Edit.ScrollTo(...);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  REdit: TRichEdit;
  i: Integer;
begin
  REdit := TRichEdit.Create(Self);
  REdit.Parent := Self;
  Redit.ScrollBars := ssVertical;
  REdit.SetBounds(10, 10, 200, 150);
  for i := 1 to 25 do
    REdit.Lines.Add('This is line number ' + IntToStr(i));
  CenterLineInRichEdit(REdit, 13);
end;

我研究了使用 WM_VSCROLL 消息,它允许向上/向下滚动一行等,但不能滚动到特定行的中心。

I have a Delphi 2007 TRichEdit with several lines in it. I want to scroll the richedit vertically such that a specific line number if approximately centered in the visible/display area of the richedit. For example, I want to write the code for CenterLineInRichEdit in this example:

procedure CenterLineInRichEdit(Edit: TRichEdit; LineNum: Integer);
begin
  ...
  Edit.ScrollTo(...);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  REdit: TRichEdit;
  i: Integer;
begin
  REdit := TRichEdit.Create(Self);
  REdit.Parent := Self;
  Redit.ScrollBars := ssVertical;
  REdit.SetBounds(10, 10, 200, 150);
  for i := 1 to 25 do
    REdit.Lines.Add('This is line number ' + IntToStr(i));
  CenterLineInRichEdit(REdit, 13);
end;

I looked into using the WM_VSCROLL message, and it allows scrolling up/down one line, etc. but not scrolling to center a specific line.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

猫瑾少女 2024-09-08 03:46:51

基于这里的想法,我想出了一个解决方案。它假设 richedit 中的所有行都具有相同的高度,并且 richedit 的默认字体正确报告其高度,但它可能对某些人有用:

type
  TCustomEditHack = class(TCustomEdit);

procedure CenterLineInEdit(Edit: TCustomEdit; LineNum: Integer);
var
  VisibleLines: Integer;
  TopLine: Integer;
  FirstLine: Integer;
begin
  FirstLine := Edit.Perform(EM_GETFIRSTVISIBLELINE, 0, 0);
  VisibleLines := Round(Edit.ClientHeight / Abs(TCustomEditHack(Edit).Font.Height));

  if VisibleLines <= 1 then
    TopLine := LineNum
  else
    TopLine := Max(LineNum - Round((VisibleLines/2)) + 1, 0);

  if FirstLine <> TopLine then
    Edit.Perform(EM_LINESCROLL, 0, TopLine - FirstLine);
end;

我使用 TRichEdit 对此进行了测试,但它也可能适用于 TMemo。

Based on the ideas here, I came up with one solution. It assumes that all the lines in the richedit are the same height and that the richedit's default font correctly reports its height, but it might be useful to some people:

type
  TCustomEditHack = class(TCustomEdit);

procedure CenterLineInEdit(Edit: TCustomEdit; LineNum: Integer);
var
  VisibleLines: Integer;
  TopLine: Integer;
  FirstLine: Integer;
begin
  FirstLine := Edit.Perform(EM_GETFIRSTVISIBLELINE, 0, 0);
  VisibleLines := Round(Edit.ClientHeight / Abs(TCustomEditHack(Edit).Font.Height));

  if VisibleLines <= 1 then
    TopLine := LineNum
  else
    TopLine := Max(LineNum - Round((VisibleLines/2)) + 1, 0);

  if FirstLine <> TopLine then
    Edit.Perform(EM_LINESCROLL, 0, TopLine - FirstLine);
end;

I tested this with TRichEdit, but it might work for TMemo as well.

橘亓 2024-09-08 03:46:51

向 RichEdit 发送 EM_LINESCROLL 消息:

SendMessage(REdit.Handle, EM_LINESCROLL, 0, NumberOfVerticalLinesToScroll);

请参阅 EM_LINESCROLL MSDN 主题

Send an EM_LINESCROLL message to the RichEdit:

SendMessage(REdit.Handle, EM_LINESCROLL, 0, NumberOfVerticalLinesToScroll);

See the EM_LINESCROLL MSDN topic.

黎夕旧梦 2024-09-08 03:46:51

尝试一下;

procedure VertCenterLine(RichEdit: TRichEdit; LineNum: Integer);
// I don't know the reason but the RichEdit 2 control in VCL does not
// respond to the EM_SCROLLCARET in Richedit.h but it does so to the
// constant in WinUser.h
const
  EM_SCROLLCARET  = $00B7;
var
  TextPos: lResult;
  Pos: TSmallPoint;
begin
  TextPos := SendMessage(RichEdit.Handle, EM_LINEINDEX, LineNum, 0);

  if TextPos <> -1 then begin
    // Go to top
    SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
    SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);

    // Get the coordinates for the beginning of the line
    Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);

    // Scroll from the top
    SendMessage(RichEdit.Handle, WM_VSCROLL,
        MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);

    // Optionally set the caret to the beginning of the line
    SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
  end;
end;

下面是一个替代方案,它将第一次出现的字符串而不是行号居中;

procedure VertCenterText(RichEdit: TRichEdit; Text: string);
const
  EM_SCROLLCARET  = $00B7;
var
  FindText: TFindText;
  TextPos: lResult;
  Pos: TSmallPoint;
begin
  FindText.chrg.cpMin := 0;
  FindText.chrg.cpMax := -1;
  FindText.lpstrText := PChar(Text);
  TextPos := SendMessage(RichEdit.Handle, EM_FINDTEXT,
      FR_DOWN or FR_WHOLEWORD, Longint(@FindText));

  if TextPos <> -1 then begin
    SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
    SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);

    Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);
    SendMessage(RichEdit.Handle, WM_VSCROLL,
        MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);

    SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
  end;
end;

Give this a try;

procedure VertCenterLine(RichEdit: TRichEdit; LineNum: Integer);
// I don't know the reason but the RichEdit 2 control in VCL does not
// respond to the EM_SCROLLCARET in Richedit.h but it does so to the
// constant in WinUser.h
const
  EM_SCROLLCARET  = $00B7;
var
  TextPos: lResult;
  Pos: TSmallPoint;
begin
  TextPos := SendMessage(RichEdit.Handle, EM_LINEINDEX, LineNum, 0);

  if TextPos <> -1 then begin
    // Go to top
    SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
    SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);

    // Get the coordinates for the beginning of the line
    Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);

    // Scroll from the top
    SendMessage(RichEdit.Handle, WM_VSCROLL,
        MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);

    // Optionally set the caret to the beginning of the line
    SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
  end;
end;

The below is an alternative in that it centers the first occurance of a string instead of a line number;

procedure VertCenterText(RichEdit: TRichEdit; Text: string);
const
  EM_SCROLLCARET  = $00B7;
var
  FindText: TFindText;
  TextPos: lResult;
  Pos: TSmallPoint;
begin
  FindText.chrg.cpMin := 0;
  FindText.chrg.cpMax := -1;
  FindText.lpstrText := PChar(Text);
  TextPos := SendMessage(RichEdit.Handle, EM_FINDTEXT,
      FR_DOWN or FR_WHOLEWORD, Longint(@FindText));

  if TextPos <> -1 then begin
    SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
    SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);

    Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);
    SendMessage(RichEdit.Handle, WM_VSCROLL,
        MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);

    SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
  end;
end;
九公里浅绿 2024-09-08 03:46:51

您将需要使用一些 Windows 消息以通用方式操作控件的这方面:

您将需要计算从当前顶行向上/向下滚动多少行以将所需的绝对行号带入视图,但您必须自己计算控件中可见的行数(使用字体规格和控件高度)。

请注意,对于 RichEdit 控件,每行的高度可能会根据应用于控件中文本的字体而变化,因此任何仅基于行号的方法可能只是近似准确。另外我不确定是否可以直接确定控件的当前可见范围(即当前可见的行数),因此需要自己计算。

根据记忆,SynEdit 控件 提供了对此类事物的一些额外控制,提供读/写 TopLine 属性以及 LinesInWindow 属性。但是,我认为 SynEdit 不支持富文本功能,但如果这实际上不是您的应用程序中需要考虑的问题(即您可以对内容中的所有行使用一致的字体),那么它可能是一个有吸引力或合适的替代方案。

You will need to use a couple of Windows messages to manipulate this aspect of your control in a generic fashion:

You will need to calculate how many lines to scroll up/down from the current top-line to bring a desired absolute line number into view, but you will have to calculate the number of lines visible in the control yourself (using font metrics and control height).

Note that with a RichEdit control the height of each line may vary according to fonts applied to the text in the control so any approach based on line numbers alone is likely to be only approximately accurate. Also I'm not sure that it's possible to determine the current visible range of the control (i.e. the number of lines currently visible) directly, so calculating it yourself is necessary.

From memory, the SynEdit control offers some additional control over such things, providing both a read/write TopLine property as well as a LinesInWindow property. However, I think SynEdit is not rich text capable, but if this is not actually a concern in your application (i.e. you can use a consistent font for all lines in the content) then it may be an attractive or suitable alternative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文