Jump to content
JohnLM

How do I synchronize two TMemo scrolling?

Recommended Posts

Specs: delphi xe7, vcl, win7 - TMemo components 

 

I've been searching to no avail on this topic.  I can't find any example source code showing how to synchronize two memo's scrolling, via keyboard, mouse, and scrollbars.

 

I see there are a few for TListBox examples but not for TMemo. 

 

I also tried one example (it was for tlistbox) and everything compiles except that TMemo does not have a .TopIndex so that source code does not compile up to that point.

(link below). 

 

link to that resource -> https://stackoverflow.com/questions/24195857/synchronize-scrollbars-of-two-listboxes

 

source from stackexchange: 

To set the top line of a list box you use TopIndex.

You can create a TListbox descendent that handles the WM_VSCROLL (and WM_HSCROLL if you want). 
You can then hook into this and update the second list box. Here is an example of this. I am 
only doing the hook one way so scrolling listbox2 won't scroll listbox1.

You will need to add this TListBox override to your unit before the form declaration:

TListBox = class(Vcl.StdCtrls.TListBox)
private
  FOnScroll: TNotifyEvent;
protected
  procedure ListBoxScroll(var Message: TMessage); message WM_VSCROLL;
public
  property OnScroll: TNotifyEvent read FOnScroll write FOnScroll;
end;

This adds a OnScroll event to the listbox. The implementation for this class:

procedure TListBox.ListBoxScroll(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnScroll) then
    FOnScroll(Self);
end;

You can then hook up the event in code:

procedure TMyForm.FormCreate(Sender: TObject);
begin
  listbox1.OnScroll := DoScrollListBox1;
end;

The code for DoScrollListBox1 is very simple:

procedure TMyForm.DoScrollListBox1(Sender: TObject);
begin
  listbox2.TopIndex := listbox1.TopIndex;
end;

This handles the scrolling by using the scroll bar. You will also need to add the following line
to your OnClick of the listbox so keyboard actions will also trigger the scrolling.

procedure TMyForm.ListBox1Click(Sender: TObject);
begin
  ...
  listbox2.TopIndex := listbox1.TopIndex; 
  ...
end;

My modified version of the above code: 

 

unit Unit1;

interface

  TForm1 = class(TForm)
    memo1: TMemo;
    memo2: TMemo;
    procedure memo1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

procedure TMemo.MemoScroll(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnScroll) then
    FOnScroll(Self);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.OnScroll := DoScrollMemo;
end;
procedure tform1.DoScrollMemo(Sender: TObject);
begin
  memo2.TopIndex := memo1.TopIndex;
end;

// This handles the scrolling by using the scroll bar. You will also need to
// add the following line to your OnClick of the listbox so keyboard actions
// will also trigger the scrolling.
procedure tform1.memo1Click(Sender: TObject);
begin
  //...
  //memo2.TopIndex := memo1.TopIndex; // <--- Does not compile at this point because there is no .TopIndex in TMemo. I learned this all to late. 
  //...
end;

Does anyone have source code for synchronizing two TMemo's?

 

Edited by JohnLM
typo at end

Share this post


Link to post

It depends a bit what the exact use case is, but it probably can be achieved with a class derived from TMemo with the following extensions:

type
  TLinkMemo = class(TMemo)
  private
    FLinkedMemo: TLinkMemo;
    procedure WMVScroll(var Message: TMessage); message WM_VSCROLL;
    procedure DoScroll(var Message: TMessage);
  public
    property LinkedMemo: TLinkMemo read FLinkedMemo write FLinkedMemo;
  end;

procedure TLinkMemo.DoScroll(var Message: TMessage);
begin
  var saveLinkedMemo := FLinkedMemo;
  try
    FLinkedMemo := nil;
    Perform(Message.Msg, Message.WParam, Message.LParam);
  finally
    FLinkedMemo := saveLinkedMemo;
  end;
end;

procedure TLinkMemo.WMVScroll(var Message: TMessage);
begin
  inherited;
  if FLinkedMemo <> nil then
    FLinkedMemo.DoScroll(Message);
end;

In FormCreate you just assign both LinkedMemo properties:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.LinkedMemo := Memo2;
  Memo2.LinkedMemo := Memo1;
end;

To avoid having to register this new control TLinkMemo you can use an interposer class declared before the form, either in the same unit or in a separate unit used in the interface uses clause.

type
  TMemo = class(TLinkMemo);

Note, that editing one of the memo will break sync between the controls.

Edited by Uwe Raabe

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×