Board index » cppbuilder » DragStart fires OnClick and OnMouseUp !!?

DragStart fires OnClick and OnMouseUp !!?


2006-06-25 08:27:31 AM
cppbuilder10
Hello,
I don't understand it ! I'm trying to dragdrop a label (not dock). The
label has an OnClick handler which I don't want fired, but for some reason
the OnClick and OnMouseUp events are fired as soon as I call DragStart, so
there is no way to assign OnClick temporarily to NULL. Automatic DragMode
doesn't work either because it suppresses all mouse events and I need to
catch OnClick if the DragThreshold wasn't reached.
Why would the OnClick and OnMouseUp event occur when I haven't let go of the
left mouse button ?
I'm stuck!
Can anyone help ?
Thanks
C++Builder 5, Win Xp Pro
 
 

Re:DragStart fires OnClick and OnMouseUp !!?

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
Quote

[...] but for some reason the OnClick and OnMouseUp events
are fired as soon as I call DragStart,
Are you actually manually calling the object's DragStart event?
If so, that's the problem. To manually begin a drag operation,
one should call BeginDrag instead.
Quote
so there is no way to assign OnClick temporarily to NULL.
You don't need to do so.
Quote
Automatic DragMode doesn't work either because [...]
dmManual is the way to go.
Quote
Can anyone help ?
Set the DragMode to dmManual and add an OnMouseDown event
where you call BeginDrag with the Imediate parameter set to
false and the Threshold parameter set to the desired level.
The Threshold parameter is simply defined as the number of
pixels that you want the user to move the mouse in any
direction before the drag operation begins.
~ JD
 

Re:DragStart fires OnClick and OnMouseUp !!?

Sorry, you're right. I am calling BeginDrag , Immediate false, Threshold 20
pixels. and that OnMouseDown. DragMode Manual.
As soon as I call BeginDrag, it fires OnClick and then OnMouseUp. This is
plainly before the DragThreshold has been reached, but why it fires these
two events when the RMB is still down I can't imagine.
What I need to do is ...
If the DragThreshold is reached, set my Mouse Event handlers to Null until
the DragDrop is finished.
Or some other way of completely suppressing my Mouse Event handlers only if
the DragThreshold is reached and only as long as is dragging.
If the DragThreshold is not reached then my Mouse Event handlers need to
operate normally. ie Popup on RMB and Selection on LMB.
The problem is, that I am trying to drag a selection of labels (which
represent Items) and the BeginDrag is calling the selection routines on the
mouse events, so cancelling or changing the selection.
Thanks again
"JD" < XXXX@XXXXX.COM >schrieb im Newsbeitrag
Quote

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
>
>[...] but for some reason the OnClick and OnMouseUp events
>are fired as soon as I call DragStart,

Are you actually manually calling the object's DragStart event?
If so, that's the problem. To manually begin a drag operation,
one should call BeginDrag instead.

>so there is no way to assign OnClick temporarily to NULL.

You don't need to do so.

>Automatic DragMode doesn't work either because [...]

dmManual is the way to go.

>Can anyone help ?

Set the DragMode to dmManual and add an OnMouseDown event
where you call BeginDrag with the Imediate parameter set to
false and the Threshold parameter set to the desired level.

The Threshold parameter is simply defined as the number of
pixels that you want the user to move the mouse in any
direction before the drag operation begins.

~ JD

 

{smallsort}

Re:DragStart fires OnClick and OnMouseUp !!?

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
Quote

[...] but why it fires these two events when the RMB is
still down I can't imagine.
It's impossible to say why with out seeing some code. Start a
new project and add just the minimum that reproduces the
problem and post that.
Quote
What I need to do is ...
There is another way ... move the call to BeginDrag to
OnMouseMove. For example:
TPoint ClickPoint;
//-------------------------------------------------------------
void __fastcall TForm1::PanelMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( Button == mbLeft )
{
ClickPoint = Point( X, Y );
}
}
//-------------------------------------------------------------
void __fastcall TForm1::PanelMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
int PixelThreshHold = 20;
if( (abs(ClickPoint.x - X)>PixelThreshHold) || (abs(ClickPoint.y - Y)>PixelThreshHold) )
{
ClickPoint = Point( X, Y );
TPanel *pPanel = static_cast<TPanel*>( Sender );
// add the code that you need here
pPanel->BeginDrag( true );
}
}
}
//-------------------------------------------------------------
Alternatively, you can add a check in the events to see if
your dragging something. AFAIK, there is no global Dragging
variable but that doesn't mean that you can't define one. By
calling BeginDrag(true), you'll be able to set such a variable.
Quote
[...] set my Mouse Event handlers to Null until the DragDrop
is finished. Or some other way of completely suppressing my
Mouse Event handlers only if the DragThreshold is reached
and only as long as is dragging.
There is a (bool) Dragging member for objects that descend
from TControl so you can code your events to check for a drag
operation in progress before executing code.
Another option is not use OnClick but just OnMouseDown,
OnMouseMove, and OnMouseUp. To simulate the OnClick, in
OnMouseUp, check the X,Y and only execute OnClick code
if the X,Y is still within the bounds of the Sender (default
Windows behavior).
~ JD
 

Re:DragStart fires OnClick and OnMouseUp !!?

Thanks for the advice. But I'm still stuck.
Here is some test code (attachment project C++Builder 5).
This is simply a lable sat on a panel, sat on a form. The mouse events for
the lable and panel are assigned to the form since I need the the form
pointer OnDrop.
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
m_nThreshold = 10;
doResetDragging();
}
//---------------------------------------------------------------------------
void TForm1::doResetDragging(void)
{
m_sClickPoint.x = 0;
m_sClickPoint.y = 0;
m_bDragging = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
doResetDragging();
m_sClickPoint.x = Mouse->CursorPos.x;
m_sClickPoint.y = Mouse->CursorPos.y;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
if(Shift.Contains(ssLeft) &&
(abs(Mouse->CursorPos.x - m_sClickPoint.x)>m_nThreshold ||
abs(Mouse->CursorPos.y - m_sClickPoint.y)>m_nThreshold))
{
if(dynamic_cast<TLabel *>(Sender) ||
dynamic_cast<TPanel *>(Sender))
{
m_bDragging = true;
((TControl*)Sender)->BeginDrag(false, 0);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if(!m_bDragging)
{
ShowMessage("Normal Mode");
doResetDragging();
}
else
{
ShowMessage("MouseUp !!?\n\rWhy does this happen !\n\rI'm Still holding
the RMB\n\rHow can I cancel my Drag flag ?\n\r");
}
}
Even this way, with a "manual Drag Threshhold" I don't have an event to
reset the dragging pointer because the MouseUp event is called immediatly
when I call BeginDrag.
In this example I want the "Normal Mode" to occur if the drag threshhold
wasn't reached, otherwise I want to start the drag when my threshold is
reached and cancel it when the RMB is released or a Drop occurs.
Arrrg!! I hope there is a solution. But I can't find it.
Thanks
"JD" < XXXX@XXXXX.COM >schrieb im Newsbeitrag
Quote

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
>
>[...] but why it fires these two events when the RMB is
>still down I can't imagine.

It's impossible to say why with out seeing some code. Start a
new project and add just the minimum that reproduces the
problem and post that.

>What I need to do is ...

There is another way ... move the call to BeginDrag to
OnMouseMove. For example:

TPoint ClickPoint;
//-------------------------------------------------------------
void __fastcall TForm1::PanelMouseDown(TObject *Sender, TMouseButton
Button, TShiftState Shift, int X, int Y)
{
if( Button == mbLeft )
{
ClickPoint = Point( X, Y );
}
}
//-------------------------------------------------------------
void __fastcall TForm1::PanelMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
int PixelThreshHold = 20;
if( (abs(ClickPoint.x - X)>PixelThreshHold) ||
(abs(ClickPoint.y - Y)>PixelThreshHold) )
{
ClickPoint = Point( X, Y );
TPanel *pPanel = static_cast<TPanel*>( Sender );
// add the code that you need here
pPanel->BeginDrag( true );
}
}
}
//-------------------------------------------------------------

Alternatively, you can add a check in the events to see if
your dragging something. AFAIK, there is no global Dragging
variable but that doesn't mean that you can't define one. By
calling BeginDrag(true), you'll be able to set such a variable.

>[...] set my Mouse Event handlers to Null until the DragDrop
>is finished. Or some other way of completely suppressing my
>Mouse Event handlers only if the DragThreshold is reached
>and only as long as is dragging.

There is a (bool) Dragging member for objects that descend
from TControl so you can code your events to check for a drag
operation in progress before executing code.

Another option is not use OnClick but just OnMouseDown,
OnMouseMove, and OnMouseUp. To simulate the OnClick, in
OnMouseUp, check the X,Y and only execute OnClick code
if the X,Y is still within the bounds of the Sender (default
Windows behavior).

~ JD

 

Re:DragStart fires OnClick and OnMouseUp !!?

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
Quote

Please trim your posts.
Quote
Even this way, with a "manual Drag Threshhold" I don't have
an event to reset the dragging pointer because the MouseUp
event is called immediatly when I call BeginDrag.
Rethink your apprach. The OnMouseDown event it where it all
begins so don't worry about resetting anything when finished.
Simply initialize when it starts.
Besides, if you had developed your test just a bit further,
you'd have (or should have) discovered that OnEndDrag would
permit you to implement your logic.
Have a look at the sample below. It works as you desire and I
added another class that produces a nice looking DragObject.
To add the class to your application, Click File | New | Unit
and save it as DragObject.cpp and then copy and paste and
include the header in the main form's (or any other form's)
unit.
TPoint ClickPoint;
bool FDragging;
TDCObject *FDCObject;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// assign these events using the Object Inspector
OnDragOver = ControlDragOver;
OnDragDrop = ControlDragDrop;
Panel1->OnMouseDown = ControlMouseDown;
Panel1->OnMouseMove = ControlMouseMove;
Panel1->OnMouseUp = ControlMouseUp;
Panel1->OnStartDrag = ControlStartDrag;
Panel1->OnDragOver = ControlDragOver;
Panel1->OnDragDrop = ControlDragDrop;
Panel1->OnEndDrag = ControlEndDrag;
Label1->OnMouseDown = ControlMouseDown;
Label1->OnMouseMove = ControlMouseMove;
Label1->OnMouseUp = ControlMouseUp;
Label1->OnStartDrag = ControlStartDrag;
Label1->OnDragOver = ControlDragOver;
Label1->OnDragDrop = ControlDragDrop;
Label1->OnEndDrag = ControlEndDrag;
// this is needed because by default few controls have csDisplayDragImage set
SetControlsControlStyle( this );
}
//-------------------------------------------------------------
void __fastcall TForm1::SetControlsControlStyle( TControl *AControl )
{
AControl->ControlStyle = AControl->ControlStyle << csDisplayDragImage;
TWinControl *wControl = dynamic_cast<TWinControl*>( AControl );
if( wControl )
{
for( int x = 0; x < wControl->ControlCount; ++x )
{
SetControlsControlStyle( wControl->Controls[x] );
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
ClickPoint = Point( X, Y );
FDragging = false;
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
int ThreshHold = 3;
if( (abs(ClickPoint.x - X)>ThreshHold) || (abs(ClickPoint.y - Y)>ThreshHold) )
{
ClickPoint = Point( X, Y );
FDragging = true;
TControl *pControl = static_cast<TControl*>( Sender );
pControl->BeginDrag( true );
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( ! FDragging )
{
ShowMessage("Execute OnClick code here");
}
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlStartDrag(TObject *Sender, TDragObject *&DragObject)
{
FDCObject = new TDCObject( static_cast<TControl*>(Sender), ClickPoint.x, ClickPoint.y );
DragObject = FDCObject;
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlEndDrag(TObject *Sender, TObject *Target, int X, int Y)
{
delete FDCObject;
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
// set Accept to false if the object is in a bad place else do nothing
}
//-------------------------------------------------------------
void __fastcall TForm1::ControlDragDrop(TObject *Sender, TObject *Source, int X, int Y)
{
// Determine if the control was dropped in a good place and make your move
}
//-------------------------------------------------------------
//-------------------------------------------------------------
#ifndef DragObjectH
#define DragObjectH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
//-------------------------------------------------------------
class TCrackControl : public TControl
{
public:
__property Color;
};
//-------------------------------------------------------------
class TDCObject : public TDragControlObject
{
protected:
virtual TDragImageList* __fastcall GetDragImages();
private:
int FX, FY;
TDragImageList* FDragImages;
public:
__fastcall TDCObject( TControl *AControl, int X, int Y );
__fastcall ~TDCObject();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#pragma hdrstop
#include "DragObject.h"
//-------------------------------------------------------------
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TDCObject::TDCObject( TControl *AControl, int X, int Y ) : TDragControlObject( Control )
{
FX = X;
FY = Y;
Control = AControl;
FDragImages = NULL;
}
//-------------------------------------------------------------
__fastcall TDCObject::~TDCObject()
{
delete FDragImages;
}
//-------------------------------------------------------------
TDragImageList* __fastcall TDCObject::GetDragImages()
{
if( !FDragImages )
{
FDragImages = new TDragImageList( NULL );
Graphics::TBitmap* Bitmap = new Graphics::TBitmap;
Bitmap->Width = Control->Width;
Bitmap->Height = Control->Height;
TWinControl* wControl = dynamic_cast<TWinControl*>( Control );
if( wControl )
{
Bitmap->Canvas->Lock();
wControl->PaintTo( Bitmap->Canvas->Handle, 0, 0 );
Bitmap->Canvas->Unlock();
}
else
{
TControlCanvas *pCanvas = new TControlCanvas();
pCanvas->Control = Control;
TRect R = Rect( 0, 0, Control->Width, Control->Height );
Bitmap->Canvas->CopyRect( R, pCanvas, R );
}
FDragImages->Width = Bitmap->Width;
FDragImages->Height = Bitmap->Height;
FDragImages->SetDragImage( FDragImages->AddMasked(Bitmap, (static_cast<TCrackControl*>(Control))->Color), FX, FY );
delete Bitmap;
}
return FDragImages;
}
//-------------------------------------------------------------
~ JD
 

Re:DragStart fires OnClick and OnMouseUp !!?

I have just looked at my code for moving things around.
I do not have a problem, since my labels and edit boxes (which
are set to dmManual, and allow OnClick etc.) are
inside a panel (dmAutomatic and does not need OnClick) and I
move the whole panel. So
you could have a small panel for each label. You will
probably reject this idea as too messy, especially
if you have a lot of labels.
George
PS I think there is a bug somewhere in the system as
MouseUp seems to fire before you lift your finger.
"Paul Ashton" < XXXX@XXXXX.COM >wrote:
Quote
Thanks for the advice. But I'm still stuck.
Here is some test code (attachment project C++Builder 5).

 

Re:DragStart fires OnClick and OnMouseUp !!?

That did it. Thanks very much for your help.
Paul.
 

Re:DragStart fires OnClick and OnMouseUp !!?

"Paul Ashton" < XXXX@XXXXX.COM >wrote:
Quote

That did it.
There's a leak in the TDragObject! You need to delete the
TControlCanvas.
Quote
Thanks very much for your help.
NP.
~ JD