Short Version
How do i call:
dataset1.FieldByName(fieldName).AsString := 'Something';
and have it work?
Long version
I have a DataSet:
var
ds: TDataSet;
ds := GetSomeSortOfDataSetFromSomewhere();
This dataset will be exported (e.g. to Excel, cSV, TSV, Markdown, HTML, XML):
ExportDataSet(ds);
and the export will contain all columns and all rows:
| Username | Fullname |
|---|---|
| ian | IAN BOYD |
| MartynA | MARTIN |
| ngal | NASREDDINE GALFOUT |
| uewr | UWE RAABE |
Now i want to modify the Fullname field for each row in-memory before doing something else with it (i.e. it's never going back into a database, i don't know where it came from , it might not have come from a database):
while not ds.EOF do
begin
ds.FieldByName('Fullname').AsString := FormatNamePrettyLike(ds.FieldByName('Fullname').AsString;
ds.Next;
end;
Trying to modify a field gives the exception:
The solution is to clone the dataset into an in-memory TClientDataset:
///<summary>Clones a dataset into a TClientDataSet; which is an editable in-memory DataSet.</summary>
function CloneDataSet(dsSource: TDataSet): TDataSet; //TDataSet > TCustomClientDataSet > TClientDataSet
var
tempProvider: TDataSetProvider;
data: OleVariant;
ds: TClientDataSet;
begin
tempProvider := TDataSetProvider.Create(nil);
try
tempProvider.DataSet := dsSource;
data := tempProvider.Data;
finally
tempProvider.Free;
end;
ds := TClientDataSet.Create(nil);
ds.Data := data;
Result := ds;
end;
Which gives larger code:
var
ds: TDataset;
dsEditable: TDataSet;
ds := GetDataSomeOfSomeSortFromSomewhere();
//Clone to dataset to an in-memory dataset so we can modify it.
dsEditable := CloneDataSet(ds);
ds.Free;
ds := edEditable;
while not ds.EOF do
begin
ds.FieldByName('Fullname').AsString := FormatNamePrettyLike(ds.FieldByName('Fullname').AsString;
ds.Next;
end;
But this gives the error:
The solution is to put the dataset in edit mode:
//The in-memory ClientDataSet won't be editable until you mark it editable.
ds.Edit;
///<summary>Clones a dataset into a TClientDataSet; which is an editable in-memory DataSet.</summary>
function CloneDataSet(dsSource: TDataSet): TDataSet; //TDataSet > TCustomClientDataSet > TClientDataSet
var
tempProvider: TDataSetProvider;
data: OleVariant;
ds: TClientDataSet;
begin
tempProvider := TDataSetProvider.Create(nil);
try
tempProvider.DataSet := dsSource;
data := tempProvider.Data;
finally
tempProvider.Free;
end;
ds := TClientDataSet.Create(nil);
ds.Data := data;
//The in-memory ClientDataSet won't be editable until you mark it editable.
ds.Edit;
Result := ds;
end;
Repeating the excerise now gives the error:
Field
Fullnamecannot be modified.
The solution is to set Field.ReadOnly to false:
//Even after marking the in-memory data-set as editable, you still can't edit it
//until you mark all fields as editable.
for i := 0 to ds.FieldCount-1 do
ds.Fields[i].ReadOnly := False;
///<summary>Clones a dataset into a TClientDataSet; which is an editable in-memory DataSet.</summary>
function CloneDataSet(dsSource: TDataSet): TDataSet; //TDataSet > TCustomClientDataSet > TClientDataSet
var
tempProvider: TDataSetProvider;
data: OleVariant;
ds: TClientDataSet;
begin
tempProvider := TDataSetProvider.Create(nil);
try
tempProvider.DataSet := dsSource;
data := tempProvider.Data;
finally
tempProvider.Free;
end;
ds := TClientDataSet.Create(nil);
ds.Data := data;
//The in-memory ClientDataSet won't be editable until you mark it editable.
ds.Edit;
//Even after marking the in-memory data-set as editable, you still can't edit it
//until you mark all fields as editable.
for i := 0 to ds.FieldCount-1 do
ds.Fields[i].ReadOnly := False;
Result := ds;
end;
Repeating the exercise gives the error:
Trying to modify read-only field.
So i give up. How do i edit a DataSet field?
The cloned in-memory TCustomClientDataSet contents are all there; i just want to edit them on the client for display purposes.
Bonus Chatter
Obviously i can't be adding new columns to the data set:
| Username | Fullname | PrettyFullname |
|---|---|---|
| ian | IAN BOYD | Ian Boyd |
| MartynA | MARTIN | Martin |
| ngal | NASREDDINE GALFOUT | Nasreddine Galfout |
| uewr | UWE RAABE | Uwe Raabe |
Obviously i can't attach an event handler to the data set:
- as that data handler would be invalid when the data set is passed onto the next person in a chain (e.g. a thread), and the original form is freed
- and it's also not what i'm asking; which is about modifying the contents of a data set
- the updating of values hits other systems (e.g. databases, web-services, etc). I want the changes done once, and then in the data set
Assuming I actually understand your question correctly, it boils down to change the field content of FullName to some pretty formatted string for display.
So as you don't want to change the actual field content, the best place to do this is in the fields OnGetText event. A proper event handler for your task could look like this:
Now you have to wire that event handler to the field. As you are working with dynamic fields this has to be done each time after opening the dataset:
As long as this happens outside of the class the event is declared in, you need to prefix the event name with a class instance of TMyClass (or whatever you may call it).