Board index » cppbuilder » Getting Hints to display during OLE Drag-and-Drop

Getting Hints to display during OLE Drag-and-Drop

Greetings-

I would like to have a Hint (a.k.a. ToolTip) window display during an OLE
Drag-and-Drop operation (the Hint would have the name of the wntity on which the
object would be dropped, and should change as the mouse is dragged over
different parts of the drop target.)  I have tried to use the standard VCL Hint
mechanisms, to no avail.  The reason seems to be that, for Hints to work, the
mouse must have been captured by the control over which the mouse is flying.
OLE Drag-and-Drop appears to break the mouse capture while it is in effect (this
is not explicitly documented by the OLE DND documentation...no surprise
there...but actually sort of makes sense when you consider that the cursor shape
is under direct control of the OLE subsystem).

The way I see it, there are two ways to fix this:  either create a TWinControl
and floating it around as the mouse moves, or explicitly passing on the mouse
movements to what ever event handler of the framework would otherwise receive
the mouse movement messages.  It seems easier to do the latter (from within
IDropTarget::DragEnter and IDropTarget::DragOver, simply pass the mouse
coordinates to the event handler).  But I don't know what event handler to
call.  Does anybody out there know which handler would be the right one?

Thanks in advance...

jb4

 

Re:Getting Hints to display during OLE Drag-and-Drop


Hi Jay,

Quote
> The way I see it, there are two ways to fix this:  either create a TWinControl
> and floating it around as the mouse moves, or explicitly passing on the mouse
> movements to what ever event handler of the framework would otherwise receive
> the mouse movement messages.

The THintWindow class is, in essence, a floating TWinControl.  What you need to
do is set up a method of communication between your IDropTarget descendant and
your application -- this can be done via custom messages.  In your IDropTarget
class, define these custom messages, then post them to the appropriate control
whose handle is passed into the constructor of your IDropTarget class.  For
example...

// in TDropTarget header...
#define WM_OLEDRAGOVER   WM_USER + 2
#define WM_OLEDRAGLEAVE  WM_USER + 3

class TDropTarget : public IDropTarget
{
// ...
    HWND m_FFormHandle;
    POINTL m_FCursorPoint;
    __fastcall TDropTarget(HWND HForm);
// ...

Quote
};

// in TDropTarget source...
__fastcall TDropTarget::TDropTarget(HWND HForm)
    : IDropTarget()
{
// ...
    m_FFormHandle = HForm;
// ...

Quote
}

STDMETHODIMP TDropTarget::DragOver(DWORD grfKeyState, POINTL pt,
    LPDWORD pdwEffect)
{
    m_FCursorPoint = pt;  
    PostMessage(m_FFormHandle, WM_OLEDRAGOVER, 0,
                reinterpret_cast<LPARAM>(&m_FCursorPoint));

    *pdwEffect = DROPEFFECT_COPY;                
    return NOERROR;

Quote
}

STDMETHODIMP TDropTarget::DragLeave()
{
    PostMessage(m_FFormHandle, WM_OLEDRAGLEAVE, 0, 0);
    m_FAcceptFormat = false;
    return NOERROR;

Quote
}

Then in your Form's implementation, create and manage the THintWindow instance,
by providing handlers for the WM_OLE* messages...

// in header...
#include "DropTarget.h"

    LPDROPTARGET lpDropTarget;
    THintWindow *HintWindow;

    MESSAGE void __fastcall WMOleDragOver(TMessage &Msg);
    MESSAGE void __fastcall WMOleDragLeave(TMessage &Msg);        

BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(WM_OLEDRAGOVER, TMessage, WMOleDragOver)
    MESSAGE_HANDLER(WM_OLEDRAGLEAVE, TMessage, WMOleDragLeave)    
END_MESSAGE_MAP(TForm)

// in source...
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    // initialize the OLE library
    OleInitialize(NULL);

    // pass the handle to the Form in the constructor so
    // TDropTarget knows who to send the WM_OLE* message to
    lpDropTarget =
        reinterpret_cast<LPDROPTARGET>(new TDropTarget(Handle));
    CoLockObjectExternal(lpDropTarget, true, true);

    // register the Form as a drop target
    RegisterDragDrop(Handle, lpDropTarget);

    // create the hint window
    HintWindow = new THintWindow(this);
    HintWindow->Brush->Color = clInfoBk;

Quote
}

__fastcall TForm1::~TForm1()
{
    // remove the Form from the list of drop targets
    RevokeDragDrop(Handle);
    lpDropTarget->Release();
    CoLockObjectExternal(lpDropTarget, false, true);

    // uninitialize the OLE library
    OleUninitialize();

Quote
}

void __fastcall TForm1::WMOleDragOver(TMessage &Msg)
{
    // extract the drag point
    POINTL *ppt = reinterpret_cast<POINTL *>(Msg.LParam);
    POINT P = ScreenToClient(Point(ppt->x, ppt->y));

    int offsetX = GetSystemMetrics(SM_CXCURSOR);
    int offsetY = GetSystemMetrics(SM_CYCURSOR);    

    // calculate the hint window position
    AnsiString text(IntToStr(P.x) + ", " + IntToStr(P.y));
    RECT R = HintWindow->CalcHintRect(100, text, NULL);
    OffsetRect(&R, ppt->x + offsetX, ppt->y + offsetY);

    // display the hint window
    HintWindow->ActivateHint(R, text);

Quote
}

void __fastcall TForm1::WMOleDragLeave(TMessage &Msg)
{
    // hide the hint window
    HintWindow->ReleaseHandle();

Quote
}

You may also want to look into using the IDropTargetHelper interface to handle
the hint window.

Good luck!

--
Damon Chandler
http://bcbcaq.freeservers.com

Re:Getting Hints to display during OLE Drag-and-Drop


Damon-

Thanks once again.  It appears that the THintWindow is the missing piece to my
puzzle.

Here's a question:  Would it be possible for the Drop target class (TDropTarget, in
your example) to control the THintWindow object directly.  In my implementation, I
have a pointer to a TTreeView control object within the form (this is the actual
drop target control), and its parent form (actually, its grandparent form...don't
ask... ;-) ).  In your example, you convert the POINTL coordinates to client
coordinates of the form.  Since I have in my TDropTarget a pointer to its target
control, I can also convert to the POINTL to client coordinates, and do everything
within the TDropTarget.  (It would actually be beneficial to divorce as much away
from the target form as possible, as we are experiencing that goofy error that has
to do with having a form that is "too big" to compile so long as you have the form's
.CPP file active in the IDE).

What do you think?

(and thanks once again!)

jb4

Re:Getting Hints to display during OLE Drag-and-Drop


Hi Jay,

Quote
> Here's a question:  Would it be possible for the Drop target class
> (TDropTarget, in your example) to control the THintWindow object directly.
<snip>
> Since I have in my TDropTarget a pointer to its target control, I can also
> convert to the POINTL to client coordinates, and do everything within the
> TDropTarget.

Yes, that should work fine as well.  There are several ways to provide
communication between the TDropTarget class and the application -- messages are
one, pointers are another.  The reason I elected to use messages in the class is
to provide portability of the TDropTarget class to non VCL specific
implementations.  A pointer to a TTreeView will work with only for VCL TreeView
controls, but if this fits your needs, then go for it.  Just keep in mind that
you may need to use the TDropTarget class in another project later on down the
road, so you may want to consider using a generic TWinControl pointer instead.
Best of luck Jay!

--
Damon Chandler
http://bcbcaq.freeservers.com

Re:Getting Hints to display during OLE Drag-and-Drop


Damon-

Once again, thanks for all your help!

jb4

Other Threads