Using OLE Drag and Drop To Improve User Interfaces
By Jim Booth
This session will be focused on the new OLE Drag and Drop capabilities in Visual FoxPro 6.0. Although our focus is on Drag and Drop we will spend some time discussing the user interface technique of direct manipulation in some detail in order to understand where drag and drop fits into user interface design.
The prerequisites for this session are having some familiarity with Visual FoxPro Forms and the controls we use in those forms. Also being comfortable with the designers and the property sheets will be helpful in following this session.
The session is aimed at a skill level that is between beginner and intermediate. The content will cover some issues that are of interest to all skill levels, including advanced, however the nature of the material is not beyond the understanding of someone who is just beyond pure beginner.
During this session we will introduce the concept of direct manipulation as a user interface tool. This will show us that Drag and Drop is one of the direct manipulation techniques. Drag and Drop is the technique we will dig even deeper into as a user interface tool understanding the various states of objects during a drag and drop process. We will compare the two methods of implementing drag and drop in Visual FoxPro 6.0, the native and the OLE drag and drop methods. Finally, we will focus our attention on the OLE Drag and Drop implementation in Visual FoxPro 6.0 and examine most of the basic features of that methodology.
An excellent book about user interface design is About Face The Essentials of User Interface Design by Alan Cooper. In this book Mr. Cooper describes three elements that comprise direct manipulation in a use interface.
An even simpler definition of direct manipulation, Cooper says, would be clicking-and-dragging things around.
However if you review the three items listed above only one is involved with what the user does, the other two have to do with the visual feedback that the user gets from the operation. In our later discussion of drag and drop we will present methods for controlling the visual feedback the user will get.
Lest we venture off thinking that drag and drop is the only direct manipulation method your users will see, lets just list some of the others. Below is a list of some of the direct manipulation interfaces you and your users deal with every day.
The Anatomy of Drag and Drop
The drag and drop process involves two objects, the source that is the object that originated the drag operation and the target that is the object being dropped on. The source object is the one that controls the entire drag and drop process. It is the master of the operation.
There are three states to the drag and drop operation start, capture, and end. Any well-designed drag and drop interface will provide visual feedback to the user relaying the state of the operation.
The start is the action that the user must take to initiate the drag operation. This is usually simply pressing the left mouse button, but could be set up to require the right button or the shift or control keys with the left button. This is the beginning of the drag operation.
Is the period when the source is being dragged. During this phase you are most interested in giving the user feedback indicating that they are dragging something and whether or not it is currently over a place where it can be dropped.
End of drag is the drop. Here you are concerned with giving the user clear visual feedback that the drop has occurred and what the effect of that drop has been.
The Two Types of Drag and Drop
There are two types of drag and drop operations that can be used. One is the internal or non-negotiated drag and drop. The second is external or negotiated drag and drop. These two operations differ in that with internal the source and target of the operation are in the same application and therefore there is no need for any negotiation between the source and target. With external drag and drop the source and target of the operation are not in the same application and there is a necessity for the target and source to negotiate with each other regarding on exactly what should happen and how it should happen.
In Visual FoxPro this might be the user dragging a textbox and dropping it on another textbox in a different form. The entire operation is inside of the Visual FoxPro application and is therefore an internal drag and drop. Although it is true that the drop target has to examine the source object to get the data or whatever else it might want, there is not really a negotiation like there would be if the source was from some other application like Visio or Microsoft Excel.
Visual FoxPro 6.0 can be the source or the target of an external drag and drop operation. When it is the target the Visual FoxPro object that receives the drop must examine the data being dropped to find out what type it is and whether or not it can accept that type of data. This is the negotiation referred to in the name.
If Visual FoxPro is the source of an external drag operation then it must place data in the data object of the drag and identify the type that the data is. This needs to be done so that the target can do the negotiating in order to determine how to handle the drop.
What Do We Know So Far?
We have now examined the drag and drop operation to expose and understand the various steps it involves. The phases of the operation are clearly defined and the nature of the two types, internal and external, drag operations are clear.
Before going further lets put some concerns to rest. Visual FoxPro handles most of the issues we have described automatically for us. Visual FoxPro, as it often does, also exposes to us the ability to manage these issues ourselves through code. I just wanted to eliminate any concerns that may have been developing because of the number of issues with external drag and drop.
Why Use Drag and Drop At All?
With all of the above concerns why should we consider using drag and drop in our user interfaces? There are a few very good reasons to implement drag and drop into your user interface designs.
Provide an intuitive method for the user to interact with the application objects
One of the things that very often put users off in a computer application is the strange way they have to do things. Most users are not programmers and they do not think like programmers. Tabbing to a certain control on screen and then pressing a function key, then tabbing somewhere else and pressing a different function key in order to take the text in one place and move it to the other place is non-intuitive to most users.
Clicking on the source text and then dragging the mouse to the target location and letting go of the mouse button is an intuitive method of accomplishing the same operation.
Efficient method for manipulating two objects, their data, and geography
I have often heard that a mouse slows computer users down, however I offer the challenge of coming up with a keyboard only interface that is as efficient as the Visual FoxPro Form Designer for moving things around on the screen.
Drag and drop is a very efficient method for managing two objects and their data, or for managing the geography of objects on the screen.
Allows complex operations to have a simple interface
During the presentation I will demonstrate a drag and drop interface that accomplishes a myriad of activities behind the scenes while the user only grabs an object, drags it to a location, and drops it there.
Once taught it is seldom forgotten
This may be the single most important reason for using drag and drop interfaces. I use Microsoft Word for most of my writing. Every now and then I need to do some kind of special formatting of a document. I get into the help file and look up the steps in doing that and get it done. You can be guaranteed that the next time I need to do that I will again be looking up the steps.
However, show a new user how to drag text from one place to another once and they will likely never forget how to do that.
Visual FoxPro 6.0 and Drag and Drop
Visual FoxPro has had drag and drop capabilities since version 3.0 was released. The implementation of drag and drop in 3.0 and 5.0 were native to Visual FoxPro and therefore only worked when dragging to and/or from a Visual FoxPro object. This method of drag and drop is still available in Visual FoxPro 6.0.
Native Drag and Drop
There are a number of events and methods that provide access to the native drag and drop features. The Drag method starts or stops a drag operation. The DragMode property can be set to automatically start a drag when the mouse button was depressed over the control. The DragIcon property assigns a mouse icon to be used during the dragging of the control.
The three items above are related to the drag source, there are also two events for the drag targets DragOver and DragDrop. The DragOver event fires for a control whenever there is something being dragged over that control and the DragDrop event fire when the item being dragged is dropped on the control. These two events are passed a number of parameters, DragOver gets 4 and DragDrop gets 3. The parameters are oSource, nXCoord, nYCoord, and nState. nState is the one that is not received by the DragDrop event. The oSource parameter is an object reference to the control that is being dragged. The nXCoord and nYCoord parameters are the x and y position of the mouse pointer. The nState parameter is one of 3 values 0 meaning the drag is entering the space over the control, 1 meaning the drag is leaving the space over the control, or 2 meaning the drag is occurring over the controls space.
There are some advantages to the native drag and drop features of Visual FoxPro. It is easy to implement and it works very well with the native Visual FoxPro objects. The major limitation of this drag and drop approach is that it ONLY works with native Visual FoxPro objects.
This is as deeply as we will venture into the native drag and drop features of Visual FoxPro in this session. The rest of the time we will spend investigating the new OLE Drag and Drop features of Visual FoxPro 6.0.
SPECIAL NOTE: Visual FoxPro Native Drag and Drop and OLE Drag and Drop do NOT work together. This is a one or the other proposition.
Introducing OLE Drag and Drop
Visual FoxPro 6.0 introduces us to OLE Drag and Drop. OLE drag and drop will allow us to do all of the things we have done with the native drag and drop features of past versions, but it also enables the drag and drop to originate from or terminate on a non Visual FoxPro application or control. We can now drag and drop onto an ActiveX control, or we can drag text from a Visual FoxPro textbox and drop that text in a Microsoft Word document or an Excel spreadsheet. The flow goes the other way too; we can drag text from a Microsoft Word document and drop it in a Visual FoxPro editbox.
OLE drag and drop has certain advantages over the native drag and drop in Visual FoxPro. OLE drag and drop works well with Visual FoxPro native objects just like the native drag and drop does. OLE drag and drop is relatively easy to implement, Visual FoxPro automatically handles many of the functions with little or no intervention by the developer. OLE drag and drop works with all OLE drag and drop enabled applications which allows the use of drag and drop between Visual FoxPro and other applications. OLE drag and drop has very flexible data management capabilities. There are at least seven different data types that may be involved with the OLE drag action.
One difference we should mention right away is that the object being dragged around is not the control or application that originated the drag operation, it is a data object that contains the data that was put in it by the source of the drag operation. We will see later that there is some default data that can get in that data object, but there are also some methods of that object that allow us to put whatever data in there that we choose.
New Problems to Conquer
There is no free lunch, with the added capabilities of OLE drag and drop there are some added problems we need to address. For one the thing being dropped on our Visual FoxPro control may not be coming from Visual FoxPro at all. This means that it is possible that a drop may occur on a textbox that is carrying a graph from Microsoft Excel. Obviously our textbox cant do a whole lot with an Excel graph.
The data being carried by the dragged object can be complex that is it may be an array of data or a number of data items of different types. Our drop management will need to deal with this situation.
One of our Visual FoxPro objects may be dragged and dropped in another application. Now although we have no control over how that other application will handle the drop, we may need to take certain precautions on our management of the data object in the drag operation.
Managing OLE Drag and Drop in Visual FoxPro
We will work from the simple to the sublime with this subject. This session is not intended to produce OLE Drag and Drop experts, but rather to expose us to the capabilities of OLE Drag and Drop. There will be some examples and demonstrations of the techniques. We will examine certain of the properties and methods in detail. However, there will be other properties and methods related to OLE Drag and Drop that we will not discuss. Those will be left for you to read and learn about on your own.
Starting the Drag
The screen shot shown below figure 1 shows a simple Visual FoxPro form with two textboxes.
Figure 1 A simple Visual FoxPro form.
We will refer to the text boxes in the form above as text1 and tgext2 with text1 being the top one. The simplest method for enabling OLE drag and drop between these two text boxes is to set the OLEDragMode for text1 to 1 Automatic and to set the OLEDropMode for text2 to 1 Enabled.
These settings will allow you to drag the text from Text1 and drop it on Text2. Text2 will, by default, insert the text being dragged at the point of the drop. You can also drag the highlighted text from Text1 and drop it into a Microsoft Word document and the text will be inserted in that document at the drop point as well.
Later we will look into taking more control over the drop action, but for now lets focus on the drag initiation. In the first example we started the drag by setting the OLEDragMode to automatic for Text1. Lets now see how we can start tge drag ourselves.
In the next example the OLEDragMode has been reset to its default of manual. Instead of using the automatic drag start we will use text1s MouseDown event to call the OLEDrag method to start the drag. In the MouseDown of Text1 we have put the following line of code.
LPARAMETERS nButton, nShift, nXCoord, nYCoord THIS.OLEDrag(.T.)
The OLEDrag method will start a drag on the control. This method takes two arguments a logical .T. will cause the control to delay a short time before beginning the drag, thus allowing the user to release the mouse button and cancel the drag. Passing a .F. to the OLEDrag causes the drag to begin immediately with no delay. In the second form we are still using the OLEDropMode of Enabled for Text2.
Next lets see the insertion of the dragged text at the location of the drop. We3 have created a new form named Insert.scx. This form has Text1 with the OLEDrag(.T.) in its MouseDown event. However this form has an editbox in it that has its OLEDropMode set to Enabled. The next two figures 2 and 3 show how we can drop text at a specific point in the editbox.
Figure 2 A drop insertion before the action.
The next figure shows the form after we have dragged the word text from the textbox and dropped it before the word dropping in the editbox.
Figure 3 After the drag and drop
Notice that the word test is gone from the te3xtbox, that is because we did a simple click and drag. If we wanted to copy the word we should have used a Ctrl-Click and drag.
All of the things you have seen so far are being done through some very simple settings of the OLEDragMode, OLEDropMode, and the use of the OLEDrag method.
Giving Feedback to the User
The OLEGiveFeedBack event can be used to set the drag mouse visual effect. We have entered the following line of code into the Textboxs OLEGiveFeedBack event of the form seen in figure 3.
eMouseCursor = "dragguy.ani"
Dragguy.ani is an animated mouse cursor that we created. Figure 4 shows the effect (of course you cannot see the animation in the still shot).
Figure 4 Using the OLEGiveFeedBack event to control the visual feedback to the user
Sensing a Drag Over
The OLEDragOver event will fire whenever an OLE Drag object is dragged over a control if the OLEDropMode is set to Enabled. There are a number of parameters that are passed to the OLEDragOver event.
The oDataObject parameter is an object reference to the data object that is being dragged. The nEffect parameter is kind of unique in that we can set its value in the OLEDragOver event and control the drop action allowed. The possible values for the nEffect parameter are 0 for no drop, 1 for copy, 2 for move, and 4 for link. The nButton parameter tells us which mouse buttons are down with 1 for right, 2 for left, and 3 for middle. The nShift parameter tells us the state of the Ctrl, Alt, and Shift keys with 1 for shift , 2 for Ctrl, and 4 for Alt. This property may be a combination so that Ctrl + Shift would have a value of 3. The nXCoord and nYCoord parameters tell us the x and y position of the mouse pointer. Finally the nState parameter tells us if the drag is entering the control, leaving the control or moving over the control with 0 indicating entering, 1 representing exiting, and 2 representing dragging over the control.
On the sample code for this session is a form named DragOver.scx. This form has a textbox, an image, and an edit box in it. Both the textbox and the image are OLEDragMode automatic. The editbox is OLEDropMode enabled. In the OLEDragOver event of the editbox is the code below .
LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord, nState IF NOT oDataObject.GetFormat("CF_TEXT") nEffect = 0 && No Drop ELSE nEffect = 1 && Copy ENDIF
This code first calls the GetFormat method of the data object and asks if the data type of the data is CF_TEXT (the data object will be described in more detail alter). The GetFormat method will return a .T. or .F. indicating if the stated data type is in the data object. The code above is setting the nEffect parameter to 0 (No drop) if the data type is not CF_TEXT and to 1 (Copy) if the data type is CF_TEXT.
When running this form we can type some text in to the text box then highlight and drag it over the editbox. When we are dragging the text from the textbox the editbox will indicate that a drop is allowed and will accept the text if a drop is done. If the image is dragged over the editbox then the GetFormat call will return .F. because the data is not text but is a BMP, the nEffect is set to 0 and the no drop icon is displayed and a drop is not allowed.
Since with OLE drag and drop we have no idea of what might be dragged over our control, using the OLEDragOver event this way if an effective method of limiting the drops allowed to only those with data that our control can handle.
The Data Object (oDataObject)
In certain of the events, like OLEDragDrop, one of the parameters passed is oDataObject. Unlike in the native Visual FoxPro drag and drop, where the oSource is an object reference to the control that is the source of the drag operation, OLE drag and drop has a DataObject that is being dragged around.
The DataObject cannot be created outside of a drag operation and it is destroyed as soon as the drag ends. The DataObject is passed to OLESetData and OLEStartDrag events of the drag source. The OLEStartDrag event is called when a drag operation is started and this event can be used to populate the DataObject with data. Depending on the object that is the source of the start of the drag operation the DataObject may be automatically populated with data (as we have seen in the examples so far). The OLESetData event is called for the Drag source whenever a drop target calls the GetData method of the DataObject and no data is in the object of the type requested. Either of these methods can use the SetData method of the DataObject to place data in the DataObject.
The other two places where the DataObject is passed are the OLEDragDrop and the OLEDragOver event a drop target. Either of these events receive the DataObject as a parameter named oDataObject and they can use the methods of the DataObject to investigate the data being dragged.
Methods of the DataObject
The DataObject has five methods available. They are ClearData, GetData, SetData, GetFormat, and SetFormat. These methods are described in the table below.
We can make use of these methods to allow a drag source to customize the format of the data according to our needs. Figure 5 shows a form with an image of an American flag, a text box, and a container.
Figure 5 Our custom OLE DataObject form DragChng.scx.
The idea of this form is that we can drag the image and drop it. If we drop it on the textbox we want to get text saying "American Flag", if we drop it on the container we want to see the image of the flag.
We accomplish this by customizing the contents of the DataObject when the drag starts. In the Images OLEStartDrag event there is the following code.
LPARAMETERS oDataObject, nEffect * Clear out all data from the data object oDataObject.ClearData *-- Specify that data can only be copied nEffect = 1 *-- Register the text (1) format oDataObject.SetFormat( 1 ) *-- Register private format in the dataobject oDataObject.SetFormat( "Private Format" )
This code first clears the data object of any data. It then registers the text data format and a special format. In the OLESetData event the code below appears.
LPARAMETERS oDataObject, eFormat DO CASE CASE trans( eFormat ) == '1' && Text format * If Text data is requested by the drop target * Put "American Flag" in the data object oDataObject.SetData( "American Flag", 1 ) CASE trans( eFormat ) == "Private Format" && Private format * If "Private Format" is requested * put the picture file name into the data object * as "Private Format" oDataObject.SetData( This.Picture, "Private Format" ) ENDCASE
What we have done so far is to react to the start of a drag of the image. When the drag is started we registered two data formats in the data object text and private format. The OLESetData event wont get fired until a drop target tries to get data our of the data object matching a certain format. When the drop target requests the text format the OLESetData event will put "American Flag" into the data object as text format. If the drop target requests our private format then the name of the file in the images picture property will be put in the data object as the private format format.
There is no special code in the textbox as its OLEDragDrop automatically requests Text format. The container is a little special though. In its OLEDragOver it has the following code.
IF nState == 0 && Drag Enter *-- We only need to check out the data format once upon enter DO CASE CASE oDataObject.GetFormat( "Private Format" ) * I can handle drops of "Private Format" * Tell OLEDragDrop that I know how to handle this This.OLEDropHasData = 1 * Tell OLEDragDrop that I want to copy this This.OLEDropEffects = 1 CASE oDataObject.GetFormat( 15 ) && Files * I can handle drops of files This.OLEDropHasData = 1 This.OLEDropEffects = 1 ENDCASE ENDIF
When the Container requests the data type Private Format, the OLESetData event will respond by putting the name of the picture file in the data object as type private format. In the containers OLEDragDrop event we handle the drop with the code below.
LOCAL aFiles[ 1 ], nFiles, i, cExt DO CASE CASE oDataObject.GetFormat( "Private Format" ) && Private format * I can handle "Private Format" data * Hide the label This.lblDropSpace.Visible = .F. * Set the picture property This.Picture = oDataObject.GetData( "Private Format" ) * Set the copy attribute nEffect = 1 * Draw the container THIS.Draw * Stop the default action of the drag and drop event NODEFAULT CASE oDataObject.GetFormat( 15 ) * I can handle file data * Get the file data into an array IF oDataObject.GetData( 15, @aFiles ) * If we got any set the count nFiles = alen( aFiles, 1 ) ELSE * Set the count to 0 nFiles = 0 ENDIF * Check each of the files FOR i = 1 to nFiles * Get the extension cExt = Upper( JustExt( aFiles[ i ] )) IF ( cExt == "BMP" ) or ( cExt == "JPG" ) or ( cExt == "GIF" ) * If it is a file we can handle * Hide the label This.lblDropSpace.Visible = .F. * Set the picture property This.Picture = aFiles[ i ] * Draw he container THIS.Draw * Pause for a few seconds in case there was more than one file INKEY(2,"H") ENDIF ENDFOR * Set the copy only attribute nEffect = 1 * stop the default action of this drag and drop NODEFAULT ENDCASE
The above code is riddled with comments explaining each and every line. If you read it carefully you will see that we can actually drag file names from the explorer and drop them in this container and the container will display the image.
Figure 6 shows the same form after the image has been dropped on both the textbox and the container.
Figure 5 Our custom OLE DataObject form after the drops.
As you have seen in this last example form, the OLE Drag and Drop operation is a two-way communication between the drag source and the drop target. When the drop target makes a request the drag source can respond. It is this two-way communication that makes OLE Drag and Drop so much more powerful even if it is only used within a Visual FoxPro application. The communication allows different data to be sent depending on where the drop occurred. It also gives the drop target a say in what it will or will not handle.
All of this and the ability to drag from and drop on non Visual FoxPro objects, well OLE drag and drop certainly has me hooked. Whenever I need Drag and Drop interface components I will be using OLE Drag and Drop.