Last edit: 05-03-17 Graham Wideman |
Delphi |
GWDropTarget unit and GWDropDemo code Article created: 98-06-26 |
This page describes the main features of the GWDropTarget unit, and the GWDropDemo application code.
The diagram below shows the basic model.
The basic idea is that main apparatus that deals with COM is located in GWDropTarget.PAS, and can be used as-is in a variety of applications. The piece that's specific to a particular app can inherit the basics and override selected functions for that app's needs. This is demonstrated in the GWDropDemo application, which uses that GWDropTarget.PAS unit, and places all the app-specific drop functionality in DropDemoAppInterface.PAS.
The second piece to notice is that GWDropTarget.PAS contains two classes, not just the TGWDropTarget object that implements the IDropTarget interface. Why? Because extra functionality is needed to deal with the fact that once the IDropTarget object is handed over to the system, the system has some control over destroying it. We therefore need someplace else to stick functionality that has a different lifetime. This helper (TGWDropAppInterface) or its descendant turns out to be a convenient place to delegate the app-specific details of drag-time behavior, and dealing with the interface to the app's data, and the app's user-interface.
Finally, TGWDropTarget inherits from TGWMonInterfacedObject and consequently can cooperate with the monitoring apparatus in GWMonOLE.pas. This can be omitted if not needed.
TGWDropAppInterface
Initialization and state | |
constructor Create(AOwner: TForm; UseMonitor: Boolean); destructor Destroy; |
|
function DropTarget_Create( AWinControl: TWinControl; Reg: Boolean ): HResult; |
Call this to create the actual TGWDropTarget object. Pass AWinControl as the control that will receive drops. Set Reg true if you want to lock and register the IDropTarget immediately. This is usually the desired option, but not for the GWDropDemo app. |
property DropTarget: TGWDropTarget; |
Use this to access DropTarget functions directly. |
function DropTarget_Exists: Boolean; |
Call this function to find out if the DropTarget exists (before calling DropTarget functions). |
procedure DropTarget_Forget; |
Don't call this. TGWDropTarget.Destroy calls this to let TGWDropAppInterface know that DropTarget is being destroyed. |
function DropTarget_LifeState: Tgwdt_ls; |
For diagnostics purposes this
indicates the state of the DropTarget object. gwdt_ls_start: object doesn't exist gwdt_ls_exists: object exists gwdt_ls_locked: object has been locked gwdt_ls_regd: object has been RegisterDragDrop'ed, ready to accept drops |
IDragDrop delegation functions. Override these to provide app-specific drag behavior. Without writing any code, the default implementation, together with the default helper functions, will at least allow your app to visually appear to accept drops, even though it does nothing with the data. | |
function DragEnter( const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint ): HResult; virtual; |
Called once by the system when
user drags into target area. Decide whether to accept the drop, usually by using dataObj to look at formats that source app is offering. Save conclusion for use by DragOver. Decide on drop effect desired based on shift/ctrl key state. Provide target window visual hit feedback if desired. |
function DragOver( grfKeyState: Longint; pt: TPoint; var dwEffect: Longint ): HResult; virtual; |
After an initial DragEnter, this is called repeatedly by the system as user drags within target area. Return indication of drop acceptance and drop effect. |
function DragLeave: HResult; virtual; |
System calls this once to indicate that "Elvis has left the building" (er, I mean the user has dragged outside of target area.) Opportunity to undo visual hit feedback. |
function Drop( const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint ): HResult; virtual |
System calls this once to indicate that user finished dropping (mouse-up) in target area. Use dataObj to get data. Return indication of drop effect (eg: DROPEFFECT_MOVE tells source app to delete its copy of the data). |
Drop Helper functions | |
function Drop_StdEffect( grfKeyState: Longint ): Longint; virtual; |
Translates state of shift/ctrl modifier keys into standard copy/move effects. Returns a dwEffect value. |
function Drop_Logic( phase: Tddp; dataObj: IDataObject; grfKeyState: Longint; pt: TPoint ): Longint; virtual; |
The default DragEnter/Over/Drop functions call Drop_Logic. Override this if you prefer to centralize all your drop logic in one place and avoid overriding the individual Drag functions. Works for simpler cases. Phase indicates which drag routine is calling. |
procedure Drop_Feedback( phase: Tddp; pt: TPoint; dwEffect: longint ); virtual; |
Dumb default visual hit feedback. Temporarily changes control's background color during hit. |
TGWDropTarget Notes
You should not have to modify TGWDropTarget or work with its functions and variables directly, though of course you may be interested in seeing how it works by looking at the source code.
One noteworthy feature is how TGWDropTarget resolves the potential Destroy-loop problem. The nature of the problem is this:
Possible Strategies:
This I have attempted to do in the TGWDropTarget code.
98-10-02: IE seems to create a lot of confusion for people wanting to write apps that accept URLs dragged from IE (This discussion applies to IE 3.02, and presumably other versions too) . The basic problem is this: IE offers only a drop effect of DROPEFFECT_LINK, and wants the receiving app to respond with a request for the same. This means that apps that only look at the key-shift state to create their request will only be able to accept drops from IE when the user presses <Ctrl-Shift> -- not entirely obvious to users.
Instead, drop-accepting apps apps should be careful to choose an effect from among those drop source offers, modified by what shift keys the user presses. This is incorporate in GWDropDemo in V1.12 and later.
Note that Netscape (at least as of V4.04) does not precipitate this confusion since it offers several drop effect options.
Go to: Drop Demo Intro Page, or