|
INPRISE Online And ZD Journals Present:
Let's face it: As computer users we live in a drag-and-drop world. Sure, drag-and-drop is a buzz phrase, but this technique is also very useful. When I get a new software package, it isn't long before I find myself trying to drag and drop some element of the program. If dragging and dropping works, I'm happy and on my way again. If it doesn't work, I grumble a little. To produce high-quality applications, you need to implement the features users have come to expect in recent years--and drag-and-drop is definitely one of those features. VCL offers a certain degree of drag-and-drop capability. For example, you can drag and drop from one component to another on a form. You can even drag from a component on one form to a component on another form. However, VCL lacks support for dragging and dropping files from Windows Explorer to an application. In this article, we'll demonstrate how you can let your C++Builder applications accept files dropped from applications such as Windows Explorer, the Find Files dialog box, or the Windows Desktop. In the course of our discussion, we'll examine the Windows API functions DragAcceptFiles() and DragQueryFile(), along with the WM_DROPFILES message. (For a discussion of dragging and dropping between VCL components, see "A Drop in the Bucket" in the September issue of C++Builder Developer's Journal.) Laying the groundwork
1. Tell Windows that your
application will The third step certainly requires the most work. Let's examine these steps individually so you'll have a clearer picture of what's required to support drag-and-drop in your applications. Receiving
After you've included the header, you need to call DragAcceptFiles() as follows:
The first parameter of DragAcceptFiles() specifies the window handle of the window that will receive dropped files. Once you've called DragAcceptFiles(), two things happen. First, the mouse cursor will automatically change to the drag cursor when you drag files over the window. Second, the window will receive a WM_DROPFILES message when you drop the dragged files on the window. Windows takes care of these details for you automatically, so you don't have to do anything more than register the window and catch the WM_DROPFILES message. The second parameter of the DragAcceptFiles() function is a Boolean value that specifies whether the window will accept dropped files. To accept dropped files, pass true for this parameter. To stop accepting dropped files, call DragAcceptFiles() again with this parameter set to false. Your program may or may not accept dropped files depending on different states, so it's important to be able to turn off drag-and-drop capabilities. You can call DragAcceptFiles() at almost any time, but your form's OnCreate event handler is a good place to initially register your application to accept dropped files. The window that will accept dropped files can be either a form or a specific component on a form. For example, you may want only a Memo component to accept dropped files, rather than the entire form. In this case, you can pass the Handle property of the Memo component to DragAcceptFiles(). However, doing so won't help you much unless you create a component derived from TMemo, which processes the WM_DROPFILES message. Creating a component is necessary because the window handle passed to DragAcceptFiles() is the window handle that will receive the WM_DROPFILES message when you drop files. If you make the form the recipient of dropped files, then your life will be a little easier. The lesson here is to add drag-and-drop support for individual components only when necessary, since doing so involves extra work. Catch the wave
The class declaration for a form that handles the WM_DROPFILES message would look like this: class TForm1 : public TForm { __published: // IDE-managed Components private: // User declarations void __fastcall WmDropFiles( TWMDropFiles& Message); public: // User declarations __fastcall TForm1(TComponent* Owner); BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WmDropFiles) END_MESSAGE_MAP(TForm) }; Notice the declaration for the WmDropFiles() function in the private section and the message map in the public section. The WmDropFiles() function is our message handler for the WM_DROPFILES message, and the message map table tells the application to call the message handler when that particular message is received. Getting the drop on the
competition
void __fastcall TForm1::WmDropFiles(TWMDropFiles& Message) { char buff[MAX_PATH]; HDROP hDrop = (HDROP)Message.Drop; int numFiles = DragQueryFile(hDrop, -1, NULL, NULL); for (int i=0;i<numFiles;i++) { DragQueryFile(hDrop, i, buff, sizeof(buff)); // process the file in 'buff' } DragFinish(hDrop); } Let's examine this code one step at a time. You begin by declaring a character array that will hold the filenames you retrieve from Windows. The character array is declared using size MAX_PATH (260) because no filename will be longer than that. Next comes a call to DragQueryFile(), which gets the number of files dropped. (For more information on this function, see the sidebar "The DragQueryFile() Function.") Now, a for loop retrieves each filename. Notice that the DragQueryFile() call inside the for loop passes the value of i in the iFile parameter. After the call to DragQueryFile(), the variable buff contains the name of the file corresponding to that index. The first time through the loop, buff will contain the name of the first file dropped; the second time, it will contain the name of the second file dropped; and so on. At this point, you'd do something with each filename as it's retrieved from Windows. Finally, notice that at the end of the function you call the DragFinish() function, passing the hDrop handle. DragFinish() frees the memory that Windows allocated for the dropped-files information. Without this call, your application will leak a little memory each time you drop files. Final thoughts
Listings A and B contain a program that implements drag-and-drop in a C++Builder program. (You can download our sample project files from www.zdjournals.com/cpb.) The program, which consists of nothing more than the main form and a Memo component, lets you drag and drop a text file onto the application. When you drop a single file, the Memo component displays the contents of the file. If you drop several files on the application, the Memo component lists the files dropped. To create this program, place a Memo component on a form. Now, enter the code from Listings A and B into the source unit and header as required. (We've marked the code you need to enter in color.) Compile and run the program. Then, experiment with dragging and dropping several files at a time and also with individual text files (source code files will work too, of course). Listing A: DDMAIN.H
Listing B: DDMAIN.CPP
|