Popolare una TComboBox è un'operazione banale finché non la si ritrova copiata e incollata in dieci form diversi, ognuno con una sorgente dati differente: un array statico, un file di configurazione JSON, il risultato di una query. Incapsulare questa logica in piccole classi dedicate riduce la duplicazione e rende più semplice cambiare sorgente in futuro senza toccare il codice del form.
Da un array o da un JSON
La stessa classe può esporre due metodi distinti per le due sorgenti più comuni: un array statico di stringhe e un oggetto JSON le cui chiavi diventano le voci della combo.
unit ComboBoxPopulator;
interface
uses
System.SysUtils, System.Classes, System.IOUtils, System.JSON, Vcl.StdCtrls;
type
TComboBoxPopulator = class
private
FComboBox: TComboBox;
public
constructor Create(AComboBox: TComboBox);
procedure PopulateFromArray(const Items: array of string);
procedure PopulateFromJSON(const JSONFilePath: string);
end;
implementation
constructor TComboBoxPopulator.Create(AComboBox: TComboBox);
begin
FComboBox := AComboBox;
end;
procedure TComboBoxPopulator.PopulateFromArray(const Items: array of string);
var
I: Integer;
begin
FComboBox.Items.Clear;
for I := Low(Items) to High(Items) do
FComboBox.Items.Add(Items[I]);
end;
procedure TComboBoxPopulator.PopulateFromJSON(const JSONFilePath: string);
var
JSONValue: TJSONValue;
JSONObj: TJSONObject;
I: Integer;
begin
FComboBox.Items.Clear;
if not TFile.Exists(JSONFilePath) then
Exit;
JSONValue := TJSONObject.ParseJSONValue(TFile.ReadAllText(JSONFilePath));
try
if JSONValue is TJSONObject then
begin
JSONObj := TJSONObject(JSONValue);
for I := 0 to JSONObj.Count - 1 do
FComboBox.Items.Add(JSONObj.Pairs[I].JsonString.Value);
end;
finally
JSONValue.Free;
end;
end;
end.Da una query con filtro
Quando le voci arrivano da una tabella, spesso serve anche filtrarle prima di mostrarle: un cliente digita qualche lettera e la combo si aggiorna. Incapsulando dati e filtro in una classe basata su TClientDataSet, il form si limita a chiamare due metodi.
unit RecordTableUnit;
interface
uses
System.SysUtils, Data.DB, Vcl.DBClient, Vcl.StdCtrls;
type
TRecordTable = class
private
FDataSet: TClientDataSet;
public
constructor Create;
destructor Destroy; override;
procedure AddRecord(const ID: Integer; const Nome: string);
procedure FilterRecords(const Filtro: string);
procedure PopulateComboBox(const ComboBox: TComboBox);
end;
implementation
constructor TRecordTable.Create;
begin
FDataSet := TClientDataSet.Create(nil);
FDataSet.FieldDefs.Add('ID', ftInteger);
FDataSet.FieldDefs.Add('Nome', ftString, 50);
FDataSet.CreateDataSet;
end;
destructor TRecordTable.Destroy;
begin
FDataSet.Free;
inherited;
end;
procedure TRecordTable.AddRecord(const ID: Integer; const Nome: string);
begin
FDataSet.AppendRecord([ID, Nome]);
end;
procedure TRecordTable.FilterRecords(const Filtro: string);
begin
FDataSet.Filter := Filtro;
FDataSet.Filtered := not Filtro.IsEmpty;
end;
procedure TRecordTable.PopulateComboBox(const ComboBox: TComboBox);
begin
ComboBox.Items.Clear;
FDataSet.First;
while not FDataSet.Eof do
begin
ComboBox.Items.Add(FDataSet.FieldByName('Nome').AsString);
FDataSet.Next;
end;
end;
end.Un'unica regola da rispettare
Qualunque sia la sorgente, vale una regola semplice: la combo non deve mai sapere da dove arrivano i dati. Il form chiama PopulateFromArray, PopulateFromJSON o PopulateComboBox allo stesso modo, e la sorgente reale resta un dettaglio implementativo che si può cambiare senza toccare l'interfaccia. È un investimento minimo che ripaga rapidamente non appena la stessa combo va riusata in un secondo form.