Well done!
I just tested this and it works pretty well.
One thing that needs improvement:
If you want to use shift-click twice:
Example:1. You select row 10.
2. You use shift-click to select row 20.
Rows 10-20 are now selected
3. You use shift-click to select row 30.
Expected behaviour: It should select rows 10-30.
Actual behaviour: It selects rows 20-30
I made some minor tweaks to the code, but essentially it's the same as you wrote:
//Declare a global variable
//Used for special function on TDBGrids: the ability to use Shift to select multiple rows
//(See DBGridShiftSelect)
var
DBGridPriorBookmark : TBookmark;
//Don't forget to clear the above bookmark when you clear or re-populate the grid
//Example, place this in Dataset.AfterRefresh:
//begin
// if assigned(DBGridPriorBookmark) then
// begin
// DataSet.FreeBookmark(DBGridPriorBookmark);
// DBGridPriorBookmark := nil;
// end;
//end;
//Add this code to a unit of its own (or in the form itself, though it would be best on its own, so it can be reused!)
// In a DBGrid, allow Shift-Click marking of a contiguous block
procedure DBGridShiftSelect(Sender: TObject; existingBookmarks: TBookmark);
var
curPos: TBookmark;
limiter: integer;
aDBGrid: TDBGrid;
aDataset: TDataSet;
iFrom,
iTo,
iTmp: integer;
begin
//Validation
if not (ssShift in GetKeyShiftState) then
exit;
if (existingBookmarks = nil) then
exit;
//Initiliasation
aDBGrid := (Sender as TDBGrid);
aDataset := aDBGrid.DataSource.DataSet;
// Get the current position (bookmark) and record number (the row we shift-clicked on)
curPos := aDataset.GetBookmark;
iTo := aDataset.RecNo;
aDataset.DisableControls;
try
//dummy loop that allows multiple exits to a single point
repeat
//go to the last bookmark before we shift-clicked
aDataset.GotoBookmark(existingBookmarks);
iFrom := aDataset.RecNo;
if iTo = iFrom then
exit;
//we want to only select moving forward, so if need be, swap the bookmarks
if iTo < iFrom then
begin
iTmp := iTo;
iTo := iFrom;
iFrom := iTmp;
aDBGrid.SelectedRows.CurrentRowSelected := True;
aDataset.GotoBookmark(curPos);
end;
//Stop after marking 255 rows (just in case recNo is not a reliable method of doing this.
//This is probably no longer an issue but it doesn't hurt to leave it here :-)
limiter := 255;
repeat
//highlight current row
aDBGrid.SelectedRows.CurrentRowSelected := True;
aDataset.Next;
iFrom := aDataset.recNo;
limiter := limiter - 1;
//keep going until we reach the shift-clicked row
until
(limiter < 0) or (iFrom = iTo);
until
true;
finally
aDataset.FreeBookmark(curPos);
aDataset.EnableControls;
end;
end;
//Add this to the DBGrid's onCellClick method:
procedure TfrmTest.DBGrid1CellClick(Column: TColumn);
begin
DBGridShiftSelect(DBGrid1, DBGridPriorBookmark);
// save last selected bookmark
DBGridPriorBookmark := DBGrid1.DataSource.DataSet.GetBookmark;
end;