Board index » cppbuilder » Drag and drop cursor trails (ugly)

Drag and drop cursor trails (ugly)


2007-11-25 02:18:05 PM
cppbuilder53
Hi,
I've just written a fairly complex form with many TFrame's and TListView's
using drag and drop to create relationships and associations between various
elements of my data. When I drag items from a TListView, occassionally the
mouse cursor will create trail of garbage behind it as if the window is not
invalidating/repainting itself behind the cursor as it moves. A screenshot
can be seen here: www.grassvalleysoftware.com/images/DragCursorTrails.jpg
I believe I am using the simplest possible method of supporting drag and
drop in my app: the target control simply has OnDragOver and OnDragDrop
event handlers. The source controls have their DragMode property set to
dmAutomatic. I don't do anything fancy in the OnDragOver events other than
to verify the source control in order to determine whether or not I should
accept the drop.
For this particular application, I am using BCB5 Pro. on XP.
I've done very little work with drag and drop. Can anyone suggest what
might be happening? It doesn't happen often, but it does happen, and I have
no idea why. Is there a known problem with drag and drop in BCB5?
Thanks,
- Dennis
 
 

Re:Drag and drop cursor trails (ugly)

Hi Dennis
Dennis Jones says:
Quote
Hi,

For this particular application, I am using BCB5 Pro. on XP.

I've done very little work with drag and drop. Can anyone suggest what
might be happening? It doesn't happen often, but it does happen, and I have
no idea why. Is there a known problem with drag and drop in BCB5?
Could You show the code You have in the DragOver
event and if the ListView is OwnerDrawn also show
the various Draw events. There might be something
there that is a bit slow.
It does look strange though.
Kind regards
Asger
 

Re:Drag and drop cursor trails (ugly)

"Asger Joergensen" < XXXX@XXXXX.COM >wrote in message
Quote

Hi Dennis

Dennis Jones says:
>Hi,
>
>For this particular application, I am using BCB5 Pro. on XP.
>
>I've done very little work with drag and drop. Can anyone suggest what
>might be happening? It doesn't happen often, but it does happen, and I
>have
>no idea why. Is there a known problem with drag and drop in BCB5?

Could You show the code You have in the DragOver
event and if the ListView is OwnerDrawn also show
the various Draw events. There might be something
there that is a bit slow.
The listviews are not owner drawn, and there is nothing slow in the
OnDragOver events because all they do is verify the source control.
Something like this:
TListView *pSourceListView = dynamic_cast<TListView *>(Source);
Accept = ( pSourceListView &&
(pSourceListView->Parent == AvailableSpecificationsFrame) );
I'm betting it's a bug in Borland's implementation of drag and drop, but I'd
like confirmation and suggestions for how to fix/avoid it (besides porting
the code to BCB6 or 2007!).
- Dennis
 

{smallsort}

Re:Drag and drop cursor trails (ugly)

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
Quote

[...] using drag and drop [...] When I drag items from a
TListView, occassionally the mouse cursor will create trail
of garbage behind it as if the window is not invaliding /
repainting itself behind the cursor as it moves.
That sounds like a TDragObject issue.
Quote
I believe I am using the simplest possible method of
supporting drag and drop in my app: the target control
simply has OnDragOver and OnDragDrop event handlers.
You and I both know that 'simplest' or 'best' is subjective.
By default, the VCL uses the cursor to indicate a non-acceptable
drop site by changing to crNo (the circle with a line through
it).
Quote
The source controls have their DragMode property set to
dmAutomatic.
I would suggest that you use dmManual instead. In OnMouseDown,
record the click point and in OnMouseMove, if the mouse has
moved x number of pixels ... then begin the drag opperation.
For example:
TPoint ClickPoint;
//-------------------------------------------------------------
void __fastcall TForm1::SomeControlMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( Button == mbLeft )
{
ClickPoint = Point( X, Y );
}
}
//-------------------------------------------------------------
void __fastcall TForm1::SomeControlMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
int PixelThreshHold = 3;
if( (abs(ClickPoint.x - X)>PixelThreshHold) || (abs(ClickPoint.y - Y)>PixelThreshHold) )
{
TSomeControl *pControl = static_cast<TSomeControl*>( Sender );
pControl->BeginDrag( true );
}
}
}
//-------------------------------------------------------------
Quote
I don't do anything fancy in the OnDragOver events other
than to verify the source control in order to determine
whether or not I should accept the drop.
For the drag events, the passed-by-reference Accept parameter
and how it is changed determines the mouse display. It's
possible that your code is effecting a rapid change where the
mouse driver can't keep up with it. If this is true, you
should be able to see a difference if you move the mouse more
slowly.
Quote
[...] Is there a known problem with drag and drop in BCB5?
No. In fact, BCB5 is considered quite stable v/s later editions.
Without seeing your code, I can only speculate and considering
that, I would expect a TDragObject to resolve your issues.
~ JD
 

Re:Drag and drop cursor trails (ugly)

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
>The source controls have their DragMode property set to
>dmAutomatic.

I would suggest that you use dmManual instead. In OnMouseDown,
record the click point and in OnMouseMove, if the mouse has
moved x number of pixels ... then begin the drag opperation.
For example:
<code snipped>
Thanks, JD, for the code snippet. Unfortunately, it had little effect. I
still see the trails occasionally.
Quote
Without seeing your code, I can only speculate and considering
that, I would expect a TDragObject to resolve your issues.
I posted the content of one of my OnDragOver events in a reply to Asger. I
have three of them, and they are all identical except for the control they
reference.
- Dennis
 

Re:Drag and drop cursor trails (ugly)

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
Quote

Thanks, JD, for the code snippet. Unfortunately, it had
little effect.
That was only intended to prevent the drag operation from
starting as soon as the control is clicked.
Quote
I posted the content of one of my OnDragOver events in a
reply to Asger. I have three of them, and they are all
identical except for the control they reference.
I see nothing wrong with that. Sure, dynamic_cast has some
runtime overhead but I can't imagine that being the cause.
Does it still happen if you simply explicitly set Accept to
true? If not, you'll just have to have seperate OnDragOver
events.
The only other thing that I can think of might be if you're
manually changing the cursor.
Sorry.
~ JD
 

Re:Drag and drop cursor trails (ugly)

"JD" < XXXX@XXXXX.COM >wrote in message
Quote

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
>
>Thanks, JD, for the code snippet. Unfortunately, it had
>little effect.

That was only intended to prevent the drag operation from
starting as soon as the control is clicked.
Well, it *did* have an effect, just not enough to eliminate it completely.
The result was that, when it happens, less of the cursor is left in the
trail (as opposed to most of the cursor).
Quote
>I posted the content of one of my OnDragOver events in a
>reply to Asger. I have three of them, and they are all
>identical except for the control they reference.

I see nothing wrong with that. Sure, dynamic_cast has some
runtime overhead but I can't imagine that being the cause.
Does it still happen if you simply explicitly set Accept to
true? If not, you'll just have to have seperate OnDragOver
events.
Yes, it does, unfortunately.
Quote
The only other thing that I can think of might be if you're
manually changing the cursor.
Oh, well I *am* changing the cursor in the "OnStartDrag" handler. Could
that be it? Why would that be a problem? If it *is* a problem, how do you
suggest I correct it? (I want to change the cursor in certain cases).
Here's what I do in the StartDrag:
if ( lvItems->MultiSelect && (lvItems->SelCount>1) )
{
lvItems->DragCursor = crMultiDrag;
}
else
{
lvItems->DragCursor = crDrag;
}
- Dennis
 

Re:Drag and drop cursor trails (ugly)

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
Quote

>That was only intended to prevent the drag operation from
>starting as soon as the control is clicked.

Well, it *did* have an effect, just not enough to eliminate
it completely. The result was that, when it happens, less of
the cursor is left in the trail (as opposed to most of the
cursor).
Very interesting ... that switching to dmManual made any difference at all.
Quote
Oh, well I *am* changing the cursor in the "OnStartDrag"
handler. Could that be it?
Maybe. IIRC, when I first learned how to Drag and Drop I had
problems with changing the cursor after the drag process had
started (v6).
Quote
If it *is* a problem, how do you suggest I correct it? (I
want to change the cursor in certain cases).
If you revert to the dmManual sample, you can change the
DragCursor prior to the drag process begining. What happens
if you comment out that code?
~ JD
 

Re:Drag and drop cursor trails (ugly)

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
If you revert to the dmManual sample, you can change the
DragCursor prior to the drag process begining. What happens
if you comment out that code?
Okay, JD, using the dmManual code and commenting out the OnStartDrag code
(no longer explicitly changing the cursor, but it does change anyway because
DragCursor is set to crDrag in the Object Inspector), the trails still
occur. If I change the DragCursor to crDefault in the Object Inspector,
then the cursor does not change (it's always the arrow), but I still get the
trails.
Now, one other thing I noticed...when dragging a single item, the text of
the item being dragged (with a faded, transparent sort of look) is dragged
around with the cursor. That's a nice effect, but I am not doing it. So I
have two questions: how is that done (can I make that work with multiple
items, because that would be cool), and could the code that implements this
be a potential cause of the cursor trails? Is that something that happens
in StartDrag?
What is strange about this whole thing is that it happens intermittently. I
have been completely unable to nail down the steps to trigger it.
- Dennis
 

Re:Drag and drop cursor trails (ugly)

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
Quote

using the dmManual code and commenting out [...] the trails
still occur. [...] (it's always the arrow), but I still get
the trails.

[...] when dragging a single item, the text of the item
being dragged (with a faded, transparent sort of look) is
dragged around with the cursor. That's a nice effect, but I
am not doing it.
The very first thing that I said in this thread was that It
sounded like a TDragObject issue and that is what you just
described.
As for why it's happening with out you explicitly doing it,
I wasn't aware that that happened. Must be something to do
with the fact that TListView is just a wrapper for a native
win32 control.
Quote
So I have two questions: how is that done (can I make that
work with multiple items, because that would be cool),
Quite easily (once you see the sample).
Quote
and could the code that implements this be a potential cause
of the cursor trails?
I think I already answered that as well - that the only time
that I've ever seen this behavior was when I made my DragImage
so large that the drag handler had trouble updating it quickly
enough.
Now, a single line of text isn't too much but it could very
well be Windows implementation.
Quote
Is that something that happens in StartDrag?
That is where you would create your TDragObject. Where it's
happening in your case .... your guess is as good as mine.
Do you still get the trail without the drag image (when
multiple items are being dragged)?
Quote
What is strange about this whole thing is that it happens
intermittently.
Makes me more convinced that it's Windows. When the VCL is off,
it's off. You just don't see intermittent behavior unless you
also have undefined behavior somewhere in the code.
This is what I'd do:
Since you would like a custom drag image anyway, the first
thing that I'd try is to create my own TDragObject and hope
that it replaces what ever is happening internally.
If that doesn't work, then I'd try to intercept the message,
swallow it and then manually call BeginDrag - again hoping
that that fixed it.
If that didn't fix it, the last resort would be to google for
how to disable the automatic drag image for a ListView (note
no T).
Code for TDragObject to follow.
~ JD
//-------------------------------------------------------------
#ifndef DCObjectH
#define DCObjectH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
//-------------------------------------------------------------
class TDCObject : public TDragControlObject
{
protected:
virtual TDragImageList* __fastcall GetDragImages();
private:
TControl *FControl;
TDragImageList* FDragImages;
public:
__fastcall TDCObject(TControl *AControl);
__fastcall ~TDCObject();
__property TControl *Control = { read = FControl };
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "DCObject.h"
//-------------------------------------------------------------
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TDCObject::TDCObject( TControl* AControl ) : TDragControlObject( AControl )
{
FControl = AControl;
FDragImages = NULL;
}
//-------------------------------------------------------------
__fastcall TDCObject::~TDCObject()
{
delete FDragImages;
}
//-------------------------------------------------------------
TDragImageList* __fastcall TDCObject::GetDragImages()
{
// This just creates a duplicate image of the control being dragged.
// In your case, you're only interested in text so size the TBitmap
// accordingly and draw the text. A sample of this is below.
// You could expand the parameter list to pass the seperate elements.
if( ! FDragImages )
{
FDragImages = new TDragImageList( NULL );
Graphics::TBitmap* Bmp = new Graphics::TBitmap;
Bmp->Width = FControl->Width;
Bmp->Height = FControl->Height;
Bmp->Canvas->Lock();
TWinControl* pControl = dynamic_cast<TWinControl*>( FControl );
pControl->PaintTo( Bmp->Canvas->Handle, 0, 0 );
Bmp->Canvas->Unlock();
FDragImages->Width = FControl->Width;
FDragImages->Height = FControl->Height;
int Index = FDragImages->AddMasked( Bmp, Application->MainForm->Color );
FDragImages->SetDragImage( Index, FControl->Width, FControl->Height );
delete Bmp;
}
return FDragImages;
}
//-------------------------------------------------------------
TDragImageList* __fastcall TDCObject::GetDragImages()
{
if( !FDragImages )
{
FDragImages = new TDragImageList( NULL );
Graphics::TBitmap* Bmp = new Graphics::TBitmap;
// can't set bitmap dimensios yet without knowing
// how big it needs to be
// Use a TControlCanvas to measure the text height
// and width
TControlCanvas *pCanvas = new TControlCanvas();
pCanvas->Control = Control;
int h,w,w1,w2;
h = pCanvas->TextHeight("Wg");
w1 = pCanvas->TextWidth( StartingRecordString );
w2 = pCanvas->TextWidth( EndingRecordString );
delete pCanvas;
// determine the widest string
if( w1>w2 ) w = w1;
else w = w2;
// adjust width and height for a nice margin
w += SomeLeftRightMargins;
if( StartingRecordString == EndingRecordString )
{
// only one record to display
h += SomeTopBottomMargin;
}
else
{
h *= 3;
h += SomeTopBottomMargin;
}
// set the bitmap
Bmp->Width = w;
Bmp->Height = h;
Bmp->Canvas->Brush->Color = FColor;
Bmp->Canvas->FillRect( TRect(0, 0, w, h) );
// i'm in a hurry now so the synatx for Textout is
// incomplete. You have to postion the text
// also might need to set the pen color
Bmp->Canvas->TextOut( the first record )
if( StartingRecordString != EndingRecordString )
{
Bmp->Canvas->TextOut( "....." )
Bmp->Canvas->TextOut( the last record )
}
FDragImages->Width = w;
FDragImages->Height = h;
int Index = FDragImages->AddMasked( Bmp, FColor );
FDragImages->SetDragImage( Index, FX, FY );
delete Bmp;
}
return FDragImages;
}
//-------------------------------------------------------------
usage (note that I co-mingled code from 2 places so the
TDragObject parameter lists don't match):
//-------------------------------------------------------------
void __fastcall TForm2::StringGrid1StartDrag(TObject *Sender, TDragObject *&DragObject)
{
TPoint Point;
::GetCursorPos( &Point );
TStringGrid *pGrid = static_cast<TStringGrid*>( Sender );
Point = pGrid->ScreenToClient( Point );
TheDragObject = new TDCObject( pGrid, Point.x, Point.y, Color );
DragObject = TheDragObject;
}
//-------------------------------------------------------------
void __fastcall TForm2::StringGrid1EndDrag(TObject *Sender, TObject *Target, int X, int Y)
{
delete TheDragObject;
TheDragObject = NULL;
}
//-------------------------------------------------------------
 

Re:Drag and drop cursor trails (ugly)

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
That is where you would create your TDragObject. Where it's
happening in your case .... your guess is as good as mine.
Do you still get the trail without the drag image (when
multiple items are being dragged)?
Yes, though a much smaller portion of it (the tip of the arrow -- like an
upside down 'v')
Quote
This is what I'd do:

Since you would like a custom drag image anyway, the first
thing that I'd try is to create my own TDragObject and hope
that it replaces what ever is happening internally.

If that doesn't work, then I'd try to intercept the message,
swallow it and then manually call BeginDrag - again hoping
that that fixed it.

If that didn't fix it, the last resort would be to google for
how to disable the automatic drag image for a ListView (note
no T).

Code for TDragObject to follow.
Thanks for all your help, JD. I will try your suggestions (and your code)
and see where that leads me.
- Dennis
 

Re:Drag and drop cursor trails (ugly)

"Dennis Jones" < XXXX@XXXXX.COM >wrote:
Quote

>Do you still get the trail without the drag image (when
>multiple items are being dragged)?

Yes, though a much smaller portion of it (the tip of the
arrow -- like an upside down 'v')
I just tested it with a TListView in one of my projects
(vsReport - dmAutomatic and all events removed) and it did
indeed create it's own drag image but not for mulitselected
items.
The thing is that I also did not see the same problem so I'm
not so sure that a custom drag image is going to help. If you
reduce the code to a small sample that illustrates problem and
zip it to the attachments group, I'll look at it and try to
find out what's up.
Quote
>Code for TDragObject to follow.
I forgot one very important thing <slaps head>.
By default, (most) VCL objects will not display a drag image
when it's dragged over it. To change that, you need to set
each TControl descendent to have it's ControlStyle to include
csDisplayDragImage. Something like:
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
for( int x = 0; x < ComponentCount; ++x )
{
TControl* pControl = dynamic_cast<TControl*>(Components[x]);
if( pControl )
{
pControl->ControlStyle = pControl->ControlStyle << csDisplayDragImage;
}
}
ControlStyle = ControlStyle << csDisplayDragImage;
}
//-------------------------------------------------------------
~ JD
 

Re:Drag and drop cursor trails (ugly)

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
By default, (most) VCL objects will not display a drag image
when it's dragged over it. To change that, you need to set
each TControl descendent to have it's ControlStyle to include
csDisplayDragImage. Something like:
That's interesting, because I don't do that and yet the drag image is being
displayed over the controls (at least the ones I care about).
- Dennis