OOP Delphi

Title: Dragtime
Question: I want to implement the behaviour seen in the IE toolbar customization dialog. How?
Answer:
If you ever tried to customize the Internet Explorer toolbar you may have noticed that you can drag listboxes items from one list to the other.
The effect is quite striking because a little arrow marks the position that the dragged item will take when dropped giving a good visual cue to the user; again, if you drag over the top or bottom items, the listbox scroll automatically to show new items if any.
Although you can achieve this behaviour using the common events exposed by the listbox itself, there is a simpler way: the DragListBox APIs.
These are 3 functions, 4 messages, a little of constants and a data structure that enable you to use the same effect in your applications with a minimal effort. Let's see how...
Start a new project and add the commctrl unit to the uses list. Every function and constant described in this article is declared in that unit. If you are hurry (or lazy) you can open the attached example project.
In the beginning you must register a window message that the system uses to notify the owner of the list box of the drag and drop states. The message is identified by a string constants the DRAGLISTMSGSTRING whose value is commctrl_DragListMsg; upon registration Windows returns an UINT that uniquely identifies the message in a system-wide basis; the message remain registered until the Windows session is closed so you dont need to deregister it.
Just after the message registration you can invoke the MakeDragList function. This function accepts the handle of the listbox to be transformed in a DragListbox.
The best place to invoke these functions is in the OnCreate event of your form. This is a snippet of the example attached to this article.
procedure TfrmMain.FormCreate(Sender: TObject);
begin
//Ask Windows the code for this message and store it!
uintDraglistMessage := RegisterWindowMessage(DRAGLISTMSGSTRING);
//Notify Window that we want a DragListBox
MakeDragList(lstSource.Handle);
end;
From now on Windows will react to user actions sending a message to the owner of the mutated listbox. Try to guess which message? Of course the same one you registered before.
Because of the dynamic nature of the message code you are forced to overload the form WndProc to check for the message notification. Thank to the nature of Delphi this is a mere formality. (Yup!)
When you receive a drag list message, the wParam contains the numeric identifier of the listbox that originates the event, while the lParam is a pointer to a DRAGLISTINFO structure; this one is a simple structure that contains the notification code, the handle of the list box and the screen coordinates of the mouse. The return value of the message changes depending on the notification code.
Now let's see the notification codes and their meanings.
The first (in time order) code you will get is DL_BEGINDRAG; this notification is sent when the user left click on a drag list items and moves the mouse. If the message return value is zero the operation is prevented, any other case let the operation begins.
Subsequently you will receive DL_DRAGGING notifications indicating that the user still moves mouse while keeping the left button pressed. You can exploit this notification to make your application reacts to the event; you can determine the item under the cursor using the LBItemFromPt function. This function accepts the handle of the list box, a TPoint with the mouse position and a boolean indicating wheter the listbox should scroll to show items or not; the return values is the item index or -1 if the mouse in not over an item.
The index you found can be used to call the DrawInsert function that draws the small arrow indicatig where the item will be inserted; the parameters to this function are the handle of the listbox owner, the handle of the listbox itself and the index to a listbox item. If you use -1 for the item index, the arrow vanishes.
You can use the message return value to change the shape of the cursor accordingly to the user actions if you set the return value to one of DL_STOPCURSOR, DL_COPYCURSOR or DL_MOVECURSOR; Any other value keep the current cursor shape. This is useful when you are using your own cursor shapes.
The following message notification you will get is DL_DROPPED. This is the place where you perform the changes on the list items: find the item with LBItemFromPt and behave consequently. The message return value is ignored so you dont need to set it. Dont forget to call DrawInsert using -1 for the item index
If, during the operation, the user presses the ESC key or the mouse right button, the drag operation is canceled and a DL_CANCELDRAG notification is sent. In this case you can simply call DrawInsert using -1 for the item index. Even in this case the message return is ignored.
The accompanying example contains all the code that shows how to respond to events and notifications just described. You may notice that the example uses just one listbox. This is true because I am preparing a new article showing how to drag and drop between two list boxes and have the exact same behaviour of IE toolbar dialog.
Up till then... enjoy!