delphiscrollrichedit

How to save and then restore vertical scroll position in RichEdit


I am trying to save and then restore the vertical scroll position in RichEdit.

A global var to store scroll pos:

SI: TScrollInfo;

This code saves the scroll position:

FillChar( SI, SizeOf(SI), #0 );
SI.cbSize := SizeOf(SI);
SI.fMask  := SIF_POS;
GetScrollInfo( RichEdit1.Handle, SB_VERT, SI );

This code tries to restore it:

RichEdit1.Perform( WM_VSCROLL, MakeLong(SB_THUMBTRACK, SI.nPos), 0 );

The text in RichEdit restores its older position OK. The problem is the vertical scrollbar won't jump to the older location.

My system: Win 7 64, Delphi 2009

What am I doing wrong?


Solution

  • Option 1

    In many ways, the "cleanest" solution would be to use the EM_GETSCROLLPOS and EM_SETSCROLLPOS messages:

    const
      EM_GETSCROLLPOS = $04DD;
      EM_SETSCROLLPOS = $04DE;
    
    var
      P: TPoint;
    
    procedure TForm1.btnSaveClick(Sender: TObject);
    begin
      RichEdit1.Perform(EM_GETSCROLLPOS, 0, @P)
    end;
    
    procedure TForm1.btnRestoreClick(Sender: TObject);
    begin
      RichEdit1.Perform(EM_SETSCROLLPOS, 0, @P)
    end;
    

    However, beware of the 16-bit limitation described in the documentation, which limits the vertical range you are able to represent using these messages. If you display large RTF documents, this might be an issue (a showstopper, really).

    Option 2

    Actually, your initial approach seems (to my surprise) not to suffer from this limitation. You will lose in precision, not range. The problem you are observing with the scrollbar can be fixed by using SB_THUMBPOSITION instead of SB_THUMBTRACK.

    Option 3

    var
      Y: Integer;
    
    procedure TForm1.btnSaveClick(Sender: TObject);
    begin
      y := RichEdit1.Perform(EM_GETFIRSTVISIBLELINE, 0, 0);
    end;
    
    procedure TForm1.btnRestoreClick(Sender: TObject);
    var
      NewY: Integer;
    begin
      NewY := RichEdit1.Perform(EM_GETFIRSTVISIBLELINE, 0, 0);
      RichEdit1.Perform(EM_LINESCROLL, 0, Y - NewY);
    end;
    

    might be a viable option.