Reputation: 49
Tried with no success to drag and drop a row to switch positions (using a ClientDataSet in memory)
The specific case is: a ClientDataSet with image file names who will result in an ordered list that will be used to create export to a PDF document where each image is a page (this is why the order is important). The DbCtrlGrid is used to visualize a thumbnail of the image, and I was trying to use drag-and-drop to exchange their positions, but I couldn't get information about the row where I dropped in the end.
It would help a method to get info about the row where the mouse is over when the OnDragDrop event triggers or any other idea
please
Upvotes: 0
Views: 1242
Reputation: 30715
I imagine your q is prompted by the fact that although the TDBCtrlGrid has a
PanelIndex
property which tells you which one of the grid's virtual panels
is active (i.e. is the one for the current row in the dataset), this doesn't
change while you've moving the mouse around e.g. during a drag operation. However,
it is not difficult to calculate this yourself, as follows.
The Height
and Width
of a TDBCtrlGrid are exact multiples of its RowCount
and
ColCount
. In the simple case of ColCount =1, it is trivially simple
to calculate which Row contains a given Y coordinate within the grid:
function TForm1.PanelIndexFromYPos(Y : Integer) : Integer;
var
PanelHeight : Integer;
begin
PanelHeight := DBCtrlGrid1.ClientHeight div DBCtrlGrid1.RowCount;
Result := Y div PanelHeight;
end;
(obviously this is for the simple case of a single column goVertical
orientated grid but would be easy to generalise)
Now, the TBDCtrlGrid's EndDrag
(and MouseOver
) tells you the Y coordinate of the TPoint where the
drag operation ends, so you can use this PanelIndexFromYPos
function to tell you
which row index the user has dropped the dragged row onto. As @KenWhite explained,
you then need to re-order your CDS to reflect the new position the dragged row should be in.
This is easy to do if your CDS has a DisplayIndex field representing what row position
a given record in the CDS and the CDS has an active index on this field. Re-ordering
the CDS's records is a bit of a rigmarole, as will be apparent from the following sample project.
TForm1 = class(TForm)
CDS1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBCtrlGrid1: TDBCtrlGrid; // Note: DragMode set to dmManual;
DBText1: TDBText; // In the DBCtrlGrid
DBText2: TDBText;
DBText3: TDBText;
edSourceIndex: TEdit;
edDestIndex: TEdit;
btnTest: TButton;
Memo1: TMemo;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure DBCtrlGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure DBCtrlGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
procedure DBCtrlGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);
procedure btnTestClick(Sender: TObject);
private
procedure MoveRow(SourceIndex, DestIndex : Integer);
procedure LogMove(OldValue, NewValue: Integer);
procedure ShowPanelInfo(Y: Integer);
protected
function PanelIndexFromYPos(Y : Integer) : Integer;
public
SourceIndex : Integer; // the DbCtrlGrid PanelIndex of the row being dragged
DestIndex : Integer; // the PanelIndex where the row is dropped
end;
[...]
function TForm1.PanelIndexFromYPos(Y : Integer) : Integer;
var
PanelHeight : Integer;
begin
PanelHeight := DBCtrlGrid1.ClientHeight div DBCtrlGrid1.RowCount;
Result := Y div PanelHeight;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
AField : TField;
begin
// Create the fields for the CDS
AField := TIntegerField.Create(Self);
AField.FieldName := 'ID';
AField.DataSet := CDS1;
// This DisplayIndex field will be used to determine which row number in
// the DBCtrlGrid will occupy, by indexing the CDS on this field
AField := TIntegerField.Create(Self);
AField.FieldName := 'DisplayIndex';
AField.DataSet := CDS1;
AField := TStringField.Create(Self);
AField.FieldName := 'Name';
AField.Size := 20;
AField.DataSet := CDS1;
CDS1.CreateDataSet;
// Add some data which will appear in the grid in reverse-alphabetical order
CDS1.InsertRecord([1, 3, 'A']);
CDS1.InsertRecord([2, 2, 'B']);
CDS1.InsertRecord([3, 1, 'C']);
CDS1.InsertRecord([4, 0, 'D']);
CDS1.IndexFieldNames := 'DisplayIndex';
end;
procedure TForm1.DBCtrlGrid1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then begin
SourceIndex := PanelIndexFromYPos(Y);
DBCtrlGrid1.BeginDrag(False);
end;
end;
procedure TForm1.DBCtrlGrid1DragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := True;
end;
procedure TForm1.DBCtrlGrid1DragDrop(Sender, Source: TObject; X,
Y: Integer);
begin
ShowPanelInfo(Y);
DestIndex := PanelIndexFromYPos(Y);
MoveRow(SourceIndex, DestIndex);
end;
procedure TForm1.MoveRow(SourceIndex, DestIndex : Integer);
var
BM : TBookMark;
Index : Integer;
procedure SetCDSIndex(Value : Integer);
var
OldValue : Integer;
begin
OldValue := CDS1.FieldByName('DisplayIndex').AsInteger;
CDS1.Edit;
CDS1.FieldByName('DisplayIndex').AsInteger := Value;
CDS1.Post;
LogMove(OldValue, Value);
end;
begin
if SourceIndex = DestIndex then exit;
CDS1.DisableControls;
try
if CDS1.FindKey([SourceIndex]) then begin
BM := CDS1.GetBookmark; // This is to keep track of the dragged row without needing to
// keep track of its (changing) DisplayIndex
if SourceIndex > DestIndex then begin
// i.e. we're moving the dragged row up in the grid
// so starting with the row above it we move the rows upwards
// eventually leaving a gap to drop the dragged row into
Index := SourceIndex - 1;
while Index >= DestIndex do begin
if CDS1.FindKey([Index]) then begin
SetCDSIndex(Index + 1);
end;
Dec(Index);
end;
end
else begin
// i.e. we're moving the dragged row down in the grid
// so starting with the row below it we move the rows upwards
// eventually leaving a gap to drop the dragged row into
Index := SourceIndex + 1;
while Index <= DestIndex do begin
if CDS1.FindKey([Index]) then begin
SetCDSIndex(Index - 1);
end;
Inc(Index);
end;
end;
end;
CDS1.GotoBookMark(BM);
if CDS1.FieldByName('DisplayIndex').AsInteger = SourceIndex then begin
SetCDSIndex(DestIndex);
end;
CDS1.FreeBookmark(BM); // should really have it's own try...finally but hey!
finally
CDS1.EnableControls;
end;
end;
procedure TForm1.LogMove(OldValue, NewValue : Integer);
begin
Memo1.Lines.Add(Format('Name: %s Old: %d New: %d ', [CDS1.FieldByName('Name').AsString, OldValue, NewValue]));
end;
procedure TForm1.ShowPanelInfo(Y : Integer);
begin
Label1.Caption := Format('y: %d panelindex: %d', [Y, PanelIndexFromYPos(Y)]);
end;
procedure TForm1.btnTestClick(Sender: TObject);
begin
// For debugging, to test mving rows without needing to drag/drop
MoveRow(StrToInt(edSourceIndex.Text), StrToInt(edDestIndex.Text));
end;
end.
Upvotes: 1