Navigation

How to handle drag and drop in a TWebBrowser control (part 2 of 4)

Overview of Solution

In article #24 we investigated how to handle OLE drag and drop by implementing the
IDropTarget interface. TWebBrowser uses this method of
accepting dragged objects. This means that we need to implement
IDropTarget and find a way to get the browser control to use our
implementation instead of its own.

So how do we supply our own drag drop handler to TWebBrowser?
The answer will come as no surprise if you have read some of my earlier
TWebBrowser articles. We must implement the
IDocHostUIHandler interface and use its GetDropTarget
method to hook up our custom IDropTarget implementation with
TWebBrowser.

Once we have implemented IDocHostUIHandler we must tell the
browser control about it. Whenever TWebBrowser is instantiated it
tries to find a IDocHostUIHandler implementation in its host (or
container). This is done by querying the host's IOleClientSite
interface. This also means we need to implement IOleClientSite in
the same object as IDocHostUIHandler.

So, to summarise we will:

Create a container for the web browser control that implements
IDocHostUIHandler and IOleClientSite and associate
this container with the web browser control.

Create an implementation of IDropTarget that handles drag and
drop as we would like.

Use the IDocHostUIHandler.GetDropTarget method to notify the
browser control about our IDropTarget implementation.

Generic Implementation of Solution

Creating the Web Browser Container

Where's the Source?

Source for TNulWBContainer and IDocHostUIHandler
is not listed in this article. However the code is provided in this
article's demo code, in the UNulContainer and
IntfDocHostUIHandler units.

Luckily, in "How to customise the TWebBrowser user interface" we developed a reusable, generic browser container class –
TNulWBContainer – that provided minimal implementations of IDocHostUIHandler and IOleClientSite. A do-nothing implementation of IDocHostUIHandler
was provided that leaves the browser control's behaviour unchanged. This
base class also takes care of associating our object with the browser
control as its container. We will reuse that code here and define a sub
class named TWBDragDropContainer that overrides the default
implementation of IDocHostUIHandler.GetDropTarget to ensure the
browser control hooks into our IDropTarget implementation.

We will make the new class as general as possible by providing a
DropTarget property that can be set to any object that implements
IDropTarget. This way the class' user can change the drag drop
behaviour of the browser control simply by assigning a different object to
the DropTarget property.

Listing 2 shows the whole of the
UWBDragDropContainer unit that contains our implementation of
TWBDragDropContainer.

Our new class includes a reimplementation of the inherited
GetDropTarget method and defines the new DropTarget
property.

GetDropTarget checks to see if a drop target handler is assigned
to the DropTarget property. If so we return a reference to our
drop target object via the method's ppDropTarget parameter and
return S_OK. Conversely if DropTarget is unassigned
then we just call the inherited GetDropTarget method to get the
default behaviour. Doing all the hard work in the base class has made this
class very easy to implement.

Implementing IDropTarget

Article #24, "How to
receive data dragged from other applications", discussed how to
implement IDropTarget. Implementation depends on what type of
data the application will accept, so we won't discuss that further in this
section. We will come back to look a couple of specific examples later.

Some Boilerplate Code for the Main Form

To use our web browser container object we need to create it in the main
form and set its DropTarget property.
Listing 3 shows the code that needs to be
added to the main form.

In the form's OnCreate event handler We create an instance of
TWBDragDropContainer and assign its DropTarget
property with our drop target object (see below for examples of such an
object). Next we navigate to about:blank just to make sure the
browser control has created a document object. The drop target object won't
be noticed by the browser until this has been done. Of course you could load
any document here – and it would be better practice to wait for the
document to finish loading before continuing. We also intialise OLE as
before.

In FormDestroy we simply clear the container object's
DropTarget property before freeing the container itself. It is
probably not necessary to set DropTarget to
nil, but it is neater, and may be safer, to
explicitly release it. Again we unintialise OLE.

That takes care of the implementation of IDocHostUIHandler and
how to notify the browser control of it's implementation. We can now move on to look at exactly how we implement the drop target. This is done by
means of a couple of case studies.