Introduction
The file drag and drop functionality was added into Windows 3.x and has continued to be available in Windows 95/98 as well as Windows NT. File drag and drop, not to be confused with the more complex OLE drag and drop, allows the user of an application to select filenames listed in a Windows Explorer folder list, drag the filenames to an application window and drop them on the application's window. The user then expects those filenames to be available to the application.
This class provides a simpler interface to the file drag and drop functionality than using the DragQueryFile
function directly. It encapsulates the handling of the drag and drop into a single class which is used as an iterator to walk through the list of files. The class will also handle the cleanup of the HDROP
data structure by calling DragFinish(hDropInfo)
when the object's destructor is triggered as the object goes out of scope.
This class also demonstrates the use of forcing the copy constructor and the assignment constructor to be protected
in order to prevent the C++ compiler from automatically creating those methods when the class is used inappropriately. The class uses the HDROP
handle to the list of dropped files and we only want a single instance of this handle being used. Otherwise, when the object goes out of scope, we could have multiple releases of the same HDROP
structure with unknown consequences.
Usage
A typical use for this class is shown below in which a list control in a dialog is filled with file names which the user drags to the dialog from a Windows Explorer window. In order for this to work, you must modify the dialog properties under the Extended Styles tab, ensuring that the Accept files option is turned on. Another alternative is to add a call to the DragAcceptFiles
function in the OnCreate
handler of a window.
The class is a read only, sequential access iterator type of class with an internal position indicator. It appears to the client to be a tape of file names that can be read one after the other using the sNextFile
method. The tape can be rewound to the beginning using the Reset
method or the tape can be positioned to the first file using the sFirstFile
method. The file name at which the tape is currently positioned can be read using the sCurrFile
method. sCurrFile
can be used multiple times to re-read the same position of the file name list.
sFirstFile (CString buf)
- returns the first file in the list
sNextFile (CString buf)
- returns the next file in the list
sCurrFile (CString buf)
- returns the current file in the list
Reset ()
- reset the position indicator to before the first file
The file name is returned to the client using a provided CString
object. A CString
object was selected as the return type due to its versatility and its compatibility with LPCSTR
. The client is required to provide the CString
object so as to minimize memory management by the RDragDropFiles
class, or between the class and the client, by making it clear who owns the buffer.
The RDragDropFiles
class uses the operator ()
as the method to determine if the end of the file list is reached, when iterating over the file list using sNextFile
. The client can also check the CString
returned using the IsEmpty
method of the CString
class to see if a file name was found.
BEGIN_MESSAGE_MAP(XListDialog, CDialog)
ON_WM_DROPFILES()
END_MESSAGE_MAP()
BOOL XListDialog::OnInitDialog()
{
CDialog::OnInitDialog();
RECT irect;
m_ListControl.GetWindowRect (&irect);
m_ListControl.InsertColumn (0, "File Name", LVCFMT_LEFT,
irect.right - irect.left - 5);
return TRUE;
}
void XListDialog::OnDropFiles(HDROP hDropInfo)
{
rjc::RDragDropFiles myFiles (hDropInfo);
CString buf;
int iStat = 0;
int iL = 0;
while (myFiles ()) {
myFiles.sNextFile (buf);
iL++;
iStat = m_ListControl.InsertItem (iL, buf);
}
}
Below is the source code for the sFirstFile
method of the class. This code shows the use of the DragQueryFile
function to first find out the length of the file name (actually a pathname), set the CString
buffer to an appropriate length, and then fetch the file name using another call to DragQueryFile
providing a pointer to the CString
buffer.
As the class was developed and tested, I found that using a 1 index for the position indicator made the code easier when allowing the client to use the sFirstFile
, sNextFile
and the Reset
methods in whatever order they wanted.
CString & RDragDropFiles::sFirstFile (CString & sBuff)
{
ASSERT (hDropInfo);
iPosition = 1;
if (iPosition <= nFiles) {
UINT nLen = DragQueryFile (hDropInfo, iPosition - 1, NULL, 0);
char *pzsFN = sBuff.GetBufferSetLength (nLen+2);
DragQueryFile (hDropInfo, iPosition - 1, pzsFN, nLen+2);
sBuff.ReleaseBuffer ();
}
else {
sBuff.Empty ();
}
return sBuff;
}
Team lead for a point of sale software application written in C and C++. Previous experience with Nortel Networks on software for telecommunications products as well as Program Management.
Education:
BS Computer Science
MBA
Masters in Project Management