2

Looking for some example code illustrating how to create at runtime TField Components associated with a dataset.

In the IDE, if you plop down a dataset component, right-clicking brings up the fields editor that provides this functionality at design time. Have not been able to find code showing how to do it at runtime.

TIA

Vector
  • 10,879
  • 12
  • 61
  • 101
  • 1
    What are you trying to accomplish? If it is to avoid using FieldByName in a loop, you can just add TField variables to a method, assign them using FieldByName once and then use the vars in the loop... – Marjan Venema Jun 29 '11 at 20:46
  • You probably need FIELDS at runtime, not PERSISTENT FIELDS. – Warren P Jun 29 '11 at 23:24
  • 1
    @Marjan for example this allows to create calculated / lookup fields dynamically at run time, or to create a dataset which is not bound to a real database (connection) but simple in-memory data – mjn Jun 30 '11 at 05:11
  • @mjn - +1 - see my comments below - that's exactly what I need to do. – Vector Jun 30 '11 at 05:26

2 Answers2

1

Each field type has a Create function that you pass the DataSet to that creates a field of that type and adds it to the Fields. From the help for DB.TStringField.Create.

var
  T: TStringField;
begin
  SQLDataSet1.Close;
  T := TStringField.Create(SQLDataSet1);
  T.FieldName := 'LastName';
  T.Name := SQLDataSet1.Name + T.FieldName;
  T.Index := SQLDataSet1.FieldCount;
  T.DataSet := SQLDataSet1;
  SQLDataSet1.FieldDefs.UpDate;
  SQLDataSet1.Open;
end;
Brian
  • 6,717
  • 2
  • 23
  • 31
  • This accomplishes nothing, really. Opening the dataset will do the same thing for you with zero code, unless the dataset is in-memory only and hasn't been persisted to disk (eg., a `TClientDataSet` that you can't `LoadFromFile` or populate from an existing database provider). The only difference is that you'll have a field named 'DataSetNameFieldName' that you can now access via `FieldByName`. Note I didn't downvote your answer; just mentioning that this is all zero-functionality code for the most part. – Ken White Jun 29 '11 at 23:41
  • Actually this appears to be the correct answer: this code creates a tcomponent descendant bound to the dataset. Please don't worry about what I want to accomplish or what I stand to gain - better to just read the question and answer it if you can. Rest assured, I know what I want to accomplish. – Vector Jun 30 '11 at 02:01
  • @Mikey: Great. The point is that **we** need to know what you want to accomplish in order to answer your question - "just read the question and answer it" isn't possible, and being rude to people trying to help won't get you far (just as future advice). I could simply have downvoted your question as being unclear, or voted to close it as "not a real question". If your intent is unclear, it's more difficult to answer the question. – Ken White Jun 30 '11 at 02:25
1

"Persistent fields" created at runtime make no sense. Creating persistent fields in the IDE allows them to be written to the .dfm for the form/datamodule, and then be created automatically when that .dfm is loaded from the executable, and can be accessed by name in your code that uses that datamodule.

If you're looking to not have to use FieldByName at runtime, you can just do something like this:

TMyDataModule=class(TDataModule)
  // Usual IDE created stuff, etc.
public
  NameFld: TStringField;
  LimitFld: TFloatField;
end;

procedure TMyDataModule.DataModuleCreate(Sender: TObject);    
begin
  NameFld := MyDataSet.FieldByName('CompanyName') as TStringField;
  NameFld.Required := True;
  LimitFld := MyDataSet.FieldByName('CreditLimit') as TFloatField;
  LimitFld.Currency := True;
  // Set other properties as needed.
end;

You now have the equivalent of persistent fields at runtime. They can be accessed as usual in other code that uses your datamodule.

procedure TMyDataModule.DoSomethingWithData(CompanyName: string; CreditLimit: Currency);
begin
  MyDataSet.Edit;
  NameFld.AsString := CompanyName;
  LimitFld.AsCurrency := CreditLimit;
  MyDataSet.Post;
end;

EDIT: Just thought of two exceptions to my statement "make no sense" - those would be calculated or lookup fields. For lookup fields, it's easiest to just add them to SQL via a JOIN and let the server do it; for calculated fields, you can use the DataSet.FieldDefs.Add and set the appropriate properties to name the field, set the newly created field's FieldType to ftCalculated, and assign an OnCalcFields event handler to handle the calculation.

Ken White
  • 123,280
  • 14
  • 225
  • 444
  • I rolled back your edit, because it changes the entire question (and therefore the answers). In the future, ask the question you really want the answer to - if you can't state the question clearly, it's difficult to provide accurate responses. "Persistent" is what you asked for, and "persistent" is what I answered. If it's not what you intended, delete it and ask a new question instead of changing the subject (or meaning) entirely after the fact. – Ken White Jun 30 '11 at 02:29
  • In addition, it doesn't make sense, because if you can create it at runtime and then write it to file, you can just create it at runtime and forget the file; the next run can create them again at runtime. ReadComponent/WriteComponent makes no sense in the context of your question; that isn't the question asked. – Ken White Jun 30 '11 at 02:31
  • @Ken - with all due respect, the crux of the question was how to make TFieldComponents at runtime that replicate what the fields editor does at design time. 'Persistent' was not really important and I used the term because that's how the Delphi docs refer to them when you create them at design time. – Vector Jun 30 '11 at 03:37
  • 1
    @Ken-As for what I need to do, I have a very old application filled with TTable components with lots of persistent fields,many of them calculated fields,created at design time + thousands of lines of code that refer to the TfieldComponents by component name without referencing datasets. Now, I have to replicate that functionality and move some of that code but use in memory datasets. Don't want to plop down hundreds of components on a data module and hide everything away- I want transparent code only. Thus, my question. Not seeking workaround, but how to do one particular task, as stated. – Vector Jun 30 '11 at 03:47
  • @Ken - sorry if I sounded rude - but IMO the question was pretty clear and my reasons were irrelevant. I know how to create datasets and databound fields and calc fields and indexes etc etc at runtime - have done it thousands of times and written my own tdataset descendants as well. But I never had the need to do it this particular way. Another long time Delphi guy in my shop who also knows lots of tricks also didn't know and never had the need - it is admittedly a 'special case'. – Vector Jun 30 '11 at 03:54
  • 2
    @Mikey - also with due respect, but you statement is not plausible. The correct answer was a snippet from the Delphi help. I would call that trivial. If you and your buddy are old Delphi guys, that should be no question. And as Ken repeatedly said: technically it makes no sense to generate many fields by code. Your approach is wrong in my opinion. If you have to replicate this, i would do it with designtime-generated fields now. – Andreas Jun 30 '11 at 06:42
  • 1
    @Andreas:you are making a good point-but I looked in the Delphi help but wasn't sure what to look for-everything spoke about the designer. There can be something right there in the help but if it's something you never do it's not always easy to find - I'm a contractor paid by the hour to get a task accomplished, and often SO gets me info faster than the incomplete Delphi help that we now have. Like debugging code - 'another pair of eyes'. At design time? I hate having to dig through embedded components (or DFMs) to find out what code is doing and don't wish this on anyone else. – Vector Jun 30 '11 at 13:37
  • @Andreas: +1 for your constructive criticism. :-) – Vector Jun 30 '11 at 16:12
  • Replacing calculated fields by additional SQL joins is not always a good solution - it increases the client/server data traffic and the database server CPU load. Doing it client side distributes the CPU load. – mjn Jul 01 '11 at 06:58
  • @mjn: I didn't say anything about "calculated field by ...SQL" - I said "lookup fields", and never said either were *always* a good idea, did I? :) But most of the time for calculated fields it's the best way - the server is designed to do those things. The cost of the server doing something like `Cost * Quantity AS TotalPrice` is negligible, and so is the increased network traffic (a single floating point column). – Ken White Jul 02 '11 at 01:35