Get current field in TQuery to a TField - delphi

This is related to another question but doesn't really fit enough to include it with the original. When a Post is called, how can I get the field (or fields) that was modified to a TField?

For logging, I use the OnBeforePost event, which is called (as it says) just before the data is posted. The drawback to this, of course, is that your log table has to have fields wide enough to hold all possible content.
procedure TMyData.SomeTableBeforePost(DataSet: TDataSet);
var
i: Integer;
begin
for i := 0 to DataSet.FieldCount - 1 do
begin
// Skip calculated and lookup fields
if DataSet.Fields[i].FieldType = ftData then
begin
if DataSet.Fields[i].OldValue <> DataSet.Fields[i].NewValue then
begin
LogTable.Insert;
LogTableColumnName.AsString := DataSet.Fields[i].FieldName;
LogTableOldValue.Value := DataSet.Fields[i].OldValue;
LogTableNewValue.Value := DataSet.Fields[i].NewValue;
LogTable.Post;
end;
end;
end;
end;

Related

How can i add fields to TfrxDBDataset

if Length(idStrArray)>0 then
begin
with DataModule4.ADQueryTemp do
begin
Close;
SQL.Clear;
SQL.Add('SELECT id, pato, ftest, res FROM tbl ');
SQL.Add('WHERE id IN ('+idStrArray+')');
Open;
(rprMasterDataFish as Tfrxmasterdata).DataSet := frxDst_Multi;
(rprMasterDataFish as Tfrxmasterdata).DataSetName := 'Multi';
end;
end;
Hello,
I have TfrxDBDataset component. I can add fields from table like above. But i also want to add fields and values manually at runtime.
I have text file like this :
id note
1 sample
2 sample
I want to read this text file and insert note to frxDst_Multi. Is this possible ?
I dont want to create a new column as note in tbl. Because, i have too many mysql server.
Thanks in advice,
You can't add fields to a dataset while it is open, so you have to do it before
it is opened, either in code or using the TDataSet fields editor. If you are
doing it in code, you can add the field in the dataset's BeforeOpen
event.
The next problem is that is you don't want to field to be bound to the table the
dataset accesses, you need to add it as a calculated field and set its value
in the dataset's`OnCalcFields' event - see example below.
Ideally, the added field would be a TMemoField, but unfortunately a TMemoField
can't be a calculated field (FieldKind = ftMemo). So probably the best thing you can do is to make it a String field, but then you will need to
give it a fixed maximum size and truncate the field's value at that size when you calculate its value.
Btw, I don't know whether your TfrxDBDataset supports the fkInternalCalc fieldkind, but if it does, then you could try adding the note field as a TMemoField instead of a TStringField one.
The one thing I haven't been able to do is to load the field's value from
an external file because you haven't said in your q how to determine the
name of the file which is to be read.
Obviously, the IsNull check in the frxDst_MultiCalcFields event is to avoid the overhead of reloading the file if its contents have already been read.
const
NoteFieldSize = 4096;
procedure TForm1.AddNoteField;
var
NoteField : TField;
begin
if frxDst_Multi.FindField('Note') = Nil then begin
NoteField := TStringField.Create(frxDst_Multi);
NoteField.FieldName := 'Note';
NoteField.Size := NoteFieldSize;
NoteField.FieldKind := fkCalculated;
NoteField.DataSet := frxDst_Multi;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
frxDst_Multi.Open;
end;
procedure TForm1.frxDst_MultiCalcFields(DataSet: TDataSet);
var
S : String;
begin
if DataSet.FieldByName('Note').IsNull then begin
S := 'a note'; // replace by code to set the field value
DataSet.FieldByName('Note').AsString := Copy(S, 1, NoteFieldSize);
end;
end;
procedure TForm1.frxDst_MultiBeforeOpen(DataSet: TDataSet);
begin
AddNoteField;
end;

How to find what form is calling a public procedure

I have too many Forms and I have a procedure that should be running on all form when created
procedure TDM.SetupForm(Max, DisableResize,
DisableMove: Boolean; FormWidth: Integer = 0; FormHeight: Integer = 0);
var
Form: TForm;
begin
Form := ??? // How to find the what form is running this procedure?
Form.AutoScroll := True;
if Max then
begin
Form.Width := Screen.WorkAreaWidth;
Form.Height := Screen.WorkAreaHeight;
Form.Top := 0;
Form.Left := 0;
end
else
begin
if FormWidth > 0 then
Form.Width := FormWidth;
if FormHeight > 0 then
Form.Height := FormHeight;
Form.Position := poScreenCenter;
Form.Align := alCustom;
end;
if DisableResize then
DeleteMenu(GetSystemMenu(Form.Handle, False), SC_SIZE, MF_BYCOMMAND);
if DisableMove then
DeleteMenu(GetSystemMenu(Form.Handle, False), SC_MOVE, MF_BYCOMMAND);
Form.BorderIcons := [biSystemMenu];
if Form.Height > Screen.WorkAreaHeight then
Form.Height := Screen.WorkAreaHeight;
if Form.Width > Screen.WorkAreaWidth then
Form.Width := Screen.WorkAreaWidth;
Form.ShowHint := True;
Form.OnClose := CloseFormAction;
end;
I call this Procedure on FormCreate event
How can I find what form is calling this procedure and use it inside same procedure without passing it as parameter?
I call this procedure on FormCreate event
It seems to me you don't actually need to know the "Last Created Form", but rather which form is currently being created, which you want to call this code for. If that is the case, simply add a TForm parameter to this procedure instead of declaring a variable and trying to obtain it from elsewhere...
procedure TDM.SetupForm(Form: TForm; Max, DisableResize,
DisableMove: Boolean; FormWidth: Integer = 0; FormHeight: Integer = 0);
begin
...Use the `Form` parameter...
Then you would pass Self into this whenever you call it from FormCreate...
DM.SetupForm(Self, ....
Ultimately, this sort of thing is best accomplished by creating a base form first, and then inheriting all the rest of your forms from this base. Such code would be implemented in the base form's constructor, and then you wouldn't have to explicitly call it from each and every form you wish to apply it to. However, it seems you already have many forms written and this would require modifying all of your existing code to consider the base form. Such design should be done from the beginning of development.
I must also note that putting UI code of such nature into a data module is not the right practice. A data module's purpose is to be disconnected from the UI. That's why it's not actually a visible form, but a non-visual-component-only solution. It's best to put such code in independent units for that purpose, such as MyApp.UICommon.pas.

Get id from Values in a StringList

I have a StringList with 2 rows
aOldPriceTerms[0] = 'FuelAddition=336643160'
aOldPriceTerms[1] = 'Freight=336643155'
So it works fine to use
aOldPriceTerms.Values['Freight'] -> '336643155'
I want to have a list of ID's from the list.
So simply
'FuelAddition','Freight'
Currently I use this code where aOldPriceTerms is the actual StringList.
function GetList(aOldPriceTerms: TStringList): String;
var
vOldTerm : string;
vOldTermsList : TStringList;
begin
vOldTermsList := TStringList.Create;
try
for i := aOldPriceTerms.Count - 1 downto 0 do
begin
vOldTerm := Copy(aOldPriceTerms[i], 1, (Pos('=', aOldPriceTerms[i])-1));
vOldTermsList.Add(vOldTerm);
end;
Result := vOldTermsList.CommaText;
finally
FreeAndNil(vOldTermsList);
end;
end;
My question is there a cleaner way to get the ids ?
Example is from the Delphi Basics, but TStringList.Names is also described in the Delphi documentation
// Now display all name and age pair values
for i := 0 to names.Count-1 do
begin
ShowMessage(names.Names[i]+' is '+names.ValueFromIndex[i]);
end;
You can use the TALNVStringList (NV for nameValue) from alcinoe (https://github.com/Zeus64/alcinoe) to handle Name and Value without all the time splitting the string
TALNVStringList is exactly the same as TStringList (nothing to change in the code except replacing TstringList by TALNVStringList) except that it's more efficient in speed because it's store the name in one dedicated field and the value in another dedicated field (no need to do all the time pos('=') and copy() to retrieve the name and the value of the row)
for i := 0 to aOldPriceTerms.Count-1 do
begin
ShowMessage(aOldPriceTerms.Names[i]+' is '+aOldPriceTerms.ValueFromIndex[i]);
end;
Exe demo showing the speed penalty of classic TstringList: https://svn.code.sf.net/p/alcinoe/code/demos/ALSortedListBenchmark/win32/ALSortedListBenchmark.exe
To get all the id's
for name in names.Names do
begin
i := names.IndexOf[name];
end;
or
To get all the Values
for name in vOldTermsList.Names do
begin
Value := vOldTermsList.Value[name];
end;

Why does setting a table's RecNo property not move to that record?

I have got a TTable component that uses the BDE to access a DBase table. There is no index on the table, so the sort order is the physical order of the records in the table. If I read the RecNo property, it contains the expected number for the current record.
I was under the impression that with this constellation (BDE + DBase) it is also possible to set the RecNo property to move to the corresponding record. But apparently this does not work in my program.
So: Do I remember this incorrectly? Or is there anything special I need to do for this to work?
(Please do not advise about dropping the BDE. I am aware of its issues and we are already migrating away from it.)
TBDEDataSet implements RecNo setter only for Paradox (not DBase).
unit DBTables;
...
procedure TBDEDataSet.SetRecNo(Value: Integer);
begin
CheckBrowseMode;
if (FRecNoStatus = rnParadox) and (Value <> RecNo) then
begin
DoBeforeScroll;
if DbiSetToSeqNo(Handle, Value) = DBIERR_NONE then
begin
Resync([rmCenter]);
DoAfterScroll;
end;
end;
end;
You might want to try something generic like this:
procedure SetRecNo(DataSet: TDataSet; const RecNo: Integer);
var
ActiveRecNo, Distance: Integer;
begin
if (RecNo > 0) then
begin
ActiveRecNo := DataSet.RecNo;
if (RecNo <> ActiveRecNo) then
begin
DataSet.DisableControls;
try
Distance := RecNo - ActiveRecNo;
DataSet.MoveBy(Distance);
finally
DataSet.EnableControls;
end;
end;
end;
end;

How to find the "foreign key" field name on a nested TClientDataSet?

Given a nested TClientDataSet, how could I find the link field name on the detail TClientDataSet?
I'm copying data from one TClientDataSet to another (record by record) and I would like to automatically ignore the link field.
I could also copy the data using the TClientDataSet.Data property but I still would need to clear the link and key fields.
You can check the "DataSetField" property of the detail/nested dataset, or access the "NestedDataSet" property of the master dataset.
Sample code to get the "link" field name:
function GetCDSDLinkFieldName(cds: TClientDataSet): string;
var
i: Integer;
cdsDetail: TClientDataSet;
begin
Result := EmptyStr;
cdsDetail := nil;
if Assigned(cds.DataSetField) then
cdsDetail := cds;
if not Assigned(cdsDetail) and (cds.FieldCount > 0) then
begin
i := 0;
while not Assigned(cdsDetail) and (i < cds.FieldCount) do
begin
if cds.Fields[i].DataType = ftDataSet then
cdsDetail := TClientDataSet(TDataSetField(cds.Fields[i]).NestedDataSet);
Inc(i);
end;
end;
if Assigned(cdsDetail) then
Result := cdsDetail.DataSetField.FieldName;
end;
Invoking example:
procedure ...
begin
ShowMessage(GetCDSDLinkFieldName(cdsMaster));
ShowMessage(GetCDSDLinkFieldName(cdsDetail));
end;
P.S.: 2 years later I don't believe this answer will help the author of the question, but maybe can help others that search for the same subject.

Resources