delphilinecenterrichedittrichedit

Delphi: Center Specific Line in TRichEdit by Scrolling


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.


Solution

  • 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;