Solved

Access ScrollBars in TRichEdit

Posted on 2011-03-04
14
783 Views
Last Modified: 2012-05-11
I need to move something as I scroll in a RichEdit. I don't seem to have access to the scrollbars (height, position, etc). How can I tell where the ScrollBar is in relation to my text?
0
Comment
Question by:TracerXXX
  • 5
  • 4
  • 4
  • +1
14 Comments
 
LVL 32

Expert Comment

by:ewangoya
Comment Utility
use the ScrollBars property to show scrollbars or in code you can write

TForm1.FormCreate(Sender: TObject);
begin
  RichEdit1.ScrollBars := ssBoth
end;
0
 
LVL 32

Expert Comment

by:ewangoya
Comment Utility
From Object Inspector - Properties
RichEditScrollBars.PNG
0
 
LVL 3

Expert Comment

by:pasolo
Comment Utility
I am afraid you need to go through the Windows API to find the position.
GetScrollInfo

For example:

var
info : TSCROLLBARINFO;
n : integer;
begin
info.cbSize := SizeOf(TSCROLLBARINFO);

GetScrollBarInfo( RichEdit.Handle, OBJID_VSCROLL, info);
0
 
LVL 24

Expert Comment

by:jimyX
Comment Utility
To get the ScrollBar height or position:
procedure TForm1.Button1Click(Sender: TObject);
var
  ScrlBrPos : integer;
begin
  ScrlBrPos := GetScrollPos(RichEdit1.Handle, SB_VERT);
  Showmessage(IntToStr(ScrlBrPos));
end;

Open in new window

0
 
LVL 24

Expert Comment

by:jimyX
Comment Utility
If you want to scroll the RichEdit according to the text:
procedure Find_a_Text(TheText: string);
var FoundPos: integer;
begin
// Perfom the search action, foundpos is the starting point of the found text
FoundPos := RichEdit1.FindText(TheText, 0, Length(RichEdit1.Text), []);
if not (FoundPos < 0)
   then begin

// These show found text as selected
        RichEdit1.SelStart := FoundPos;
        RichEdit1.SelLength := Length(TheText);

// If the found text is outside the displayed portion of the text
// this instruction performs an automatic scroll.
        RichEdit1.Perform(EM_SCROLLCARET, 0, 0);

// Make sure the control is redrawn
        RichEdit1.Invalidate;
        end;
end;

Open in new window

http://www.delphi3000.com/articles/article_2410.asp?SK=
0
 

Author Comment

by:TracerXXX
Comment Utility
Some Detail: My RichEdit has a TPanel next to it (along the left side) that will hold TImages. The Panel needs to move with the vert scrollbar of the RichEdit. The images are blue balls to indicate breakpoints in the RichEdit Text (see below):

 RichEdit and Panel
I created a new TRichEdit so I could surface the scroll meesages and create OnScroll Events. In the OnVertScroll event I get the scroll position (thanks jimyX) and change Panel.Top so it moves with the RichEdit. The problem is the Panel only moves on the MouseUp, not dynamically during the scrolling.

The event is firing when it should (I can increment a counter and watch it change), it just seems that the value of GetScrollPos() doesn't change until MouseUp. Any ideas?
0
 
LVL 24

Expert Comment

by:jimyX
Comment Utility
Since the event is firing try to Refresh or Update the Panel or the view thier. Or you can try "Application.ProcessMessages;"
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 

Author Comment

by:TracerXXX
Comment Utility
I previously had tried both. I can update a counter and write it to my form caption and it updates as I scroll, but if I do the same thing with GetScrollPos, it only updates on mouse up. So it doesn't seem to be a refresh problem. The issue seems to be with the GetScrollPos.

I'd send some code but I left it at another location.
0
 
LVL 24

Expert Comment

by:jimyX
Comment Utility
I can't test code at the moment neither, but I would try "invalidate" as well at "OnVertScroll" I am not sure though if it works.

Have you considered another approaches?
Such as using "OnMouseMove" of the Form while the mouse is pressed. So you set a flag in "OnVertScroll" to true and when the mouse moves while the flag is true set the Panel.Top value at "OnMouseMove".

Or use Canvas Draw "Canvas.Ellipse(x,y,x+30,y+30);" after setting the desired color and everything else, instead of using the Panel, so whenever you Scroll you are going to draw on the canvas at the location you set.

Once I have Delphi in hand will try those scenarios.
0
 
LVL 32

Expert Comment

by:ewangoya
Comment Utility
Sorry I did not initially understand your question.

To get scroll bar events, you have to trap the messages being sent to the component. Create a new windowproc method, Try this
TForm3 = class(TForm)
    RichEdit1: TRichEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FOldRichEditProc: TWndMethod;
    procedure RichEditProc(var Message: TMessage);
  public
    { Public declarations }
  end;

implementation

procedure TForm3.FormCreate(Sender: TObject);
begin
  FOldRichEditProc := RichEdit1.WindowProc;
  RichEdit1.WindowProc := RichEditProc;
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
  RichEdit1.WindowProc := FOldRichEditProc;
end;

procedure TForm3.RichEditProc(var Message: TMessage);
var
  Position: Integer;
begin
  if (csDestroying in ComponentState) then
    Exit;

  FOldRichEditProc(Message);  //make sure message is handled properly

  //here you need to trap your messages, it may not only be scroll message
  // you can find the message in Windows
  //example
  //if Message.Msg = WM_VSCROLL then
  //begin
    //Here its your choice, you can look for specific scrollbar messages
    {case Message.wParamLo of
      SB_LINEDOWN, SB_LINEUP, SB_PAGEDOWN, SB_PAGEUP:
      begin
        Position := GetScrollPos(RichEdit1.Handle, SB_VERT);
        Caption := IntToStr(Position);
      end;
    end;}   
  //end;

  Position := GetScrollPos(RichEdit1.Handle, SB_VERT); //there maybe a better way here
  //Add your code to move the Panel in relation to scroll position
  Caption := IntToStr(Position);
end;

Open in new window

0
 

Author Comment

by:TracerXXX
Comment Utility
ewanqoya,

I tried your method in place of my method and the results are the same. GetScrollPos only updates (Caption incremented) on MouseUp. Your comment in your code "there may be a better way here" says it all. GetScrollPos does not work as expected and I need a better way.

I appreciate the help you and jimyX are giving me.
0
 
LVL 32

Accepted Solution

by:
ewangoya earned 500 total points
Comment Utility

Ok, seems GetScrollPos really does not return if the thumbtrack is pressed.
For that  I've overriden the WMVScroll method

This will detect the position when the ThumbTrack is dragged.
You have to use them in combination with the previous code.
Here is the code a simple program, it just outputs the scrollbar position in a memo, you will replace that with your code to synchronize the Panel
unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;

type
  TScrollEvent = procedure(Sender: TObject; AScrollCode, APos: Integer) of Object;

  TRichEdit = class(ComCtrls.TRichEdit)
  private
    FScrollEvent: TScrollEvent;
    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  end;

  TForm3 = class(TForm)
    RichEdit1: TRichEdit;
    Memo1: TMemo;
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    FOldRichEditProc: TWndMethod;
    procedure DoScroll(Sender: TObject; AScrollCode, APos: Integer);
    procedure RichEditProc(var Message: TMessage);
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.DoScroll(Sender: TObject; AScrollCode, APos: Integer);
begin
  Memo1.Lines.Add(IntToStr(APos));
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
  RichEdit1.WindowProc := FOldRichEditProc;
end;

procedure TForm3.FormShow(Sender: TObject);
begin
  FOldRichEditProc := RichEdit1.WindowProc;
  RichEdit1.WindowProc := RichEditProc;
  RichEdit1.FScrollEvent := DoScroll;
end;

procedure TForm3.RichEditProc(var Message: TMessage);
var
  Position: Integer;
begin
  if (csDestroying in ComponentState) then
    Exit;

  FOldRichEditProc(Message);  //make sure message is handled properly

  if Message.Msg = WM_VSCROLL then
    case Message.wParamLo of
      SB_LINEUP,
      SB_LINEDOWN,
      SB_PAGEUP,
      SB_PAGEDOWN,
      SB_TOP,
      SB_BOTTOM,
      SB_ENDSCROLL:
      begin
        Position := GetScrollPos(RichEdit1.Handle, SB_VERT);
        DoScroll(RichEdit1, Message.wParamLo, Position);
      end;
    end;
end;

{ TRichEdit }

procedure TRichEdit.WMVScroll(var Message: TWMVScroll);
begin
  inherited;
  if (Message.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then
    FScrollEvent(Self, Message.ScrollCode, Message.Pos);
end;

end.

Open in new window

Unit3.dfm
Unit3.pas
0
 

Author Comment

by:TracerXXX
Comment Utility
ewanqoya,

I rolled in your code and I'm very close. my panel moves with the Scroll properly now. The only issue I have is an "invalid handle" exception (code: 1400) on exit of the application. Since I don't fully understand your code I'm not sure where the error is occuring. I've attached the code if you wouldn't mind taking a look.

 NewRichEdit.pas Unit3.pas Unit3.dfm
unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, NewRichEdit;

type
  TForm3 = class(TForm)
    NewRichEdit1: TNewRichEdit;
    pnlBreak: TPanel;
    Image1: TImage;
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    FOldRichEditProc: TWndMethod;
    procedure DoScroll(Sender: TObject; AScrollCode, APos: Integer);
    procedure RichEditProc(var Msg: TMessage);
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

{----------------------------------------------------------------------}
procedure TForm3.FormShow(Sender: TObject);
begin
  SetScrollPos(NewRichEdit1.Handle, SB_VERT, 0, True);
  FOldRichEditProc := NewRichEdit1.WindowProc;
  NewRichEdit1.WindowProc := RichEditProc;
  NewRichEdit1.OnScrollEvent := DoScroll;
end;

{----------------------------------------------------------------------}
procedure TForm3.DoScroll(Sender: TObject; AScrollCode, APos: Integer);
begin
   pnlBreak.Top:=(APos * -1) + NewRichEdit1.Top;
end;

{----------------------------------------------------------------------}
procedure TForm3.RichEditProc(var Msg: TMessage);
var
  Position: Integer;
begin
  if (csDestroying in ComponentState) then
    Exit;

  FOldRichEditProc(Msg);  //make sure message is handled properly

  if Msg.Msg = WM_VSCROLL then
    case Msg.wParamLo of
      SB_LINEUP,
      SB_LINEDOWN,
      SB_PAGEUP,
      SB_PAGEDOWN,
      SB_TOP,
      SB_BOTTOM,
      SB_ENDSCROLL:
      begin
        Position := GetScrollPos(NewRichEdit1.Handle, SB_VERT);
        DoScroll(NewRichEdit1, Msg.wParamLo, Position);
      end;
    end;
end;

end.

Open in new window

0
 
LVL 32

Expert Comment

by:ewangoya
Comment Utility

on FormDestroy, you need to reassign the old window proc

procedure TForm3.FormDestroy(Sender: TObject);
begin
  NewRichEdit1.WindowProc := FOldRichEditProc;
end;
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now