Lets say we have a Dataset with 5 fields, displayed in a DBGrid.
User want to locate a text, anywhere in the grid.
Is there any way to use TDataset.Locate() method if I want to search thru all the fields or I have to implement a custom code to achieve this?
CodePudding user response:
The short answer is that you will need some custom code, because when you call Locate with more than one field, you have to pass a variant array containing the value you want to match in the corresponding field. Although you can specify that the match can be based on partial key (field) values, the pattern to match cannot span more than one field.
Fortunately there is a straightforward way to achieve the result you want, which is as follows:
Add a calculated string field of FieldKind fkInternalCalc to the dataset, let's call it AllFields.
In the dataset's OnCalcFields set AllField's value to the concatenation of the AsString values of all the other fields of the dataset;
Call Locate against the AllFields field, with code like
if MyDataSet.Locate('AllFields', AValue, [loPartialKey]) then;
That's all you need.
Example
type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
MyDataSet: TClientDataSet;
DataSource1: TDataSource;
Button1: TButton;
DBNavigator1: TDBNavigator;
edSearch: TEdit;
procedure FormCreate(Sender: TObject);
procedure MyDataSetCalcFields(DataSet: TDataSet);
procedure edSearchChange(Sender: TObject);
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
var
AField : TField;
i : Integer;
begin
AField := TIntegerField.Create(Self);
AField.FieldName := 'ID';
AField.DataSet := MyDataSet;
AField := TStringField.Create(Self);
AField.FieldName := 'FieldA';
AField.DataSet := MyDataSet;
AField := TStringField.Create(Self);
AField.FieldName := 'FieldB';
AField.DataSet := MyDataSet;
AField := TStringField.Create(Self);
AField.FieldName := 'FieldC';
AField.DataSet := MyDataSet;
AField := TStringField.Create(Self);
AField.FieldName := 'FieldD';
AField.DataSet := MyDataSet;
AField := TStringField.Create(Self);
AField.FieldName := 'AllFields';
AField.Size := 255; // increase if necessary
AField.FieldKind := fkInternalCalc; // not fkCalculated
AField.DataSet := MyDataSet;
MyDataSet.CreateDataSet;
for i := 1 to 100 do begin
MyDataSet.InsertRecord([i, 'value' IntToStr(i), 'value' IntToStr(i), 'value' IntToStr(i), 'value' IntToStr(i)]);
end;
end;
procedure TForm1.MyDataSetCalcFields(DataSet: TDataSet);
var
S : String;
i : Integer;
begin
S := '';
for i := 0 to DataSet.FieldCount - 2 do // the 2 is to stop the loop before the AllFields filed
S := S DataSet.Fields[i].AsString;
DataSet.Fields[5].AsString := S; // AllField is field 5
end;
procedure TForm1.edSearchChange(Sender: TObject);
begin
if edSearch.Text <>'' then
MyDataSet.Locate('AllFields', edSearch.Text, [loPartialKey, loCaseInsensitive])
else
MyDataSet.First;
end;
Note: for the above code to work, MyDataSet's dataset type needs to be one which supports the fkInternalCalc field kind, such as TClientDataSet.