TDragDrop

 
     

 

 

  Description
 
  TDragDrop is a component for COM/OLE based drag&drop operations. The component is able to make child-classes of TWinControl (e.g. TEdit, TMemo, TListView, etc.) to be the source AND target of drag&drop operations.

Normally, you only use this component directly (for testing), if you develop an own drag&drop component based on this class.

   
  Important for Use
 
  This file (index.htm) contains more information about restrictions, the disclaimer, remarks etc.
   
  History
 
  Version 4.30 published on 17-Dec-1999
 
  • The method DropHandler was added
  • The implementation of the context-menu has changed; now you can insert custom menuitems
  Version 4.20 published on 13-Nov-1999
 
  • IMPORTANT: The mouse coordinates pt in OnDragEnter, OnDragOver, OnDrop, OnDragDetect, OnprocessDropped and OnBeforeScrolling are not a longer absolute to the screen but relative to the DragDropControl; maybe, that you must adjust your code
  Version 4.10 published on 06-Jul-1999
 
  • Bug with AcceptOwnDnD fixed
  • The handling of the clipboard has totally changed. As result of it, the implemention of the method Execute has changed, but not the using. You won't notice the modification (and haven't to change code), unless you haven't created your own classes based on TDragDrop.

    Following is only important, if you've build your own class based on TDragDrop:

    Normally you create a data object in the method Execute before data is dragged. Simply move this code to the new method CreateDataObject, inherited from TDragDrop and your class are compatible with this version. As a result of this, the your child-class has automaticly the capability for clipboard operations without any additonal code for this in contrast to earlier times. So, the expenditure for child-class development has shrinked. Fine, or?

  Version 4.00 published on 30-May-1999
 
  • OnDragDetect was changed in the declaration; now it isn't so long any more
  • The available drag effects in SourceEffects and TargetEffects were corrected
  • The scroll cursors during d&d are now displayed
  • New great properties added: ScrollDetectOptions and SourceCompatibility
  • New design for the auto-detection for drag&drop
  Version 3.20 published on 3-May-1999
 
  • Property RenderOn extended with a new value
  • The bug with source & target effects in version 3.11 wasn't fixed in the Delphi4 component; this is now made up
  • The documentation is completed (for a longer time, there are the properties CHCopy, CHMove, CHLink etc. available)
  • Bug was fixed with DragOver (modifications of the dwEffect has no effect, if the target doesn't accept the data)
  • Some bugs with the auto-detection for drag&drop fixed (the component sends now always a "mousebutton-up" message, after a dectection has started; the detection had not worked, if the start position was near to the border of the visual control)
  Version 3.11 published on 27-Feb-1999
 
  • Property RenderOn extended with new values
  • New property: AvailableDropEffects
  • Bug fixed with source & target effects (mostly invisible)
  • In the Delphi2 version: unused method (SetTargetWindowControl) deleted (relict from an earlier time)
  • SetDragDropControl corrected (you can change now the DragDropControl at run-time, if you need)
  Version 3.10 published on 19-Jan-1999
 
  • Range-Check Error fixed in Execute
  • Bug in WinNT4 detected
  • Bug in detecting the language fixed
  • The property RenderAsync was replaced by the new property RenderOn
  • Bug fixed: You don't need any longer enable even the target effects to make your DragDropControl able to be the source of d&d operations
  • Bug fixed: Modifications of the drop effect dwEffect in the event-handlers OnXXXXX effects even to the drop
  Version 3.00 updated on 31-Nov-1998
 
  • Native version for Delphi4 available
  • Some bugs fixed
  • Polish version available
  • Paramters to the event OnProcessDropped and OnDragDetect added
  • ExecuteDragDrop renamed to Execute
  • Execute has new result values
  • The drop effects were renamed
  • Sorry for so heavy code breaking, but in my opinion, there was the necessity to change these things
  Version 2.01 published on 03-Oct-1998
 
  • Language auto-detect feature added
  • Some bugs fixed
  Version 2.00 updated on 03-Oct-1998
 
  • New methods & properties (clipboard actions, drag detect, etc.)
  • The use of the COM was corrected (ref-counter, etc.)
  Version 1.01 published on 12-Aug-1998
 
  • Some small updates
  Version 1.00 published on 06-Aug-1998
 
  • First official release
   
  Properties
 
  AcceptOwnDnD published
  If your DragDropControl is configured to be source AND target of drag&drop operations you can determine here, if your DragDropControl should accept its own drag&drop operation. This is important in example, if you want to change the order of Listbox items using this component and not the internal drag&drop operations.
 
  AutoDetectDnD published
  If your DragDropControl is configured to be source of drag&drop operations you can define here, if this component should detect drag operations or not. The property DragDetectDelta is involved in the detection.

IMPORTANT: If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".

 
  BringToFront published
  If the user has dropped data on the target control, you can determine here if the form should come to front after the drag&drop operation or not.
 
  CHCopy public
  Use this property to set a custom cursor for the copy-effect during drag&drop.
 
  CHLink public
  Use this property to set a custom cursor for the link-effect during drag&drop.
 
  CHMove public
  Use this property to set a custom cursor for the move-effect during drag&drop.
 
  CHScrollCopy public
  Use this property to set a custom cursor for the copy-effect during drag&drop and scrolling in the target window.
 
  CHScrollLink public
  Use this property to set a custom cursor for the link-effect during drag&drop and scrolling in the target window.
 
  CHScrollMove public
  Use this property to set a custom cursor for the move-effect during drag&drop and scrolling in the target window.
 
  DragDetectDelta published
  Is the drag detection enabled by AutoDetectDnD or calling StartDnDDetection, the drag detection was successful, if the mouse was moved over more than in this property defined pixels.
 
  DragDropControl published
  Choose here a component like Listbox etc. that should be handled as source and target for drag&drop operations.

IMPORTANT: If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".

 
  OwnerIsSource public, read-only
  This property indicates during a drag&drop operation, if the DragDropControl is the source of the d&d-operation or not.
 
  Registered public, read-only
  You can determine here, if the DragDropControl is registered as target of drag&drop operations or not.
 
  RenderOn published
  You can determine here, when the dropped data should be rendered (only important for the inherited classes).

If it is set to rdoDropSync, the data is rendered before calling the event-handler OnDrop.

If it is set to rdoDropAsync, the data is rendered before calling the event-handler OnProcessDropped.

If it is set to rdoEnter, the data is rendered before calling the event-handler OnDragEnter.

If it is set to rdoEnterAndDropSync, the data is rendered before calling the event-handler OnDragEnter AND before calling the event-handler OnDrop (yes two times - in some cases it's necessary).

If it is set to rdoEnterAndDropAsync, the data is rendered before calling the event-handler OnDragEnter AND before calling the event-handler OnProcessDropped (yes two times - in some cases it's necessary).

If it is set to rdoNever, the data is never rendered. Normally, you never need this value - it makes only sense for application-internal drag&drop (the application knows the data; why should we spend our precious time on rendering the known data?)

Be careful, if you set this property to rdoDropAsync, rdoDropEnterAndDropSync, rdoDropEnterAndDropAsync or rdoEnter, because the OLE2 Drag&Drop isn't correctly implemented in every program. On testing (high recommend!!!), you will be surprised, how many well-known programs make trouble because of wrong implemention.

 
  ScrollDetectOptions published
  This property has many sub-properties:
  • AreaBottom, AreaLeft, AreaRight and AreaTop
  • HorzScrolling and VertScrolling
  • HorzPageScroll and VertPageScroll
  • ScrollDelay and StartDelay

AreaBottom, AreaLeft, AreaRight, AreaTop have the sub-properties Margin, Range. You can determine with Margin where the scroll-detect-area begins, measured from the clientarea-border. You can determine with Range the width resp. height of the scroll-detect-area. The properties AreaBottom and AreaTop are used for the vertical scroll detection, AreaLeft and AreaRight for the horizontal detection.

HorzScrolling and VertScrolling are further sub-properties under ScrollDetectOptions. You can enable/disable the scroll detection with these properties.

HorzPageScroll and VertPageScroll are even further sub-properties under ScrollDetectOptions. You can switch here between line or page scrolling.

ScrollDelay is even a sub-property under ScrollDetectOptions. You can determine with property the delay time to the next scroll event in the DragDropControl.

StartDelay is even a further sub-property under ScrollDetectOptions. You can determine with property the delay time till the first scroll event is sent to the DragDropControl.

The scroll-detection does only work, if at least one TargetEffect is set.

 
  SourceCompatibility published
  This property was added due to incompatible implemention of drag&drop in other applications. They set some value incorrectly in the FormatEtc record, because these values aren't used for the dragged format. With this property you can determine, if you don't to check these values for a successful drag&drop. If you disable a check, you take risk to drop data in a invalid format. Normally, you need not to switch this property.
 
  SourceEffects published
  Choose the effects that the DragDropControl should support. If you don't want that the DragDropControl serve as drag&drop source all items must be false.

IMPORTANT: If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".

 
  TargetEffects published
  Choose the effects that the DragDropControl should support. If you don't want that the DragDropControl serve as drag&drop target, all items must be false.
 
  TargetPopUpMenu published
  Here you determine, if should the popup-menu get visible when a drag&drop operations occurs with pressing the right mouse button or not. (Important, if the DragDropControl is configured as drag&drop target!!!)
 
  Events
 
  OnBeforeScrolling published
  This event is called before a scroll-event is sent to the DragDropControl. The scroll-detection must be enabled for this event.
 
  OnAfterScrolling published
  This event is called after a scroll-event is sent to the DragDropControl. The scroll-detection must be enabled for this event.
 
  OnDragDetect published
  Is called on starting (parameter DragStatus=ddsLeft or ddsRight, depends which mousebutton is pressed), on starting the drag (DragStatus=ddsDrag), on finishing the detection (DragStatus=ddsNone) and cancelling the detection (DragStatus=ddsCancelled). If DragStatus=ddsDrag, the drag&drop operation won't be started automatically. If you want to start drag&drop, you must call Execute on yourself. The parameters DetectStartX and DetectStartY specify the cursor position, where the detection has started. The parameters x and y specify the current position of the cursor.

IMPORTANT: If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".

 
  OnDragEnter published
  Is called, when the DragDropControl is a target of drag&drop operations. The mouse cursor moves ON (one call only) the DragDropControl. Here, you influence if a drop can be accepted and the drop's effect if accepted.
 
  OnDragLeave published
  Is called, when the DragDropControl is a target of drag&drop operations. The mouse cursor leaves the DragDropControl (one call only) or the drag&drop operation is cancelled.
 
  OnDragOver published
  Is called, when the DragDropControl is a target of drag&drop operations. The mouse cursor moves OVER (called on every mouse move) the DragDropControl. Even here, you can influence if a drop can be accepted and the drop's effect if accepted. Because this function is very often called, YOUR method should be programmed very efficient.
 
  OnDrop published
  Is triggered, when the DragDropControl is the target of drag&drop operations. It instructs the DragDropControl to handle the data which are dropped on it. Save here only the dropped data (, if this isn't done by the child-class), because the source of the drag&drop operation isn't accessable any longer as soon as this event-method was left. For processing the data in a child-class, it's high recommend to use OnProcessDropped.
 
  OnDropHandlerSucceeded protected
  Is triggered, if the DropHandler was successfully called and executed.

Also check the section: "Details about the DropHandler".

   
  OnGiveFeedback published
  Is called, when the DragDropControl is the source of drag&drop operations. It enables the source application to give visual feedback to its end user during a drag&drop operation by providing the OLE DoDragDrop function with an enumeration value specifying the visual effect. Mostly you don't need this event.
 
  OnMenuDestroy protected
  Is triggered, when the context-menu and its items are going to be destroyed. This event important for you, if you have add custom items to the menu.

Also check the section: "Details about the context-menu".

   
  OnMenuExecCmd protected
  Is triggered, when a custom item was selected in the context-menu by the user. Now, you should execute the routines etc, which are linked with the selected menuitem.

Also check the section: "Details about the context-menu".

   
  OnMenuPopup protected
  Is triggered, when the context-menu is going to popup.

Also check the section: "Details about the context-menu".

   
  OnMenuSucceeded protected
  Is triggered, when the routines etc, which are linked with the selected menuitem, were successfully executed.

Also check the section: "Details about the context-menu".

   
  OnProcessDropped published
  Is called after OnDrop by a self sended message with "PostMessage". This has the effect, that the user can access to the source of the drag&drop operation during you process the dropped data. Of course, you must save the dropped data in OnDrop. In my child-classes you haven't care about this.
 
  OnQueryContinueDrag published
  Is called, when the DragDropControl is a source of drag&drop operations. You can determine whether the drag&drop operation should be continued, cancelled, or completed. You do not call this method directly. The OLE DoDragDrop function calls this method during a drag-and-drop operation. Mostly, you don't need this event.
 
  Methods
 
  CopyToClipboard public
  Call this method, if you want to copy your data to the clipboard.
 
  DoMenuDestroy protected
  Never call the method directly - it is called by this component itself. This method has the same purpose as the event OnMenuDestroy. You can use this method for inherited classes.

Also check the section: "Details about the context-menu".

   
  DoMenuExecCmd protected
  Never call the method directly - it is called by this component itself. This method has the same purpose as the event OnMenuExecCmd. You can use this method for inherited classes.

Also check the section: "Details about the context-menu".

   
  DoMenuPopup protected
  Never call the method directly - it is called by this component itself. This method has the same purpose as the event OnMenuPopup. You can use this method for inherited classes.

Also check the section: "Details about the context-menu".

   
  DropHandler protected
  Never call the method directly - it is called by this component itself. Inherit this method for implementing custom drop-handlers.

Also check the section: "Details about the DropHandler".

   
  Execute public
  Call this method, if you detect that the user wants to start a drag&drop operation (e.g. MouseDown). Before calling, you have to create a valid IDataObject (for more informations, look over the source of the components "TDragDropFiles" or "TDragDropFiles").

IMPORTANT: If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".

 
  ExecuteOperation public
  This method is called by Execute. Normally, you don't call this method directly.
 
  GetFromClipboard public
  Call this method, if you want to get data from the clipboard. This will only succeed, if the data formats are supported.
 
  StartDnDDetection public
  Call this method, if you want to start the drag detection manually. Normally, you call this method in the OnMouseDown event.
 
  Constants
 
  DropEffect_None, DropEffect_Copy, DropEffect_Move, DropEffect_Link, DropEffect_Scroll
  These constants describe the kind of drop effect the user want to have. These values are used by the parameter dwEffect of methods. DropEffect_Scroll indicates a scrolling context of the drop target.
 
  How Do I Use It As Drag&Drop Source
 
  1. Drop a TWinControl on the form (e.g. a listbox).
  2. Choose the TWinControl (listbox) in DragDropControl.
  3. Choose the drag&drop effects under SourceEffects that the DragDropControl (listbox) should support. If you don't want any longer that the DragDropControl serve as drag&drop source all items must be set to false.
  4. Now, your DragDropControl (listbox) must detect, if the user wants to start a drag&drop operation. You can use the event "OnMouseDown" of your DragDropControl (listbox). You must create a DataObject. If you have no idea, how to do that, look over the source of the component TDragDropFiles or TDragDropText.
  5. Tell Windows that there is a drag&drop operation to handle: Just call the method ExecuteOperation of this component (for child-classes: call Execute). Now, windows handles the drag&drop operation.
  6. You may use the events OnGiveFeedback and OnQueryContinueDrag, but often you need not.
     
  Hints:
 
  • Drag&drop operations cannot be done at design-time!
  • If you use the drag-detection, it's high recommended to check the section "Problems With The Drag-Detection".
   
  How Do I Use It As Drag&Drop Target
 
  1. Drop a TWinControl on the form (e.g. a listbox).
  2. Choose the TWinControl (listbox) in DragDropControl.
  3. Choose the drag&drop effects under TargetEffects that the DragDropControl (listbox) should support. If you don't want any longer that the DragDropControl serve as drag&drop target all items must be set to false.
  4. Now, you have to program what happen, when the user drops a bitmap on your DragDropControl (listbox). For this, the best is to use the event OnDrop (or OnProcessDropped in the child-class; but only there!). At first, you should check what drag&drop effect was chosen by the user. You get the effect from method's parameter dwEffect.
  5. You may use the events OnDragEnter, OnDragOver and OnDragLeave, but often you need not.
     
  Hints:
 
  • Drag&drop operations cannot be done at design-time!
   
  Some Important Hints
 
 
  • Normally, you don't use this component for drag&drop. It's better and mostly simpler to implement a child-class like TDragDropFiles or TDragDropText. You can design your own drag&drop/clipboard-formats, but for such special formats and other formats, I don't support, you must write an own child-class. But I think, it shouldn't be so difficult with these parent-classes, I offer you here.
  • Don't start threads in OnDrop, because this may causes access violations. Mostly, you don't need a thread - you can use OnProcessDropped.
  • Don't try to start OLE drag&drop and Delphi internal's drag&drop at same time - it doesn't work. If you want to drop additional data, you've to build a own clipboard-format (RegisterClipboardFormat) and new child-class. Sorry, I don't know an easier way.
  • If you do drag data with the right mouse-button, you may get at some TWinControls a context popup-menu (e.g. TMemo). To avoid popping up the menu, define a own empty menu and set its property "AutoPopup" to false. Use the event OnDragDetect to determine, when set the TWinControl's property to the empty popup-menu or nil. Here an exsample for TMemo:

procedure TForm1.DragDropFilesEx1DragDetect(grfKeyState: Longint; x,y: Integer; DragStatus: TDragDetectStatus);
begin

if (DragStatus=ddsCancelled) or (DragStatus=ddsDrag) then Memo1.PopupMenu:=PopupMenu1;
if
DragStatus=ddsNone then Memo1.PopupMenu:=nil;
if
DragStatus=ddsDrag then
begin

// Prepare for dragging
// ...
// An example for the child-class
// DragDropText:

if
Memo1.Lines.Count=0 then exit;
DragDropText1.Lines.Assign(Memo1.Lines);
DragDropText1.Execute;

end;

end;

   
  How To Build An Own Child-Class
 
  Before you build your own child-class, it's very useful to understand the mechanism of drag&drop. Graham Wideman has written some very good documents with diagrams! It's high recommended to visit his site: http://www.wideman-one.com.

Use my "Drag&Drop-Analyser" for testing & analysing the dropped data. You can use it as target of drag&drop operations. It gives informations about the dropped data.

You have not to do much, if you build a child-class like TDragDropFiles or TDragDropText. You must only build a child-class from TEnumFormatEtc, TDataObject and TDragDrop. and override the following methods, the rest of the work, I've already done:

TDataObject:
function RenderData(var FormatEtc:TFormatEtc; var StgMedium: TStgMedium):HResult;

TDropTarget:
procedure
AccepTDataObject(DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: longint; var Accept:boolean);
procedure
RenderDropped(DataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: longint);

TDragDrop:
function
CreateDataObject:TDataObject;

   
  Details about the context-menu
 
  With the context-menu are following properties, events and methods involved: TargetPopUpMenu, OnMenuDestroy, OnMenuExecCmd, OnMenuPopup, OnMenuSucceeded, DoMenuDestroy, DoMenuExecCmd and DoMenuPopup. The context-menu does only appear, if TargetPopUpMenu is set to true and the data is dragged with the right mousebutton. Override the methods (DoXXXXXXX ), if you write a custom component based on TDragDrop otherwise use the events (OnXXXXXXX).

The order of the called methods/events after a drop is as following: First, xxMenuPopup (the "xx" stand for "Do" and "On") is called. You implement in xxMenuPopup your custom menuitems. Here is an example:

var MinCustCmd:integer;

procedure TForm1.DragDropFilesEx1MenuPopup(Sender: TObject; AMenu: HMENU; DataObj: IDataObject; AMinCustCmd, grfKeyState: Integer; pt: TPoint);
var lpmii:TMenuItemInfo;
begin

MinCustCmd:=AMinCustCmd; // we need this value later, so the variable must be global
fillchar(lpmii,sizeof(lpmii),0);
lpmii.cbSize:=sizeof(lpmii);
lpmii.fMask:=MIIM_TYPE
or MIIM_ID;
lpmii.wID:=AMinCustCmd;
lpmii.fType:=MFT_STRING;
lpmii.dwTypeData:=
'&test item';
lpmii.cch:=length(
'&test item');
MinCustCmd:=AMinCustCmd;
InsertMenuItem(AMenu,0,true,lpmii);

end;

If you override the method DoMenuPopup, you also must call:

inherited DoMenuPopup(Sender, AMenu, DataObj, AMinCustCmd, grfKeyState, pt);

You find more information on adding items to a popup-menu in the Win32-help. Look for "InsertMenuItem". If you add more items, you must increase AMinCustCmd for every new item, to identify the items later! Of course, you must remember the ID to all items or you don't know, which item was selected!

xxMenuExecCmd is only called, if it was detected, that one of your custom menuitems was selected. Now, you have to executed the routines etc, which are linked with the selected menuitem. Here the continue of the example:

procedure TForm1.DragDropFilesEx1MenuExecCmd(Sender: TObject; AMenu: HMENU;DataObj: IDataObject; Command: Integer; var dwEffect: Integer; var Succeeded: Boolean);
begin

Succeeded:=Command=MinCustCmd;
if Succeeded then ShowMessage(
'The test item was selected!');

end;

Don't forget to set the parameter Succeeded. Set only the value to true after the routine was successfully executed. If you override the method DoMenuExecCmd, you also must call the inherited method!

xxMenuSucceeded is only called, if the routines in xxMenuExecCmd were successfully executed. This notification is helpful, if you must update the display after successful update. If you override the method DoMenuSucceeded, you also must call the inherited method!

xxMenuDestroy is called after all. Now, it's time to de-intialize everything what you've initialized in xxMenuPopup. Here the continue of the example:

procedure TForm1.DragDropFilesEx1MenuDestroy(Sender: TObject; AMenu: HMENU);
begin

if DeleteMenu(AMenu,MinCustCmd,MF_BYCOMMAND) then messagebeep(0);

end;

If you override the method DoMenuDestroy, you also must call the inherited method!

   
  Details about the DropHandler
 
  The method DropHandler was implemented to allow alternative handling of the dropped data. This is especially important for implementing shell extentions. Was a drophandler successfully called, the event OnDropHandlerSucceeded is triggered. You can only use the the DropHandler only, if you inherit the class TDragDrop. Mostly, the coding in the method DropHandler is nearly similar with that in the method RenderDropped. First, you will render the dropped data, then you will handle the data. You don't do more in this method, and you cannot do more!
   
  FAQ
 
  Q: I want a single component to be the target of more than one type of drag, what can I do? One always overrides the other. If I use the TDragDrop, where is the information about what was dropped?

A: You've to write your own child-class from TDragDrop. But you can copy the most of my child-classes together, for faster developing your class.

You can't access the dropped data directly. For this you have the class TDataObject (look at params of methods ...). You have format description for every data(-set) in your data object . You can enumerate the available formats with EnumFormatEtc. With "QueryGetData" you can check, if a special format is available. The data itself you can get with "GetData". Because of my components design you should better use my new introduced method "RenderData".

I hope, it helps a little bit. Don't be disappointed, if you don't understand drag&drop immediately. I've even needed some months to understand the mechanisms completely.

   
  Problems With The Drag-Detection
 
  I got reports, that some controls e.g. listboxes or grids make trouble in some situations with the drag-detection. This behaviour results of a to old or just incompatible implementation of events in the controls. In such a case you must write a workaround. Mostly, following helps:

// The type of "P" is TPoint
GetCursorPos(P);
P := ScreenToClient(P);
SendMessage(<control>.Handle,WM_LBUTTONUP,0
   ,Longint(PointToSmallPoint(P)));

In some cases e.g. with grids this doesn't work. In these cases you must do a little bit more, as I show you the demo "GridFixDemo.zip". Mostly, the workaround isn't really difficult to write.

   
  Known Bugs
 
 
   

© 1998,99 by Dieter Steinwedel

Back to index