Quando dgMultiSelect è attivo su una TDBGrid, l'utente può selezionare più righe con Ctrl+click o Shift+click, esattamente come in Esplora File. Il problema è che queste righe selezionate non sono record accessibili direttamente: la griglia le tiene in SelectedRows, una TBookmarkList — una lista di segnalibri opachi che vanno usati per riposizionare il cursore del dataset, uno alla volta.
Cosa sono davvero i bookmark
Un bookmark in un dataset Delphi non è un indice numerico di riga: è un identificatore opaco (un puntatore, per i dataset più datati come BDE/ADO; un formato interno per FireDAC) che il motore dati usa per ritrovare esattamente lo stesso record anche se, nel frattempo, l'ordinamento o il filtro sono cambiati. Per questo SelectedRows.Items[i] va sempre passato a GotoBookmark, mai interpretato come un numero di riga.
Sommare un campo sulle righe selezionate
Il pattern base scorre SelectedRows, sposta il cursore su ciascun bookmark e legge il campo che interessa:
function SommaRigheSelezionate(Grid: TDBGrid; const NomeCampo: string): Currency;
var
i: Integer;
begin
Result := 0;
with Grid.DataSource.DataSet do
begin
DisableControls; // evita repaint della griglia a ogni spostamento del cursore
try
for i := 0 to Grid.SelectedRows.Count - 1 do
begin
GotoBookmark(Grid.SelectedRows.Items[i]);
Result := Result + FieldByName(NomeCampo).AsCurrency;
end;
finally
EnableControls;
end;
end;
end;DisableControls/EnableControls non sono opzionali su griglie con molte righe selezionate: senza, ogni singolo GotoBookmark forza un repaint della griglia (e di qualunque altro controllo data-aware collegato allo stesso dataset), rendendo l'operazione visibilmente più lenta con qualche centinaio di righe.
Selezionare tutte le righe da codice
Per offrire anche un bottone "Seleziona tutto" occorre scorrere l'intero dataset e marcare ogni riga corrente come selezionata:
procedure SelezionaTutteLeRighe(Grid: TDBGrid);
begin
Grid.SelectedRows.Clear;
with Grid.DataSource.DataSet do
begin
DisableControls;
First;
try
while not Eof do
begin
Grid.SelectedRows.CurrentRowSelected := True;
Next;
end;
finally
EnableControls;
end;
end;
end;Oltre la somma: un'azione generica sulle righe selezionate
Lo stesso scheletro di ciclo si presta a qualunque operazione multi-riga — non solo somme, ma anche export selettivo, invio email multiplo, cancellazione in blocco — passando una procedura anonima che riceve il dataset già posizionato sulla riga corretta:
procedure PerOgniRigaSelezionata(Grid: TDBGrid; const Azione: TProc);
var
i: Integer;
begin
with Grid.DataSource.DataSet do
begin
DisableControls;
try
for i := 0 to Grid.SelectedRows.Count - 1 do
begin
GotoBookmark(Grid.SelectedRows.Items[i]);
Azione(Grid.DataSource.DataSet);
end;
finally
EnableControls;
end;
end;
end;
// Utilizzo: esporta il campo Codice di ogni riga selezionata in una TStringList
PerOgniRigaSelezionata(DBGrid1,
procedure(DS: TDataSet)
begin
ListaExport.Add(DS.FieldByName('Codice').AsString);
end); Un limite da conoscere
dgMultiSelect e SelectedRows sono una caratteristica della TDBGrid della VCL: non hanno un equivalente diretto nei grid FireMonkey (FMX), dove la selezione multipla va gestita diversamente, di solito con uno stato applicativo separato (un TList<Integer> di indici o id) collegato manualmente agli eventi di click sulle righe.