Delphi

Lavorare con la selezione multipla di una TDBGrid: bookmark, somme ed export

calendar_today personTeam EGSOFT schedule4 min di lettura
info

Disclaimer: le informazioni pubblicate in questa sezione hanno finalità di pura divulgazione tecnica. EG Software S.r.l. non si assume alcuna responsabilità per un utilizzo improprio dei contenuti, né per eventuali danni diretti o indiretti derivanti dalla loro applicazione, e non garantisce l'aggiornamento, l'accuratezza o la completezza delle informazioni riportate. Prima di utilizzare in produzione codice o procedure qui descritte, verificane sempre l'adeguatezza al proprio contesto.

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.

Altri articoli su Delphi grid_viewTutte le tematiche
Scrivici su WhatsApp