How to set active cell in TDBGrid?
Asked Answered
S

2

5

I want to activate a cell in a TDBGrid by code. By "activate" I mean like the user clicked inside the cell, ready to edit the cell content. How could I do this?

Edit: This probably involves two steps: change the currently active cell, then enter edit mode.

Shortterm answered 2/11, 2011 at 10:22 Comment(0)
E
4

If you mean ‘activate the edit mode for the currently active cell’, then you should probably do like this:

MyDBGrid.EditorMode := True;

Activating a particular cell can be done either via SelectedIndex:

MyDBGrid.SelectedIndex := 2;  { or maybe MyDBGrid.SelectedIndex + 1 }

or via SelectedField:

MyDBGrid.SelectedField := MyDataSet.FieldByName('Name');

To determine which cell is under the mouse cursor at the moment, you can use MouseCoord, which returns a TGridCoord record holding the coordinates of the cell under the cursor. The TGridCoord.X field can be used directly to set the grid's active column.

var
  Cell: TGridCoord;

...

Cell := MyDBGrid.MouseCoord(X, Y);
MyDBGrid.SelectedIndex := Cell.X;

Setting the row is trickier, and so far the only way I could find involves the so called protected hack, the method of accessing protected properties and methods of a class. And it's the TDBGrid class that we need to ‘hack’.

Basically, you declare an empty descendant of TDBGrid, like this:

type
  THackDBGrid = class(TDBGrid);

Then, when you need to access a protected property or method, you simply cast the instance of a standard class (MyDBGrid in this case) to the ‘hacked’ type (THackDBGrid):

… THackDBGrid(MyDBGrid).protected_property_or_method

The item we are interested in is the Row property. It returns the Y coordinate of the active row. We need to know it to determine the difference between the active row and the one under the cursor, so we could then move the underlying dataset's record pointer accordingly. Here's how:

MyDataSet.MoveBy(Cell.Y - THackDBGrid(MyDBGrid).Row);

The Row value is not absolute: it is relative to the visible top row, but so is TGridCoord.Y, so the difference between the two corresponds to the difference between the data rows in the underlying dataset.

One thing that I'd like to stress: this protected hack method should be used discreetly. Protected items are protected for a reason. So, if you can avoid it, please do so. And if you can't (there's no other way or it helps you to do things much more easily), please remember to refrain from changing anything directly using protected hack. I mean, it might be all right, but generally you never know for sure. You can see that I only used the method to read the protected contents, I didn't change anything directly. The object's state was eventually changed, but that was the result of a standard mechanism triggered by the MoveBy method.

You can read more about protected hack here.

Elainaelaine answered 2/11, 2011 at 11:5 Comment(4)
I need to change the currently active cell first - that's where I am stuck. (Specifically I need the cell under the mouse cursor.) I edited the question to be more specific about this.Shortterm
@HeinrichUlbricht: Updated with a way of finding the cell under the mouse cursor.Elainaelaine
Getting closer :) Y is still being ignored. I need to be able to freely specify the selected cell - this includes row and column (or X and Y).Shortterm
@HeinrichUlbricht: Yeah, saw that coming. Setting the row would be much trickier. Right, back to the drawing board then.Elainaelaine
S
1

My implementation based on Andriy's excellent detective work:

type
  TDBGridAccess = class(TDBGrid);

// Set the currently active grid cell to (DestCol, DestRow). Both values are
// relative to the currently _visible_ upper left grid cell.
procedure SelectDBGridCell(Grid: TDBGrid; DestCol, DestRow: Integer);
var
  CurrentRow: Integer;
begin
  if not Assigned(Grid.DataSource) or not Assigned(Grid.DataSource.DataSet) then
    Exit;

  CurrentRow := TDBGridAccess(Grid).Row;
  Grid.DataSource.DataSet.MoveBy(DestRow-CurrentRow);
  // check if the leftmost grid column is the indicator column which has no
  // equivalent field in the dataset
  if dgIndicator in Grid.Options then
    Grid.SelectedIndex := DestCol-1 else
    Grid.SelectedIndex := DestCol;
end;

procedure TDBGridController.HandleDBGridMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  CellAtMousePos: TGridCoord;
  CurrentRow: Integer;
  DBGrid: TDBGrid;
begin
  DBGrid := Sender as TDBGrid;
  CellAtMousePos := DBGrid.MouseCoord(X, Y);
  if (CellAtMousePos.X<>-1) and (CellAtMousePos.Y<>-1)  then
    SelectDBGridCell(DBGrid, CellAtMousePos.X, CellAtMousePos.Y);
end;

(The grid selection follows the mouse cursor. But SelectDBGridCell could also be used to select a cell based on other criteria.)

Works like a charm from a technical standpoint. Usability is another question.

Shortterm answered 17/11, 2011 at 9:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.