Contents Index Topic Contents | ||
Previous Topic: Debugging With the Shell Next Topic: File Manager Extensions |
Dragging and Dropping
This overview explains the general concepts that you need to know to allow your applications to support drag-and-drop capabilities. It also lists the basic steps for implementing drag-and-drop support.
About Dragging and Dropping
A Microsoft® Win32®-based application should fully support the source and target drag-and-drop capabilities provided by OLE. One of the most attractive features of drag-and-drop in OLE is that the code that handles the actual data transferyour implementation of the IDataObject interfaceis reusable. You will be able to use the same code to implement cut-and-paste operations. OLE separates what the user does to cause the data transfer from how your application actually transfers the data. This allows you to use the same back-end IDataObject interface for the various ways a user can decide to transfer data.
General OLE Concepts
One of the most useful features of OLE is that it is completely modular. It is designed so that each component can exist, for the most part, on its own. For example, you can add drag-and-drop support to your application without adding in-place activation, automation, or compound storage.
Even if your application only uses a small part of OLE, the Component Object Model (COM) enables other applications to know your application's OLE capabilities. COM is the fundamental, underlying model that OLE is based upon; all OLE objects are also COM objects.
COM stipulates that any COM object must control its own life span and be able to tell other objects about its capabilities when queried. To control its life span, a COM object maintains a reference count. Capabilities are grouped into logical sets called interfaces; each interface is a set of member functions necessary to support a certain capability. COM objects must use the IUnknown interface to respond to queries and maintain a reference count. Because all OLE interfaces are derived from IUnknown, they are COM objects. This interface has three member functions: QueryInterface, AddRef, and Release.
An object uses QueryInterface to tell other objects about its capabilities. If the object implements the requested interface, it returns a pointer to the interface. If it does not implement it, the object returns the E_NOTIMPL error value stating that the object does not support the requested interface. AddRef and Release are used to control the object's life span. AddRef is called when another object holds a pointer to the object, and Release is called when the pointer is no longer needed. If a call to Release causes the object's reference count to go to zero, the object can safely delete itself.
COM provides a couple of immediate benefits:
- An object can determine in advance if another object supports a certain feature. If the feature is not supported, the calling object can react accordingly.
- Objects do not remain in memory for more time (or less time) than necessary, and they do not rely on the user to launch or close them.
Drag-and-Drop in OLE
Every drag-and-drop operation involves two objects: a source and a target. The source object contains the data to be dragged, and the target object accepts the dragged data.
You can also use OLE drag-and-drop to add drag-and-drop support within your own application. There is nothing to prevent your application from being both a drop source and a drop target or from accepting dropped objects from itself.
The OLE data transfer mechanism is a crucial element of drag-and-drop support. This mechanism allows objects to be very specific about the data that they transfer. Instead of simply being able to transfer a bitmap, an object can now transfer a bitmap of the object's contents rendered for a printer device and stored in a stream to be released by OLE.
To accomplish this, OLE uses the IDataObject interface and the FORMATETC and STGMEDIUM structures. Applications implement IDataObject to accomplish all data transfers in OLE; it includes member functions that set and retrieve an object's data, enumerate the available data formats, and receive data change notifications. FORMATETC and STGMEDIUM provide details about the data that is being transferredincluding the target device, aspect, storage medium, and release method.
Adding drop source capabilities
To enable your application to become the source of a drag-and-drop operation, follow these steps:
- Initialize the OLE libraries. Any application that uses the OLE libraries must check the version of the libraries and call the OleInitialize function during this process. OleInitialize must be called before you call any other OLE functions. Make sure the system's OLE libraries are at least as recent as the ones for which the application was written. Because each call to OleInitialize must have a matching call to the OleUninitialize function, you should maintain an fOleInitialized flag so that you will know whether to call OleUninitialize when your application exits.
- Implement the IDropSource interface. Not including the member functions that it inherits from IUnknown, IDropSource has only two member functions: IDropSource::QueryContinueDrag and IDropSource::GiveFeedback. OLE calls QueryContinueDrag intermittently during the drag operation. Its parameters include the state of the keyboard, which the drop source uses to control the drag operation. The drop source returns the S_OK value to continue dragging, the DRAGDROP_CANCEL value to cancel dragging, or the DRAGDROP_DROP value to drop the object. OLE calls GiveFeedback to tell the drop source to update the cursor and ask the source window for visual feedback about what would happen if the user dropped at the current point. OLE will update to its default cursors if the DRAGDROP_S_USEDEFAULTCURSORS value is returned.
- Implement the IDataObject interface, which is used by OLE applications to transfer data. In a drag-and-drop operation, the drop source gives OLE a pointer to its IDataObject implementation. OLE saves the pointer and passes it to the drop target when the cursor first enters the target window and when the drop occurs. Fortunately, you only need to implement the following IDataObject member functions for drag-and-drop support: IDataObject::GetData, IDataObject::GetDataHere, IDataObject::QueryGetData, and IDataObject::EnumFormatEtc.
- Call the DoDragDrop function to begin the drag operation. After you have detected that the user wants to drag something, you should call this function. OLE uses the IDataObject and IDropSource pointers that are passed in, along with its list of registered drop targets, to control the drag operation. When the drag operation is complete, DoDragDrop returns either the DRAGDROP_S_DROP or DRAGDROP_S_CANCEL value. In addition, OLE returns a DWORD in the address pointed to by the pdwEffect parameter that tells how the drop should affect the source datathat is, whether the operation was a move, copy, link, or scroll. You should look at the pdwEffect value and modify the source data as necessary.
- Call OleUninitialize. Before an OLE application exits, it must call this function to release the OLE libraries. You should check your fOleInitialized flag before calling OleUninitialize. It should only be called if OleInitialize returned successfully.
Adding drop target capabilities
To enable your application to become a drop target, follow these steps:
- Initialize the OLE libraries. You should check the build version and call the OleInitialize function exactly as you would for a drop source.
- Call the RegisterDragDrop function. OLE keeps a list of the windows that are drop targets. Every window that accepts dropped objects must register itself and its IDropTarget interface pointer. Then when the user drags the object over a drop target window, OLE has the IDropTarget pointer handy.
- Implement the IDropTarget interface. OLE uses the IDropTarget pointer that you registered with RegisterDragDrop to keep you informed of the state of a drop operation. When the cursor first enters a registered drop target window, OLE calls the IDropTarget::DragEnter member function. In this member function, you should ensure that your application can create the dragged object if it is dropped. Your application may also display visual feedback showing where the dropped object will appear, if appropriate.
When the cursor moves around inside a drop target window, OLE calls the IDropTarget::DragOver member function, just as Microsoft Windows® 95 sends WM_MOUSEMOVE messages. Here you should update any visual feedback that your application displays to reflect the current cursor position.
When the cursor leaves a drop target window, OLE calls the IDropTarget::DragLeave member function. In DragLeave you should remove any feedback you displayed during DragOver or DragEnter. OLE calls your IDropTarget::Drop member function when the user drops the object. To be precise, a drop occurs when the drop source returns the DRAGDROP_DROP value from the IDropSource::QueryContinueDrag member function. In the Drop member function, you should create an appropriate object from IDataObject that is passed as a parameter. The following example shows how to implement IDropTarget::Drop.
STDMETHODIMP CDropTarget::Drop (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pointl, LPDWORD pdwEffect) { FORMATETC fmtetc; SCODE sc = S_OK; UndrawDragFeedback(); // removes any visual feedback // QueryDrop returns TRUE if the app. can accept a drop based on // the current key state, requested action, and cursor position. if (pDataObj && QueryDrop(grfKeyState,pointl,FALSE,pdwEffect)) { m_pDoc->m_lpSite = CSimpleSite::Create(m_pDoc); m_pDoc->m_lpSite->m_dwDrawAspect = DVASPECT_CONTENT; // Initialize the FORMATETC structure. fmtetc.cfFormat = NULL; fmtetc.ptd = NULL; fmtetc.lindex = -1; fmtetc.dwAspect = DVASPECT_CONTENT; // draws object's content fmtetc.tymed = TYMED_NULL; HRESULT hrErr = OleCreateFromData (pDataObj,IID_IOleObject,OLERENDER_DRAW, &fmtetc, &m_pDoc->m_lpSite->m_OleClientSite, m_pDoc->m_lpSite->m_lpObjStorage, (LPVOID FAR *)&m_pDoc->m_lpSite->m_lpOleObject); if (hrErr == NOERROR) // The object was created successfully. else // The object creation failed. sc = GetScode(hrErr); } return ResultFromScode(sc); }
- Call the RevokeDragDrop function. Before a drop target window is destroyed, it must call this function to allow OLE to remove the window from its list of drop targets.
- Uninitialize the OLE libraries. Like a drop source, your application needs to uninitialize the OLE libraries before terminating.
Scrap Files
Windows allows the user to transfer objects within a data file to the desktop or a folder. The result of the transfer operation is a file icon called a scrap. An OLE application automatically supports the creation of scrap files if its IDataObject interface supports enough data formats so that the drop target can create either an embedding or a shortcut object. You do not need to add any other functionality to your application to allow the user to create a scrap file. However, there are three optional features you may wish to add to your application: round-trip support, caching additional data formats in a scrap file, and delay-rendered formats.
Round-trip support
Round-trip support allows an object to be dragged out of a document and into a new container and then dragged from the new container back into the original document. When the user transfers a scrap into your application, it should integrate the scrap as if it were being transferred from its original source. For example, if a selected range of cells from a spreadsheet is transferred to the desktop, the cells become a scrap. If the user transfers the resulting scrap into a word processing document, the cells should be incorporated as if they were transferred directly from the spreadsheet. Similarly, if the user transfers the scrap back into the spreadsheet, the cells should be integrated as if they were originally transferred within that spreadsheet.
Your application must include code that integrates a scrap into a document; otherwise, the embedding object of the scrap is copied into the document rather than the data associated with the scrap. To retrieve the data for the scrap, your application must examine the class identifier (CLSID) of the scrap object by retrieving the CF_OBJECTEDESCRIPTOR file format data. If the application recognizes the CLSID, the application should transfer the native data into the document rather than calling the OleCreateFromData function.
Caching additional data formats
When an IDataObject interface object is dropped onto a file system folder, such as the desktop, the shell receives the CLSID of the object and looks for the list of clipboard formats to be cached in the scrap file. The list is located in the following registry location:
HKEY_CLASSES_ROOT\CLSID\{clsid of object}\DataFormats\PriorityCacheFormatsThe clipboard formats should be added to the registry as the names of named values (the value should be empty). The additional formats give the user more choices when copying the scrap file and opening the Paste Special dialog box from another application. You should choose only useful formats to keep the scrap file from becoming too large. For example, MSPaint scrap-caches the CF_BITMAP format and WordPad scrap-caches the RTF format as shown in the examples below.
HKEY_CLASSES_ROOT\CLSID\{D3E34B21-9D75-101A-8C3D-00AA001A1652}\DataFormats\PriorityCacheFormats,"#8",,"" HKEY_CLASSES_ROOT\CLSID\{73FDDC80-AEA9-101A-98A7-00AA00374959}\DataFormats\PriorityCacheFormats,"Rich Text Format",,""Delay-rendered formats
Certain data formats can specify that data will be rendered at a time later than when the data transfer normally occurs. These are called delay-rendered formats. The list of clipboard formats to be delay-rendered is specified under the HKEY_CLASSES_ROOT\CLSID\{clsid}\DataFormats\PriorityCacheFormats key. The IDataObject of a scrap object with this CLSID will offer these formats in addition to the native data and cached data. When the drop target requests one of these formats, the shell runs the application and renders the format from the active object. However, you should avoid using this mechanism because it does not work if the server is not available or if the application is not an OLE application.
Clipboard Formats for Shell Data Transfers
Windows allows the user to transfer data objects between applications and the shell. The user can transfer data objects, such as printers, files, shortcuts, and folders, either by dragging and dropping them or by using the Cut, Copy, and Paste menu commands. All transfer methods involve the clipboard.
Windows defines several clipboard formats that you can support to transfer objects between your application and the shell. The Windows header files do not include predefined clipboard format identifiers for these clipboard formats. Instead, they provide a set of clipboard format names and corresponding identifiers. To obtain an identifier for a clipboard format, you simply pass the format's identifier to the RegisterClipboardFormat function. The following list contains the clipboard format names and their corresponding identifiers.
Format name Identifier "FileContents" CFSTR_FILECONTENTS "FileGroupDescriptor" CFSTR_FILEDESCRIPTOR "FileName" CFSTR_FILENAME "FileNameMap" CFSTR_FILENAMEMAP "InShellDragLoop" CFSTR_INDRAGLOOP "Net Resource" CFSTR_NETRESOURCES "Paste Succeeded" CFSTR_PASTESUCCEEDED "Performed DropEffect" CFSTR_PERFORMEDDROPEFFECT "Preferred DropEffect" CFSTR_PREFERREDDROPEFFECT "PrinterFriendlyName" CFSTR_PRINTERGROUP "Shell IDList Array" CFSTR_SHELLIDLIST "Shell Object Offsets" CFSTR_SHELLIDLISTOFFSET "UniformResourceLocator" CFSTR_SHELLURL The following sections describe the clipboard formats used to transfer data between applications and the shell.
CF_HDROP format
The global memory object contains a DROPFILES structure. If the object was copied to the clipboard as part of a drag-and-drop operation, the pt member of DROPFILES includes the coordinates of the point where the drop occurred. The pFiles member is the offset to a double null-terminated list of file names. An application can retrieve information from the data object by passing the object's handle to the DragQueryFile and DragQueryPoint functions.
"FileContents" format
The data object contains the contents of one or more files in a format that can be written to a file. When a group of files is being transferred, the target of the drag-and-drop operation can use the lindex member of the FORMATETC structure to indicate which file to retrieve. The names and attributes of each file are contained in the "FileGroupDescriptor" data.
"FileGroupDescriptor" format
The data object contains the file names and attributes of a group of files being transferred during an OLE style drag-and-drop operation. The data object consists of a FILEGROUPDESCRIPTOR structure and any number of FILEDESCRIPTOR structures (one for each file in the group).
"FileName" format
The global memory object contains a single null-terminated and fully qualified file name. This format is supported for compatibility with applications written for Windows version 3.1. New applications should support the CF_HDROP clipboard format instead of the "FileName" format.
"FileNameMap" format
The "FileNameMap" format is used with the CF_HDROP clipboard format to rename a list of files that are copied to a new location during a copy and paste operation or a drag-and-drop operation. Data in the "FileNameMap" format consist of a double null-terminated list of file names; each corresponds to the file names in the CF_HDROP data. When the files listed in the CF_HDROP data are copied to the new location, the files receive the new names specified in the "FileNameMap" data. For example, if the CF_HDROP data contains two files with the names c:\temp.000 and c:\temp.001, the "FileNameMap" data contains the following list of file names.
"new.txt\0another.txt\0\0"If the files are copied to c:\target, they receive the following names:
c:\target\new.txt(was c:\temp.000) c:\target\another.txt(was c:\temp.001)The system stores files in the recycle bin using a coding system for the file names (dcxxxx.ext). When the user drags or copies files from the recycle bin, the system uses the file names specified in the "FileNameMap" format to rename the files.
"InShellDragLoop" format
The global memory object contains a DWORD value that indicates if the data object is inside of a drag-and-drop loop. If this value is nonzero, the data object is inside of a drag-and-drop loop. If this value is zero, the data object is not inside of a drag-and-drop loop. This allows a data object implementation to avoid performing time-consuming rendering of data while the user is dragging the object. It also allows the data object to know when the object is actually dropped so that the full data can be rendered.
"Paste Succeeded" format
The global memory object contains a DWORD value that indicates that the drop target's delete-on-paste operation succeeded. This allows the drop target to specify the DROPEFFECT_ flag that was actually performed. The drop target calls IDataObject::SetData to set this value when the operation is complete.
"Performed DropEffect" format
The global memory object contains a DWORD value that represents the drop target's performed drop effect. This allows the drop target to specify the DROPEFFECT_ flag that was actually performed. In the case of an optimized move operation, the target can complete the move, including deletion of the source object, and set this clipboard format to DROPEFFECT_MOVE so that the source does not attempt to delete the source object. The drop target calls IDataObject::SetData to set this value when the operation is complete.
"Preferred DropEffect" format
The global memory object contains a DWORD value that represents the drop source's preferred drop effect. This allows the drop source to specify which of the DROPEFFECT_ flags passed to DoDragDrop is the default drop action.
"PrinterFriendlyName" format
This format is similar to the CF_HDROP format, except that the pFiles member of the DROPFILES structure is the address of a double null-terminated list of printer-friendly names.
"Shell IDList Array" format
The global memory object contains an array of item identifier lists. The memory object consists of a CIDA structure that contains offsets to any number of item identifier lists (ITEMIDLIST structures). The first structure in the array corresponds to a folder, and subsequent structures correspond to file objects within the folder. If the ITEMIDLIST for the folder is empty (mkid.cb is 0), the parent folder is the desktop itself.
"Shell Object Offsets" format
The global memory object contains an array of POINT structures. The first structure specifies the screen coordinates of a group of shell objects, and the remaining structures specify the relative offsets of each item in the group. All coordinates are in pixels.
"Net Resource" format
The global memory object contains a list of network resources. The memory object consists of an NRESARRAY structure and any number of NETRESOURCE structures (one for each network resource in the list). Note that the string parameters (LPSTR types) in the NETRESOURCE structure contain offsets instead of addresses.
"UniformResourceLocator" format
The global memory object contains a single null-terminated string that contains a Uniform Resource Locator (URL) address.
Top of Page
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.