?
Solved

How to pan an image with the mouse

Posted on 2010-01-07
21
Medium Priority
?
810 Views
Last Modified: 2012-05-08
I have a TImage component on a form, where the image is larger than the form. The form's AutoScroll property is set to True. I can use the scroll bars to move the image around on the form, but I want to use the mouse. I want to click on the image and drag it to move it on the form. As I do this, the form's scroll bars should move correspondingly. I am going to set the image cursor to crHandPoint so that people will expect that they can move the image around.

My question is, how do I code this?
0
Comment
Question by:wipnav
  • 11
  • 9
21 Comments
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26204466
use a tscrollbox with align alclient and put the image into it
0
 
LVL 1

Author Comment

by:wipnav
ID: 26204545
I did that, but the image still doesn't move when I click on it and drag it.
0
 
LVL 14

Assisted Solution

by:SteveBay
SteveBay earned 600 total points
ID: 26204590
You will need to implement some event for mouseodwn mousemove and mouseup:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
     FMouseIsDown : Boolean;
     FLastX : integer;
     FLastY : integer;
    { Private declarations }
  public
	Image : TImage;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}



procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     Screen.Cursor := crHandPoint;
     FMouseIsDown := True;
     FLastX := X;
     FLastY := Y;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var dx,dy : integer;
begin
     if FMouseIsDown then
          begin
          dx := X - FLastX;
          dy := Y - FLastY;
          ScrollBox1.ScrollBy(dx, dy );
          FLastX := X;
          FLastY := Y;
          end
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     Screen.Cursor := crDefault;
     FMouseIsDown := False;
end;

end

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26207942
Another way is to put a Tpanel on your form.
Drop a Timage to the panel with align alClient and cursor crHandPoint
Now, In TImage.onMouseDown event

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012;
begin
  Begin
   ReleaseCapture;
   panel1.perform(WM_SysCommand, SC_DragMove, 0);
  End;
end;

This will drag the panel, and so the image.
0
 
LVL 1

Author Comment

by:wipnav
ID: 26208760
SteveBay,

You seem to be on the right track. The scroll box isn't necessary because the form can be scrolled as well. The problem is that the image movement is very jerky. The scroll bars jump around like crazy. Also, I don't want the image to scroll such that it ever moves away from the edge of the window.

Ferruccio68,

Your code doesn't do what I'm looking for.
0
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26208889
Gosh, I've completely misunderstood (or forgot the real one) the question

Try this one, without TScroolBox


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  down: boolean;

implementation

{$R *.dfm}

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  down := true;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if down then
  begin
    vertscrollbar.Position := Y;
    horzscrollbar.Position := X;
  end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  down := false;
end;

end.

Open in new window

0
 
LVL 1

Author Comment

by:wipnav
ID: 26208968
Not good. Did you run your code?

The idea is to be able to pan the image around the form so that all parts of it can be seen.
0
 
LVL 23

Assisted Solution

by:Ferruccio Accalai
Ferruccio Accalai earned 1400 total points
ID: 26208988
Well it seems that getting the x and y values is not the right solution, but that idea combined with the one by SteveBay should solve the problem
So the final combined code is the following:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  private
    FMouseIsDown: Boolean;
    FLastX: Integer;
    FLastY: Integer;
    { Private declarations }
  public
    Image: TImage;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Screen.Cursor := crHandPoint;
  FMouseIsDown := True;
  FLastX := X;
  FLastY := Y;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  dx, dy: Integer;
begin
  if FMouseIsDown then
  begin
    dx := X - FLastX;
    dy := Y - FLastY;
    vertscrollbar.Position := vertscrollbar.Position+dx;
    horzscrollbar.Position := horzscrollbar.Position+dy;
    FLastX := X;
    FLastY := Y;
  end
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Screen.Cursor := crDefault;
  FMouseIsDown := False;
end;

end.

Open in new window

0
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26209002
Inversion:
vertscrollbar.Position := vertscrollbar.Position+dy;
horzscrollbar.Position := horzscrollbar.Position+dx;
0
 
LVL 1

Author Comment

by:wipnav
ID: 26209085
Progress yes, a final solution, not yet.

1. The image moves the opposite way that you would expect. The "Hand" should grab the image and move it in the direction that the hand is moved, not the opposite way as with your code.
2. If I attempt to move the image only in the Y direction it also moves in the X direction. The same for movement in only the X direction.
3. Sometimes the image doesn't want to move all of the way to the edge.
0
 
LVL 1

Author Comment

by:wipnav
ID: 26209093
Your inversion comment helped with #2 above.
0
 
LVL 23

Assisted Solution

by:Ferruccio Accalai
Ferruccio Accalai earned 1400 total points
ID: 26209444
Well, change the mousemove procedure as follows

We'll use the width and height of the image to create the correct range between pixels and scrollbar integer position, and of course we'll scroll it  in the same direction of the mouse.

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  dx, dy: Integer;
begin
  if FMouseIsDown then
  begin
    dx := X - FLastX;
    dy := y - FLasty;
    vertscrollbar.Position := vertscrollbar.Position-(dy*Image1.height div vertscrollbar.Range);
    horzscrollbar.Position := horzscrollbar.Position-(dx*Image1.width div horzscrollbar.Range);
    FLastX := X;
    FLastY := Y;
  end
end;
0
 
LVL 1

Author Comment

by:wipnav
ID: 26210392
I think we are getting real close. The functionality seems to be there, but there is still a lot of jitter when I move the image. I recall that there some sort of double buffering thing that might help. Have you heard of that?
0
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26210467
Yes, you can set the Tform property doublebuffered to true.
This should reduce the flickering
0
 
LVL 1

Author Comment

by:wipnav
ID: 26210558
I tried it, and it helps a little.

There is still a problem. As you move the mouse slowly, the image jumps back and forth. It's like it is too sensitive to the mouse movement.
0
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26210691
Maybe due to the vetrscrollbar and horzscrollbar increment?
In my test is set to 1
0
 
LVL 1

Author Comment

by:wipnav
ID: 26212135
I set the increment to 1 also.

When I move the mouse as slowly as possible, I see the image jump around more than I'm moving the mouse. Do you see the same thing? I think something must be wrong with the logic.
0
 
LVL 1

Author Comment

by:wipnav
ID: 26212270
I think something needs to be done to reduce the sensitivity to mouse movement. Maybe if the mouse only moves a pixel or two, that the image doesn't move.
0
 
LVL 1

Accepted Solution

by:
wipnav earned 0 total points
ID: 26273203
Here is the correct coding. The FLastX/Y value needs to be adjusted by the amount that the image was moved.

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  Dx, Dy: Integer;
begin
  if FMouseIsDown then
  begin
    Dx := X - FLastX;
    Dy := y - FLasty;
    VertScrollBar.Position :=
     VertScrollBar.Position - (Dy * Image1.height div vertscrollbar.Range);
    HorzScrollBar.Position :=
     HorzScrollBar.Position - (Dx * Image1.width div horzscrollbar.Range);
    FLastX := X - Dx;
    FLastY := Y - Dy;
  end
end;
0
 
LVL 1

Author Comment

by:wipnav
ID: 26273277
Here is my final code with the correct the setting of the cursor.
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormResize(Sender: TObject);
  private
     FMouseIsDown : Boolean;
     FLastX : integer;
     FLastY : integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormResize(Sender: TObject);
begin
  if (Image1.Height > ClientHeight) or (Image1.Width > ClientWidth) then
    Cursor := crHandPoint
  else
    Cursor := crDefault;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  FMouseIsDown := True;
  FLastX := X;
  FLastY := Y;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  Dx, Dy: Integer;
begin
  if FMouseIsDown then
  begin
    Dx := X - FLastX;
    Dy := y - FLasty;
    VertScrollBar.Position :=
     VertScrollBar.Position - (Dy * Image1.Height div VertScrollBar.Range);
    HorzScrollBar.Position :=
     HorzScrollBar.Position - (Dx * Image1.Width div HorzScrollBar.Range);
    FLastX := X - Dx;
    FLastY := Y - Dy;
  end
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  FMouseIsDown := False;
end;

end.

Open in new window

0
 
LVL 23

Expert Comment

by:Ferruccio Accalai
ID: 26281423
Well, I'm back after my deserved relaxing weekend :)
In fact I was guessing about this
FLastX := X - Dx;
FLastY := Y - Dy;
but I'm glad to see you've adjusted the final code by yourself.

Regards
Ferruccio
 
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Whether it be Exchange Server Crash Issues, Dirty Shutdown Errors or Failed to mount error, Stellar Phoenix Mailbox Exchange Recovery has always got your back. With the help of its easy to understand user interface and 3 simple steps recovery proced…
Suggested Courses

864 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