I originally thought that a problem you would be likely to have with this is that Shift + Click is handled by the grid itself, so the time the Grid's OnMouseUp fires, the grid has already moved the dataset cursor to the row that was shift-clicked. So extending the grid selection from where it was before the shift-click would not be straightforward, because you would lack the information of where the ds cursor previously was.
However, it turns out to be quite straightforward to do. I've added some comments to the code below to explain how it works.
For simplicity, I've implemented it using a ClientDataSet with an integer ID field. The point of using an ID field is that the code uses current and prior values of the ID, in a sort of hand-over-hand way as the dataset scrolls, to do its stuff. Doing similar using bookmarks would be messier because of the need to continually allocate and deallocate them, so I leave that as an exercise for the reader.
Code:
// Form variables
SettingBookmarkRange : Boolean;
CurrentRowID,
PreviousRowID: Integer;
procedure TForm1.CDS1AfterScroll(DataSet: TDataSet);
begin
if SettingBookmarkRange then exit;
PreviousRowID := CurrentRowID;
CurrentRowID := CDS1.FieldByName('ID').AsInteger;
Caption := Format('Current %d, prior %d', [CurrentRowID, PreviousRowID]);
end;
procedure TForm1.SetBookmarkRange;
var
BM,
TempBM : TBookmark;
NewBM : TBookmarkStr;
FoundPrevious : Boolean;
begin
// This code is called after the user does a Shift-Click in the grid
// First we set a flag to temporarily prevent the CurrrentRowID and
// PreviousrowID from being updated during the dataset's OnScroll event
SettingBookmarkRange := True;
BM := CDS1.GetBookmark;
// Set a flag to keep track of whether we've found the row with the PreviousRowID
FoundPrevious := False;
try
CDS1.DisableControls;
// First, search forwards to see if we can find the the row with the PreviousRowID
// In other words, we're first checking that the Shift-Click was on a row *above*
// the previous row
CDS1.Next;
while not FoundPrevious and not CDS1.Eof do begin
if CDS1.FieldByName('ID').AsInteger = PreviousRowID then begin
FoundPrevious := True;
// We found the row with the PreviousRowID, so now get the Grid to add it, and
// all the rows back to where we started, in its SelectedRows list
while not CDS1.Bof and (CDS1.FieldByName('ID').AsInteger <> CurrentRowID) do begin
DBGrid1.SelectedRows.CurrentRowSelected := True;
CDS1.Prior;
end;
end
else
CDS1.Next;
end;
if not FoundPrevious then begin
// If we get here, the Shift-Click must have been in a row further down the
// grid than the previously-current one
while not FoundPrevious and not CDS1.Bof do begin
if CDS1.FieldByName('ID').AsInteger = PreviousRowID then begin
FoundPrevious := True;
// We found the row with the PreviousRowID, so now get the Grid to add it, and
// all the rows back to where we started, in its SelectedRows list
while not CDS1.Eof and (CDS1.FieldByName('ID').AsInteger <> CurrentRowID) do begin
DBGrid1.SelectedRows.CurrentRowSelected := True;
CDS1.Next;
end;
end
else
CDS1.Prior;
end;
end;
finally
CDS1.GotoBookmark(BM);
CDS1.FreeBookmark(BM);
CDS1.EnableControls;
SettingBookmarkRange := False;
end;
end;
procedure TForm1.DBGrid1MouseUp(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
begin
Caption := IntToStr(CDS1.Fields[0].Value);
if ssShift in Shift then
SetBookMarkRange;
end;