1

我有一个 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 消息,它允许向上/向下滚动一行等,但不能滚动到特定行的中心。

4

4 回答 4

3

向 RichEdit 发送 EM_LINESCROLL 消息:

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

请参阅EM_LINESCROLL MSDN 主题

于 2010-05-12T20:28:11.417 回答
3

基于这里的想法,我想出了一个解决方案。它假定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。

于 2010-05-15T04:30:27.700 回答
2

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

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

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

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

于 2010-05-13T01:31:23.480 回答
2

试试这个;

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;
于 2010-05-13T16:05:09.867 回答