OnMouseMove in TDrawGrid on touch screen - delphi

Program use on touch screen Windows-tablet. Have a grid, where we need to select some cells in a row: target cursor over cell, click left button of mouse, holding it, pull the mouse to the side, and then release left button of mouse (at the same time code in OnDrawCell is drawing cells).
By mouse or by touchpad of notebook works very well. But on tablet's touchscreen doesn't work at all.
I use TDrawGrid and OnMouseDown, OnMouseMove, OnMouseUp events.
In Shift use all options: ssLeft, ssTouch, ssPen. Look at full code:
procedure TfmMain.GridMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
ACol, ARow: Integer;
ARect: TRect;
begin
(Sender as TDrawGrid).MouseToCell(X, Y, ACol, ARow);
ARect := (Sender as TDrawGrid).CellRect(ACol, ARow);
pmIsLeft := X<(ARect.Left+((ARect.Right-ARect.Left) div 2));
pmCol := ACol;
pmRow := ARow;
if (ssLeft in Shift) or (ssTouch in Shift) or (ssPen in Shift) then
begin
ChooseDaysInGridRowIndex := ARow;
SetLength(ChooseDays, 0);
end;
end;
procedure TfmMain.GridMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
ACol, ARow: Integer;
begin
if (ssLeft in Shift) or (ssTouch in Shift) or (ssPen in Shift) then
begin
(Sender as TDrawGrid).MouseToCell(X, Y, ACol, ARow);
if (ChooseDaysInGridRowIndex>0) and
(ChooseDaysInGridRowIndex<=(Sender as TDrawGrid).RowCount-1) and
((PagesDays[pagesBuildings.ActivePageIndex][ARow, ACol][0].ReservID<=0) or
((Length(ChooseDays)=0) and ((PagesDays[pagesBuildings.ActivePageIndex][ARow, ACol][1].ReservID<=0)))) then
begin
SetLength(ChooseDays, Length(ChooseDays)+1);
ChooseDays[High(ChooseDays)] := Point(ACol, ChooseDaysInGridRowIndex);
InvalidateRect((Sender as TDrawGrid).Handle,
(Sender as TDrawGrid).CellRect(ACol, ARow),
True);
end;
ChooseDaysInGrid := True;
end;
end;
procedure TfmMain.GridMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
ACol, ARow: Integer;
begin
if ChooseDaysInGrid then
begin
(Sender as TDrawGrid).MouseToCell(X, Y, ACol, ARow);
ChooseDaysInGrid := False;
fmGuestArrival.roomID := GridRowTitles[(Sender as TDrawGrid).Tag] [ARow].RoomID;
// Integer((Sender as TDrawGrid).Objects[0, ARow]);
if Length(ChooseDays)>0 then
begin
fmGuestArrival.dateArrival.DateTime :=
IncDay(StartDatePeriod, ChooseDays[0].X-1);
if Length(ChooseDays)>1 then
begin
fmGuestArrival.dateDeparture.DateTime :=
IncDay(StartDatePeriod, ChooseDays[High(ChooseDays)].X - 1);
end
else
begin
fmGuestArrival.dateDeparture.DateTime :=
IncDay(fmGuestArrival.dateArrival.DateTime, 1);
end;
end;
fmGuestArrival.IsEditing := False;
fmGuestArrival.cbStatus.ItemIndex := 0;
fmGuestArrival.ShowModal;
end;
end;

Add Gesturing support by adding a TGestureManager (GestureManager1). Then assign GestureManager1 to the Touch.GestureManager property of the TDrawGrid. Open Touch.Gestures.Standard property of the TDrawGrid and select the gestures you want to be notified of. Create an OnGesture event and add code as needed.
The details are documented by Embarcadero

Related

Delphi appearance change will not let me drag a borderless window

I'm having an issue when changing the appearance of my window. For some reason, I can't drag my borderless window. I can drag the window, but I can't grab the corners to make the size of the window bigger. It's for a screenshot app.
procedure TFrmCapture.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
SC_DRAGMOVE = $F012;
begin
if Button = mbLeft then
begin
ReleaseCapture;
Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
end;
end;
procedure TfrmCapture.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_DOWN then
self.Top := self.Top + 1;
if Key = VK_UP then
self.Top := self.Top - 1;
if key = VK_LEFT then
self.Left := self.Left - 1;
if key = VK_RIGHT then
self.Left := self.Left + 1;
end;
procedure TfrmCapture.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if MouseIsDown then
begin
ReleaseCapture;
Self.Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0);
end;
end;
procedure TfrmCapture.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MouseIsDown := False;
end;
procedure TfrmCapture.mnuCaptureClick(Sender: TObject);
begin
//Set button press
ButtonPress := 1;
//Get window rect.
GetWindowRect(Handle, WindowInfo.wRect);
//Set left and top form pos.
WindowInfo.X := Left;
WindowInfo.Y := Top;
//Close form.
Close;
end;
procedure TfrmCapture.mnuCloseClick(Sender: TObject);
begin
ButtonPress := 0;
Close;
end;

Using TControlBar, how do I limit the movement of the bands to a single row?

I have use for the TControlBar component in my current project but I'm having issues with the control drawing extra rows when I'm moving the bands around,
basically what I want is the ControlBar to always only have 1 Row which is of fixed Height, and where the bands can't escape it while being dragged.
How can I achieve this ?
You can do a workaround for this:
procedure TForm1.ControlBar1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var R:TRect;
Pt:TPoint;
begin
Pt:=ControlBar1.ClientToScreen(Point(0,Y));
R.Left:=Pt.X;
R.Top:=Pt.Y;
R.Right:=Pt.X+ControlBar1.Width;
R.Bottom:=Pt.Y;
ClipCursor(#R);
end;
procedure TForm1.ControlBar1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ClipCursor(nil) ;
end;
With that you can restrict the mouse movement to allow only vertical positioning of the Bands.
I solved this months ago by basically deriving my own component from the TPanel class and implementing a drag solution of child panels to mimic the behavior I wanted.
This is the most basic principle I used to implement the desired effect :
var oldPos : TPoint;
procedure TMainForm.ControlMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
begin
if Button = mbLeft then
if (Sender is TWinControl) then
begin
inReposition:=True;
SetCapture(TWinControl(Sender).Handle);
GetCursorPos(oldPos);
TWinControl(Sender).BringToFront;
end else
((Sender as TLabel).Parent as TQPanelSub).OnMouseDown((Sender as TLabel).Parent as TQPanelSub,Button,Shift,X,Y)
end;
procedure TMainForm.ControlMouseMove(Sender: TObject; Shift: TShiftState; X: Integer; Y: Integer);
var
newPos: TPoint;
temp : integer;
begin
if (Sender is TWinControl) then begin
if inReposition then
begin
with TWinControl(Sender) do
begin
GetCursorPos(newPos);
Screen.Cursor := crSize;
(* Constrain to the container *)
Top := 0;
temp := Left - oldPos.X + newPos.X;
if (temp >= 0) and (temp <= (Parent.Width - Width))
then Left := temp;
oldPos := newPos;
end;
end;
end else
((Sender as TLabel).Parent as TQPanelSub).OnMouseMove((Sender as TLabel).Parent as TQPanelSub,Shift,X,Y);
end;
procedure TMainForm.ControlMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
begin
if inReposition then
begin
Screen.Cursor := crDefault;
ReleaseCapture;
inReposition := False;
end;
end;
This is just the basis that I wanted from the TControlBar which infact is a horribly written component.

TScrollbox MouseDown override

I created a Custom scrollbox derives from TScrollbox that works the same except that it will scrolls when dragging in the client area aside from its scrollbars.
My problem now is i cannot Drag To Scroll when mouse is on a button or panel inside my CustomScrollbox.
the MouseDown, MouseUp, MouseMove override will not trigger because it hovers into different controls.
How can I keep tracking the MouseDown, MouseUp, MouseMove and prevent Button/Panels events from firing(inside my CustomScrollbox) when i start dragging?
here's the video of my smooth CustomScrollbox
So you want to adjust the mouse down behaviour of all childs, in such way that when a dragging operation is being initiated, the mouse events of the clicked child should be ignored. But when no drag is performed, then it would be required to fire the child's mouse events as usual.
Not a bad question actually. Since most of the default control interaction is tight to the release of the mouse button (e.g. OnClick is handled in WM_LBUTTONUP), this still should be possible in an intuitive manner.
I tried the code below, and it feels quite nice indeed. It involves:
handling WM_PARENTNOTIFY to catch when a child control is clicked on,
bypassing Child.OnMouseMove and Child.OnMouseUp,
transfer control to the scrollbox when the move exceeds Mouse.DragThreshold,
resetting focus to the previous focussed control before the drag,
canceling all changes made to the child's mouse events after the drag.
unit Unit2;
interface
uses
Windows, Messages, Classes, Controls, Forms, StdCtrls, ExtCtrls;
type
TScrollBox = class(Forms.TScrollBox)
private
FChild: TControl;
FDragging: Boolean;
FPrevActiveControl: TWinControl;
FPrevScrollPos: TPoint;
FPrevTick: Cardinal;
FOldChildOnMouseMove: TMouseMoveEvent;
FOldChildOnMouseUp: TMouseEvent;
FSpeedX: Single;
FSpeedY: Single;
FStartPos: TPoint;
FTracker: TTimer;
function ActiveControl: TWinControl;
procedure ChildMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure ChildMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
function GetScrollPos: TPoint;
procedure SetScrollPos(const Value: TPoint);
procedure Track(Sender: TObject);
procedure WMParentNotify(var Message: TWMParentNotify);
message WM_PARENTNOTIFY;
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
public
constructor Create(AOwner: TComponent); override;
property ScrollPos: TPoint read GetScrollPos write SetScrollPos;
end;
TForm2 = class(TForm)
ScrollBox1: TScrollBox;
...
end;
implementation
{$R *.dfm}
{ TScrollBox }
type
TControlAccess = class(TControl);
function TScrollBox.ActiveControl: TWinControl;
var
Control: TWinControl;
begin
Result := Screen.ActiveControl;
Control := Result;
while (Control <> nil) do
begin
if Control = Self then
Exit;
Control := Control.Parent;
end;
Result := nil;
end;
procedure TScrollBox.ChildMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (Abs(FChild.Left + X - FStartPos.X) > Mouse.DragThreshold) or
(Abs(FChild.Top + Y - FStartPos.Y) > Mouse.DragThreshold) then
begin
MouseCapture := True;
TControlAccess(FChild).OnMouseMove := FOldChildOnMouseMove;
TControlAccess(FChild).OnMouseUp := FOldChildOnMouseUp;
MouseDown(mbLeft, Shift, FChild.Left + X, FChild.Top + Y);
FChild := nil;
if FPrevActiveControl <> nil then
FPrevActiveControl.SetFocus;
end
else
if Assigned(FOldChildOnMouseMove) then
FOldChildOnMouseMove(Sender, Shift, X, Y);
end;
procedure TScrollBox.ChildMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FChild <> nil then
begin
if Assigned(FOldChildOnMouseUp) then
FOldChildOnMouseUp(Sender, Button, Shift, X, Y);
TControlAccess(FChild).OnMouseMove := FOldChildOnMouseMove;
TControlAccess(FChild).OnMouseUp := FOldChildOnMouseUp;
FChild := nil;
end;
end;
constructor TScrollBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTracker := TTimer.Create(Self);
FTracker.Enabled := False;
FTracker.Interval := 15;
FTracker.OnTimer := Track;
end;
function TScrollBox.GetScrollPos: TPoint;
begin
Result := Point(HorzScrollBar.Position, VertScrollBar.Position);
end;
procedure TScrollBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
FDragging := True;
FPrevTick := GetTickCount;
FPrevScrollPos := ScrollPos;
FTracker.Enabled := True;
FStartPos := Point(ScrollPos.X + X, ScrollPos.Y + Y);
Screen.Cursor := crHandPoint;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TScrollBox.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
if FDragging then
ScrollPos := Point(FStartPos.X - X, FStartPos.Y - Y);
inherited MouseMove(Shift, X, Y);
end;
procedure TScrollBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
FDragging := False;
Screen.Cursor := crDefault;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TScrollBox.SetScrollPos(const Value: TPoint);
begin
HorzScrollBar.Position := Value.X;
VertScrollBar.Position := Value.Y;
end;
procedure TScrollBox.Track(Sender: TObject);
var
Delay: Cardinal;
begin
Delay := GetTickCount - FPrevTick;
if FDragging then
begin
if Delay = 0 then
Delay := 1;
FSpeedX := (ScrollPos.X - FPrevScrollPos.X) / Delay;
FSpeedY := (ScrollPos.Y - FPrevScrollPos.Y) / Delay;
end
else
begin
if (Abs(FSpeedX) < 0.005) and (Abs(FSpeedY) < 0.005) then
FTracker.Enabled := False
else
begin
ScrollPos := Point(FPrevScrollPos.X + Round(Delay * FSpeedX),
FPrevScrollPos.Y + Round(Delay * FSpeedY));
FSpeedX := 0.83 * FSpeedX;
FSpeedY := 0.83 * FSpeedY;
end;
end;
FPrevScrollPos := ScrollPos;
FPrevTick := GetTickCount;
end;
procedure TScrollBox.WMParentNotify(var Message: TWMParentNotify);
begin
inherited;
if Message.Event = WM_LBUTTONDOWN then
begin
FChild := ControlAtPos(Point(Message.XPos, Message.YPos), False, True);
if FChild <> nil then
begin
FPrevActiveControl := ActiveControl;
FOldChildOnMouseMove := TControlAccess(FChild).OnMouseMove;
TControlAccess(FChild).OnMouseMove := ChildMouseMove;
FOldChildOnMouseUp := TControlAccess(FChild).OnMouseUp;
TControlAccess(FChild).OnMouseUp := ChildMouseUp;
end;
end;
end;
end.
Note: When no drag is initiated (mouse movement < Mouse.DragThreshold), all mouse and click events of the clicked child remain intact. Otherwise only Child.OnMouseDown will fire!
For testing purposes, this answer is incorporated in the code above.
With thanks to #TLama for suggesting to use WM_PARENTNOTIFY.

How to drag report in ppviewer?

Anyone know how to drag the report in TppViewer? (Delphi 7) i try to use the dagdrop event and dragover event of ppviewer but failed, anyone can help?
procedure Tfrm1.ppviewer1DragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
inherited;
Accept := Source IS TppViewer;
end;
procedure Tfrm1.ppviewer1DragDrop(Sender, Source: TObject; X,
Y: Integer);
begin
inherited;
if Source is TppViewer then begin
TppViewer(Source).Left := X;
TppViewer(Source).Top := Y;
end;
end;
This answer assumes that you are trying to scroll in the report, by dragging.
TReportPreviewer is the Form
ReportViewer is the ppViewer
Dragging is a Boolean
SaveX, SaveY are Integer
procedure TReportPreviewer.ReportViewerMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Dragging := true;
SaveX := X;
SaveY := Y;
end;
procedure TReportPreviewer.ReportViewerMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
if Dragging then
begin
if ReportViewer.ScrollBox.Visible then
ReportViewer.ScrollBox.HorzScrollBar.Position := ReportViewer.ScrollBox.HorzScrollBar.Position - (X - SaveX);
if ReportViewer.ScrollBox.Visible then
ReportViewer.ScrollBox.VertScrollBar.Position := ReportViewer.ScrollBox.VertScrollBar.Position - (Y - SaveY);
SaveX := X;
SaveY := Y;
end;
end;
procedure TReportPreviewer.ReportViewerMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Dragging := false;
end;
I tried using ScrollBy instead of moving the scrollbar position, but it seemed to reset for some reason.
Are you trying to drag a report file into the Viewer? if so biased on the following advice:
How to Drop Images from Windows Explorer to a TImage control
Delphi - Drag & Drop with ListView
WM_DROPFILES Message
You can achieve this by using the following code:
procedure TMainForm.FormCreate(Sender: TObject);
begin
//Tell Windows that the Report Viewer accepts files
ShellAPI.DragAcceptFiles(ppViewer1.Handle,True);
Application.OnMessage := ApplicationMessage;
end;
procedure TMainForm.ApplicationMessage(var Msg: TMsg; var Handled: Boolean);
begin
if (Msg.hwnd = ppViewer1.Handle) and (Msg.message = WM_DROPFILES) then
begin
Handled := ReportFileDrop(Msg);
end;
end;
function TMainForm.ReportFileDrop(var Msg: TMsg):Boolean ;
var
numFiles : longInt;
buffer : array[0..MAX_PATH] of char;
l_file:String;
l_filemsg:TWMDROPFILES;
begin
Result := False;
//Convert the TMsg into a TWMDROPFILES record
l_filemsg.Msg := Msg.message;
l_filemsg.Drop := Msg.wParam;
l_filemsg.Unused := Msg.lParam;
l_filemsg.Result := 0;
numFiles := DragQueryFile(l_filemsg.Drop, $FFFFFFFF, nil, 0) ;
if numFiles > 1 then
begin
ShowMessage('You can drop only one file at a time!') ;
end
else
begin
try
DragQueryFile(l_filemsg.Drop, 0, #buffer, sizeof(buffer)) ;
l_file := buffer;
//Only try and load the report if the file has the correct extension
if (Length(l_file) > 0) and (ExtractFileExt(LowerCase(l_file)) = '.rtm') then
begin
//Load the Report
Result := True;
end;
except
//Handle errors
end;
end;
end;

Scroll TTreeView while dragging over/near the edges

I have a TTreeView that can have lots of nodes, when a lot of nodes are expanded the tree uses a lot of screen space.
Now suppose I want to drag a node that is near the bottom of the TreeView to the top, I can't physically see the top part of the TreeView because the node I am selecting is at the bottom. When dragging the node to the top of the TreeView I would like the TreeView to automatically scroll with me when dragging, by default this does not seem to happen.
A perfect example of this behaviour is seen in Windows Explorer. If you try to drag a file or folder, when you hover the dragged item (node) it automatically scrolls up or down depending on cursor position.
Hope that makes sense.
PS, I already know how to drag nodes, I want the TreeView to scroll with me when dragging if hovering near the top or bottom of the TreeView.
Thanks.
This is the code I use. It will work for any TWinControl descendent: list box, tree view, list view etc.
type
TAutoScrollTimer = class(TTimer)
private
FControl: TWinControl;
FScrollCount: Integer;
procedure InitialiseTimer;
procedure Timer(Sender: TObject);
public
constructor Create(Control: TWinControl);
end;
{ TAutoScrollTimer }
constructor TAutoScrollTimer.Create(Control: TWinControl);
begin
inherited Create(Control);
FControl := Control;
InitialiseTimer;
end;
procedure TAutoScrollTimer.InitialiseTimer;
begin
FScrollCount := 0;
Interval := 250;
Enabled := True;
OnTimer := Timer;
end;
procedure TAutoScrollTimer.Timer(Sender: TObject);
procedure DoScroll;
var
WindowEdgeTolerance: Integer;
Pos: TPoint;
begin
WindowEdgeTolerance := Min(25, FControl.Height div 4);
GetCursorPos(Pos);
Pos := FControl.ScreenToClient(Pos);
if not InRange(Pos.X, 0, FControl.Width) then begin
exit;
end;
if Pos.Y<WindowEdgeTolerance then begin
SendMessage(FControl.Handle, WM_VSCROLL, SB_LINEUP, 0);
end else if Pos.Y>FControl.Height-WindowEdgeTolerance then begin
SendMessage(FControl.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
end else begin
InitialiseTimer;
exit;
end;
if FScrollCount<50 then begin
inc(FScrollCount);
if FScrollCount mod 5=0 then begin
//speed up the scrolling by reducing the timer interval
Interval := MulDiv(Interval, 3, 4);
end;
end;
if Win32MajorVersion<6 then begin
//in XP we need to clear up transient "fluff"; results in flickering so only do it in XP where it is needed
FControl.Invalidate;
end;
end;
begin
if Mouse.IsDragging then begin
DoScroll;
end else begin
Free;
end;
end;
Then to use it you add an OnStartDrag event handler for the control and implement it like this:
procedure TMyForm.SomeControlStartDrag(Sender: TObject; var DragObject: TDragObject);
begin
TAutoScrollTimer.Create(Sender as TWinControl);
end;
Here's an alternative based on the fact that the selected node always automatically scrolls in view.
type
TForm1 = class(TForm)
TreeView1: TTreeView;
TreeView2: TTreeView;
procedure TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
procedure TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
FDragNode: TTreeNode;
FNodeHeight: Integer;
end;
...
procedure TForm1.TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
with TTreeView(Sender) do
begin
FDragNode := GetNodeAt(X, Y);
if FDragNode <> nil then
begin
Selected := FDragNode;
with FDragNode.DisplayRect(False) do
FNodeHeight := Bottom - Top;
BeginDrag(False, Mouse.DragThreshold);
end;
end;
end;
procedure TForm1.TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
Pt: TPoint;
DropNode: TTreeNode;
begin
Accept := Source is TTreeView;
if Accept then
with TTreeView(Source) do
begin
if Sender <> Source then
Pt := ScreenToClient(Mouse.CursorPos)
else
Pt := Point(X, Y);
if Pt.Y < FNodeHeight then
DropNode := Selected.GetPrevVisible
else if Pt.Y > (ClientHeight - FNodeHeight) then
DropNode := Selected.GetNextVisible
else
DropNode := GetNodeAt(Pt.X, Pt.Y);
if DropNode <> nil then
Selected := DropNode;
end;
end;
procedure TForm1.TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
var
DropNode: TTreeNode;
begin
with TTreeView(Sender) do
if Target <> nil then
begin
DropNode := Selected;
DropNode := Items.Insert(DropNode, '');
DropNode.Assign(FDragNode);
Selected := DropNode;
Items.Delete(FDragNode);
end
else
Selected := FDragNode;
end;
You may want to link the OnDragOver event handler to the parent of the TreeView too, which results in scrolling ánd dropping when the mouse is outside the TreeView. If you dó want the scrolling, but not the dropping when the mouse is outside the TreeView, then check if Target = Sender in the OnEndDrag event handler.
Just to be complete, workarounds like in the other answers are not required anymore. Later versions have an option for this:
TreeOptions.AutoOptions.toAutoScroll := True

Resources