As we mention in the article "What a Drag!", VCL provides built-in support for drag-and-drop operations between components. Implementing this type of drag-and-drop operation is fairly simple, but a couple of small issues can hold you up if you're not familiar with how VCL's drag-and-drop mechanism actually works. In this article, we'll describe how to accomplish drag-and-drop between components. To do so, we'll examine the OnDragDrop, OnDragOver, OnEndDrag, and OnStartDrag events. We'll also take a look at the DragMode property.
For example, you may want dragging to be possible only when a particular flag is set to true in your application. In this case, your OnMouseDown event handler might contain code like the following:
if (allowDrag == true) ListBox1->BeginDrag(false);The BeginDrag() method's Immediate parameter, determines whether dragging should begin immediately or whether the mouse has to be moved a short distance before dragging begins. Typically, you'll call BeginDrag() with the Immediate parameter set to false so that a mouse click on a control doesn't register as a drag operation. (The rest of this article will assume that you have the DragMode property set to dmAutomatic.)
Once you've prepared the source component for dragging, VCL will display one of two drag cursors during dragging. The no-drop cursor (a white circle with a diagonal slash through it) will appear over a component that doesn't accept dropped items. When the cursor passes over a component that allows dropping, then the cursor will change to the drag cursor (an arrow with a blank piece of paper). The destination component controls whether or not it allows dropping.
The source component is now ready to allow dragging. So, let's turn our attention to the destination component.
void __fastcall TForm1::Memo1DragOver(
TObject *Sender, TObject *Source, int X,
int Y, TDragState State, bool &Accept)
{
}
While this function includes a lot of parameters, you'll mostly be concerned with the Accept parameter.
Set this parameter to true to allow dropping and to false to disallow dropping. The Source parameter
determines the component that originated dragging. For example, let's say you have a form with two
list boxes (ListBox1 and ListBox2) and a Memo component (Memo1). You want to let the user drag
from ListBox1 to the Memo component, but you don't want to let the user drag from ListBox2 to the
memo. The OnDragOver event handler will look something like this:void __fastcall TForm1::Memo1DragOver(
TObject *Sender, TObject *Source, int X,
int Y, TDragState State, bool &Accept)
{
if (Source == ListBox1) Accept = true;
else Accept = false;
}
That's all the code you have to write. VCL takes care of displaying the drag cursor when the user drags
from ListBox1 to the memo and the no-drop cursor when the user tries to drag from ListBox2 to the
memo. The user can let go of the mouse when the no-drop cursor is displayed, but VCL won't generate
an OnDragDrop event unless the component has said that it will accept dropping. Speaking of the
OnDragDrop event, let's take a look at it now. (For a description of the OnDragOver event's other
parameters, see the VCL online help.) If a component says that it will accept dropping, then an
OnDragDrop event will fire when the user drops something on the component.
For instance, continuing with our previous example, suppose you want to display the contents of the item dragged from ListBox1 to the Memo component. The OnDragDrop event handler will be as follows:
void __fastcall TForm1::Memo1DragDrop(
TObject *Sender, TObject *Source, int X, int Y)
{
int index = ListBox1->ItemIndex;
String item = ListBox1->Items->Strings[index];
Memo1->Lines->Add(item);
}
You've already determined that the only list box for which you'll allow dropping is ListBox1.
Accordingly, you know that if you receive an OnDragDrop event, the source component is ListBox1.
As a result, you don't perform any checks to see who the sender is--you simply get the text for the
current list box index and add it to the memo's text.
Listing A contains a program that let you drag items from a list box to a Memo component. (You can
download our sample project files from www.cobb.com/cpb.) The Memo component won't accept
items dropped from a second list box on the form. To run this program, place a Memo component, a
Label component, and two ListBox components on a form. Set the DragMode property for both list
boxes to dmAutomatic. Double-click on the events for the list boxes and memos to generate the event
handlers. Run the program and observe how the different components react when you attempt to drag
and drop between them.
Listing A: VCLDDTst.CPP
//----------------------------------------------------- #include#pragma hdrstop #include "DDMain2.h" //----------------------------------------------------- #pragma resource "*.dfm" TForm1 *Form1; //----------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //----------------------------------------------------- void __fastcall TForm1::Memo1DragOver( TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) { if (Source == ListBox1) Accept = true; else Accept = false; } //----------------------------------------------------- void __fastcall TForm1::Memo1DragDrop( TObject *Sender, TObject *Source, int X, int Y) { Memo1->Lines->Add( ListBox1->Items->Strings[ ListBox1->ItemIndex]); } //----------------------------------------------------- void __fastcall TForm1::ListBox1StartDrag( TObject *Sender, TDragObject *&DragObject) { Label1->Caption = "Status: Drag Started"; } //----------------------------------------------------- void __fastcall TForm1::ListBox1EndDrag( TObject *Sender, TObject *Target, int X, int Y) { if (Target == Memo1) Label1->Caption = "Status: Drag Accepted"; else Label1->Caption = "Status: Drag Aborted"; }
Kent Reisdorph is a senior software engineer at TurboPower Software and a member of TeamB, Borland's volunteer online support group. He's the author of Teach Yourself C++Builder in 21 Days and Teach Yourself C++Builder in 14 Days. You can contact Kent at kentr@turbopower.com.