Hi all

I'm self taught and relatively new to programming and have been battling with a TDBGrid's onMouseDown event in Delphi 7. Hopefully I'm missing something small and you masters out there will be able to enlighten me!

I have a TVirtualStringGrid and TDBGrid on my form. I want to be able to double click on a row on the DBGrid and open up a form to edit the contents of that record, but I also want to be able to drag a row from the DBGrid and drop it on a node in the TVirtualStringGrid.

Using the DragMode - dmAutomatic or dmManual - in the onDblClick or onClick events, I can either drag or double click, but not both. Using onMouseDown with DataGrid.BeginDrag(False, 5) and DragMode set to dmManual, to my understanding, I should solve my problem.

After much fighting, I tested with the following:

procedure TfrmMain.DataGridMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     if ((Button = mbLeft) and not (ssDouble in Shift)) then
     begin
          DataGrid.BeginDrag(False, 5);
          label2.caption := 'Mouse-Down - Left Button!';
     end;
     if Button = mbRight then
     begin
          label2.caption := 'Mouse-Down - Right Button!';
     end;
     if ssDouble in Shift then label2.caption := 'Mouse-Down - Double Click!';
     if ssCtrl	    in Shift then label2.caption := 'Mouse-Down - Ctrl Button!';
end;

procedure TfrmMain.DataGridMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     label2.caption := 'Mouse up!';
end;

What happens now, is that if I click on one of the cells in the grid, I get absolutely no response until I release the button, then I get the 'Mouse up!' message.

If I click on the white space to the right of the final column of data, I get the correct mouse down message, but no mouse up message!

What am I missing?

Thanks!

Rory

Ah, ye olde rodent...

Remember, a (basic) mouse only does two things: move and click (single click, that is).

So, Windows and Delphi and whatever else have to make some basic assumptions about how the user will manipulate the mouse to distinguish things like drag and double-click from normal movement and single clicks.

Unfortunately, you have told Delphi to throw all that out the window and do it your way... (alas!)

To fix it, get rid of all special MouseDown and MouseUp event handler methods.

To handle a double click, provide an event method only for OnDblClick.

To handle dragging, you need to provide for at least the first of the following three:
OnDragDrop
OnStartDrag
OnDragOver
Leave the drag mode as automatic.

In the Object Inspector, switch to the events tab, (single) click on one of those events, and press F1 for more information.

You can also give the control a popup menu, which will be properly (and automatically, so you don't need to worry about it) handled when the user right-clicks with the mouse.

If you want to do anything unusual (like respond to single left-clicks with the Shift key held down, or handle middle-clicks, etc) you can do that too, just don't do anything on a normal left-click.

Hope this helps.

Thanks, Duoas, but if I leave drag mode as automatic, the instant I double click, Delphi thinks I am dragging. I can drag and drop, but can't double click unless drag mode is manual.

Hmm, that's right. (Stupid grid controls are poorly designed... they give everyone and everything grief...)

You'll have to help it along. This is what I did. (I used a tStringGrid to test.)

Set DragMode to dmManual. (Sorry about that earlier.)
Set Options.goRangeSelect to FALSE.

procedure Form1.stringGrid1MouseMove(
  sender: tObject;
  shift:  tShiftState;
  x, y:   integer
  );
  begin
  // If we should be dragging, then start
  if ssLeft in shift then stringGrid1.beginDrag( TRUE )
  end;

I also used OnStartDrag to create a custom tDragObject (which you'll probably want to do also...). The source argument to OnDragOver and OnDragDrop is the object you create.

My custom object looked like this:

type
  TMyDragObject = class( TDragObject )
    // Tracks which cell we are dragging, so when we drop
    // it we can append its contents to the target cell's text.
  public
    sCol, sRow: integer;  // start col, row
    valid:      boolean;  // convenience
    constructor create( aCol, aRow: integer; aValid: boolean );
  end;

constructor TMyDragObject.create(
  aCol, aRow: integer;
  aValid:     boolean
  );
  begin
  inherited create;
  sCol  := aCol;
  sRow  := aRow;
  valid := aValid
  end;

And the way I used it:

procedure TForm1.StringGrid1DblClick( Sender: TObject );
  begin
  // Double-clicks just append an X to the cell's text.
  with stringgrid1 do
    cells[ col, row ] := cells[ col, row ] +'X';
  end;

procedure TForm1.StringGrid1DragDrop(
  Sender, Source: TObject;
  X,      Y:      Integer
  );
  var dCol, dRow: integer;
  begin
  // Drop the source object onto the cell under the mouse cursor.
  // Note that since I used 'OnDragOver' I know that this method
  // is only called if the 'source' object is valid.
  stringgrid1.mousetocell( x, y, dCol, dRow );
  with stringgrid1, source as TMyDragObject do
    cells[ dCol, dRow ] :=
    cells[ dCol, dRow ] +'(' +cells[ sCol, sRow ] +')'
  end;

procedure TForm1.StringGrid1StartDrag(
  Sender:         TObject;
  var DragObject: TDragObject
  );
  begin
  // A drag object is created whether or not we like it.
  // However, we can decide _here_ whether or not it
  // is a _valid_ cell we are dragging.
  // For our purposes, fixed cells are invalid.
  // (My string grid had no fixed columns.)
  with stringgrid1 do
    dragObject := TMyDragObject.create( col, row, row >= 1 )
  end;

procedure TForm1.StringGrid1DragOver(
  Sender, Source: TObject;
  X,      Y:      Integer;
  State:          TDragState;
  var     Accept: Boolean
  );
  begin
  accept := (sender = stringgrid1)
        and (source is TMyDragObject)
        and (source as TMyDragObject).valid
  end;

Hope this helps. :)

Comments
Very helpful! Thank you!
This question has already been answered. Start a new discussion instead.