Title: Using Drag and Drop in Delphi
Question: In this article I try to cocer Drag and Drop in Delphi applications
Answer:
Introduction
Due to the graphical nature of Windows the majority of operations can be performed solely with the mouse. One operation that is often useful is for the user to be able to drag an object from one position to another. Fortunately for us, Borland (Inprise) have made this facility easy to implement for the Delphi developer. The tutorial will show you how to do this, by explaining and providing sample code to drag items from one ListBox to another.
The Events Used
All Classes that are descendants of TControl have the following events available to us:
OnDragDrop
OnDragOver
OnEndDrag
OnStartDrag
I will explain the use of each of these events and all of their parameters, for more information on this check out the relevant help files.
OnDragDrop
This event is fired whenever an item is dropped on a control. The majority of the code needed for implementing drag and drop is entered here. The parameters are used as follows:
Parameter
Sender
Type
TObject
Purpose
The Sender parameter is the object which received the dropped item.
Parameter
Source
Type
TObject
Purpose
This is a pointer to the actual item that is being dragged (and dropped). In the case of an item in a listbox being dragged this points to the ListBox and not the actual text being dragged, so it is up to the developer to handle which item in the ListBox the intended drag operation is to be performed on.
Parameter
X,Y
Type
Integer
Purpose
These are the relative coordinates of where the object was dropped on the sender object.
OnDragOver
This event is fired whenever an item is dragged over a control. Its main use is to decide whether the item can be dropped here and change the cursor to reflect this. The parameters are used as follows:
Parameter
Sender, Source
Type
TObject
Purpose
Represent the same information as the onDragDrop event.
Parameter
X,Y
Type
Integer
Purpose
Represent the same information as the onDragDrop event.
Parameter
var Accept
Type
Boolean
Purpose
This should be set to true if the control that the drag is currently over can accept the dragged item. This is also reflected by the cursor being either the drag or the no-entry cursor (These can easily be changed to your own choice)
Parameter
State
Type
TDragState
Purpose
This parameter tells you whether the drag operation is just entering, moving into or leaving the Sender object.
OnEndDrag
This event is fired whenever an item is dropped. Unlike th onDrop event the use of this event is to perform the neccessary operation on the object that was dragged. This is normally used for cleaning up any thing that was created in the onStartDrag event. The parameters are used as follows:
Parameter
Sender
Type
TObject
Purpose
This is the Object that was actually dragged.
Parameter
Target
Type
TObject
Purpose
This is the Object that the drag object was dropped on. If the drag operation failed then this is set to nil.
Parameter
X,Y
Type
Integer
Purpose
The relative coordinates of where the drag object was dropped on the target parameter.
OnStartDrag
This event is fired whenever an item first dragged. Its is mainly used to initialize any variable that are needed for the drag operation. It is not always needed and will not be used in our simple example.
Parameter
Sender
Type
TObject
Purpose
This is the Object that is beginning to be dragged.
Parameter
DragObject
Type
TDragObject
Purpose
This Object allows us to store and pass on a lot more information about what is being dragged. Check out the help file for further information on this, when used correctly this can add great power at low cost to your applications.
An Example
What We Will Do
In this example we will have two listboxes on a form and implement the facility to allow our users to drag items from one listbox to the other, and also reposition items in each listbox by dragging and dropping the selected strings.
Designing The Interface
As this is a very simple application demonstrating how to implement drag and drop we only need a very simple application. The code that follows assumes that you have used the default names that Delphi provides, if not adjust accordingly.
The steps involved are:
o Start a new application.
o On the main form place two TlistBox objects (from the standard page of the component palette)
o For each ListBox item set the DragMode property to dmAutomatic (using the Object Inspector)- this means that we do not need to begin the drag operation manually
o Next use the Object Inspector to generate the events for the onDragOver and onDragDrop for one of the listboxes, and set the corresponding events of the other listbox to match those already generated (by using the drop down list in the Object Inspector)
That is the interface complete, all we need to do now is write the actual code.
The Code
As seen in the interface section we have only created to procedure stubs. These are in fact all that we need to implement our drag and drop.
The DragOver procedure is implemented as follows:
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
if Source is TListBox then
Accept := True;
end;
What we are doing here is making sure that the Object being dragged onto the listbox is also another listbox (though we only use the selected item in the listbox) and setting the accept var accordingly.
Next we need to accept the dropped object and update the listboxes contents accordingly. This is done in the DragDrop procedure as follows:
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
iTemp : integer;
ptTemp : TPoint;
szTemp : String;
begin
{ change the x,y coordinates into a TPoint record }
ptTemp.x:=x;
ptTemp.y:=y;
{ Use a while loop instead of a for loop due to items possible being removed from
listboxes this prevents an out of bounds exception }
iTemp := 0;
While iTemp begin
{ look for the selected items as these are the ones we wish to move }
if TListBox(Source).selected[iTemp] then
begin
{ use a with as to make code easier to read }
With Sender as TListBox do
begin
{ need to use a temporary variable as when the item is deleted the indexing
will change }
szTemp := TListBox(Source).items[iTemp];
{ delete the item that is being dragged }
TListBox(Source).items.Delete(iTemp);
{ insert the item into the correct position in the listbox that it was dropped on }
items.Insert(itemAtPos(ptTemp,true),
szTemp);
end;
end;
inc(iTemp);
end;
end;
I have thoroughly commented this procedure, so will not explain its workings here. All that it is doing is deleting the dragged item and adding it in the correct place in the listbox that it was dropped onto.
Closing Comments
I hope this tutorial has provided enough information for you to use drag and drop in your applications. The example shown is a very simple one but provides the basic framework for more complex tasks. In a later article or tutorial I intend to dig deeper into the use of TDragObject.