TAdvStringGrid

Example: Image drag & drop in TAdvStringGrid

vcl grid drag and drop


In this example, we want to illustrate both the concept of dragging images between two grids but also enable to drag an image from Windows file explorer and add it to a grid cell. First of all, the feature we'll use to add an image to a cell is using a FilePicture cell type. A FilePicture is nothing more than a reference to a picture file, i.e. the filename that is added to the cell. This makes the memory overhead of using the picture in a cell small. The cell only needs to store the filename. Whenever the cell needs to be rendered it displays the picture loaded from the file.

Note that a FilePicture cell is created with:

grid.CreateFilePicture(Column, Row, Transparent, StretchMode, Padding, HorizontalAlignment, VerticalAlignment).FileName := aFile;

So, this function will be used in the first part of the demo. To enable the grid for OLE drag & drop that is the mechanism the Windows file explorer uses for its drag operations, set grid.DragDropSettings.OleDropTarget = true. That is sufficient to have the grid.OnOleDropFile() event triggered when a file is dropped on the grid. The implementation to create a FilePicture cell type from this event becomes:
procedure TForm1.AdvStringGrid1OleDropFile(Sender: TObject; ARow, ACol: Integer;
  FileName: string; var Allow: Boolean);
var
  ext: string;
begin
  ext := Uppercase(ExtractFileExt(FileName));

  if (ext = '.JPEG') or (ext = '.JPG') or (ext = '.PNG') then
    (Sender as TAdvstringgrid).CreateFilePicture(ACol,ARow,false,StretchWithAspectRatio,0,haLeft,vaTop).Filename := FileName;
end;
Here we accept JPEG or PNG images and add these to the grid cell where the drop happens.

In the second part, let's also add the capability to perform image drag & drop between grid cells or between grids. This will be based also on FilePicture cell types. Since the drag & drop operation will also be initiated from the grid, we need to set grid.DragDropSettings.OleDropSource = true.

To initialize the grid with some pictures, following code is used:
procedure TForm1.FormCreate(Sender: TObject);
begin
  advstringgrid1.DragDropSettings.OleDropTarget := true;
  advstringgrid1.DragDropSettings.OleDropSource := true;

  advstringgrid2.DragDropSettings.OleDropTarget := true;
  advstringgrid2.DragDropSettings.OleDropSource := true;

  advstringgrid1.CreateFilePicture(1,1,true,noStretch, 0,haLeft,vaTop).Filename := '.\banana.png';
  advstringgrid1.CreateFilePicture(2,1,true,noStretch, 0,haLeft,vaTop).Filename := '.\kiwi.png';
  advstringgrid1.CreateFilePicture(3,1,true,noStretch, 0,haLeft,vaTop).Filename := '.\lemon.png';
  advstringgrid1.CreateFilePicture(4,1,true,noStretch, 0,haLeft,vaTop).Filename := '.\pear.png';
  advstringgrid1.CreateFilePicture(5,1,true,noStretch, 0,haLeft,vaTop).Filename := '.\strawberry.png';
end;
When a drag is started after selecting a cell, first the grid.OnOleDragStart event is triggered and after that grid.OnOleDragOver event as the mouse moves over the grid cells. In the OleDragStart event, we keep a reference to the source grid since we are using two grids here.
procedure TForm1.AdvStringGrid1OleDragStart(Sender: TObject; ARow,
  ACol: Integer);
begin
  dropsource := Sender as TAdvStringGrid;
end;
The grid.OnOleDrag event is triggered when the drag is about to start. Here we only allow a drag to start from a cell that has a FilePicture already as it doesn't make much sense to drag and drop an empty cell. A reference to the filename of the cell FilePicture is kept for future reference in the drop operation.
procedure TForm1.AdvStringGrid1OleDrag(Sender: TObject; ARow, ACol: Integer;
  data: string; var Allow: Boolean);
begin
  fn := '';

  Allow :=  (Sender as TAdvStringgrid).HasFilePicture(ACol,ARow);
  if Allow then
    fn := (Sender as TAdvStringGrid).GetFilePicture(ACol,ARow).Filename;
end;
In the grid.OnOleDragOver event code, a check is done whether the drag is over the cell different from the cell from where the drag originated. The Allow parameter is set to false if this is the case as it doesn't make sense that a cell FilePicture is dropped on the same cell.
procedure TForm1.AdvStringGrid1OleDragOver(Sender: TObject; ARow, ACol: Integer;
  var Allow: Boolean);
begin
  if (Sender = dropsource) and (ACol = (Sender as TAdvStringGrid).Col) and (ARow = (Sender as TAdvStringGrid).Row) then
    Allow := false;
end;
Finally, when the FilePicture drag is over a valid cell and dropped, the grid.OnOleDrop event is triggered and from this event, we check if the cell contains a FilePicture already and if so, replace the filename or create a new FilePicture.
procedure TForm1.AdvStringGrid1OleDrop(Sender: TObject; ARow, ACol: Integer;
  data: string; var Allow: Boolean);
var
  fp: TFilePicture;
begin
  if fn <> '' then
  begin
   fp := (Sender as TAdvstringgrid).GetFilePicture(ACol,ARow);
   if Assigned(fp) then
     fp.Filename := fn
   else
     (Sender as TAdvstringgrid).CreateFilePicture(ACol,ARow,false,StretchWithAspectRatio,0,haLeft,vaTop).Filename := fn;
   fn := '';
  end;
end;