| 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

- Only with WinNT4: I got the report, that it is impossible to discover which drop effect has effected (important when using Execute)
- If you you have problems with the drag-detection, check the section " Problems With The Drag-Detection ".

© 1998,99 by Dieter Steinwedel

Back to index