I am looking for a way to provide a ListSource to a TDBLookupComboBox in delphi without having an actual table on the database server to draw that list from. The DataField for the Combo Box is a 1 character field that contains a coded value such as 'A' = 'Drivers License', 'B' = 'Passport', 'C' = 'Library Card', etc. That is to say that the table only contains A, B, or C. The application is responsible for Displaying 'Drivers License' in the GUI. Normally a database might have a look up table but this database does not and I can not add one. My idea is that the DataSource and ListSource for a DB Look-up control do not have to be the same database, so if it were possible to define a small table in my form that contains the look-up data then I could use that an not require a real database table.
Does anyone know of a delphi component that allows a TDataSet to be defined on a form without having any actual data files behind it?
I know there's different in-memory dataset. Delphi comes with TClientDataSet, which you can use the way you want. You have to deploy the midas.dll with your executable in order to work, or you must include the the MidasLib in your uses clause in order to statically link this library in your executable (no midas.dll needed at runtime).
To get what you want from the TClientDataSet, you can create fields and:
store the records in a xml file (for example with another auxiliary tool you made). At runtime load the data with the LoadFromFile method of the TClientDataSet. Additionally you can store this xml as a resource with the $R directive and manipulate this resource at runtime to feed your ClientDataSet with the contained data, to prevent the deployment (and possible modification) of the xml file with your exe.
use the CreateDataSet method and insert/populate records with what you want at runtime
code sample:
procedure TFrom1.Init;
begin
cdsIDType.CreateDataSet;
cdsIDType.InsertRecord('A', 'Drivers License');
cdsIDType.InsertRecord('B', 'Passport');
//etcetera.
end;
An alternative solution is to use TComboBox rather than TDBLookupComboBox. Use a TDictionary to define a simple in memory lookup.
type
TMyForm = class(TForm)
MyComboBox: TComboBox;
MyDataset: TSimpleDataSet;
procedure MyComboBoxChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
ComboLookup: TDictionary<string, Char>;
end;
implementation
{$R *.dfm}
procedure TMyForm.FormCreate(Sender: TObject);
var
Key: string;
begin
ComboLookup := TDictionary<string, Char>.Create;
ComboLookup.Add('Drivers License', 'A');
ComboLookup.Add('Passport', 'B');
ComboLookup.Add('Library Card', 'C');
for Key in ComboLookup.Keys do
begin
MyComboBox.Items.Add(Key);
end;
end;
procedure TMyForm.MyComboBoxChange(Sender: TObject);
begin
// This may be wrong didn't bother to look
//up the correct way to change a field's value in code.
MyDataset.Fields.FieldByName('IDCard').AsString := ComboLookup[MyComboBox.Text];
end;
You could use TComboBox.Items.AddObject instead of a separate lookup table but you'd have to create a wrapper class to store a char as a TObject or use Chr to convert it to an integer then cast it to TObject but a the above is simpler in my opinion.
Use a TClientDataset and define the fields, then connect to a datasource.
In the oncreate event of the form do this:
execute the createdataset method of the clientdataset and then populate it with the A,B,C data.
If you use the jvcl, what you want can be accomplished without involving a dataset. Just use a TjvDBComboBox, use the Items property to set the values you want the UI to display, and use the Values property to set the actual values stored in the database.
Related
I am trying to pass a DataModule to a form in the form's constructor. I also want the form to be the "Owner" of the DataModule so that the form will destroy the DataModule when it is closed. That creates the problem of both objects needing each other in their constructors.
I tried to set the owner of the DataModule after creation but that is a read only property.
My second form looks like this:
type
TSecondPopup = class(TForm)
private
FMyData: TMyData;
public
constructor Create(MyData: TMyData); reintroduce;
end;
var
SecondPopup: TSecondPopup;
implementation
{$R *.dfm}
constructor TSecondPopup.Create(MyData: TMyData);
begin
FMyData := MyData;
inherited Create(nil);
end;
There is no special code in my data module.
In my main form I want to do something like this when showing the second form:
procedure TMainApp.Button1Click(Sender: TObject);
var
MyData: TMyData;
SecondPopup: TSecondPopup;
begin
MyData := TMyData.Create(nil);
SecondPopup := TSecondPopup.Create(MyData);
// Can't change owner now. It is a read only property.
// MyData.Owner := SecondPopup;
SecondPopup.Show;
end;
I know I can change the DataModule to be a property on the form. Then I could create the form first, then create the data module setting the owner, and finally set the property on the form. I am trying to use constructor dependency injection on this project. It had been working great when I had a shared data module that the main form passed to multiple forms. In that case the main form holds on to the data module until it exists. In this case there is only one form that needs this data module so I wanted to force it to manage the life of the data module by setting the owner.
Another option would be to explicitly free the DataModule when I close the second form. However that form has no way of knowing if the caller also passed the datamodule to a different form.
Is there a way to use the constructor to inject my object but still get the form to manage the lifetime?
Currently using Delphi XE3.
Changing ownership of a component is possible with NewOwner.InsertComponent(TheComponent). And since a component can only be owned by one component at a time, the RTL takes care of removing ownership from the previous owner automatically.
But...
Another option would be to explicitly free the DataModule when I close the second form. However that form has no way of knowing if the caller also passed the datamodule to a different form.
If you want the possibility to pass a single DataModule to multiple Forms, then changing ownership of the DataModule is not the solution: the one Form that owns the DataModule will not be able to deside whether it may free the DataModule. Thus the conclusion is that the DataModule cannot be owned by any form, except by the MainForm. (Then I would prefer to let it own by the Application object, but that's a matter of taste.)
Subsequently, you will then need a reference counting mechanism within the DataModule for the Forms which it is attached to.
You don't have to change the DataModule's Owner. You can simply destroy the DataModule whenever you want, even if it has an Owner assigned. When the DataModule is freed, it will simply remove itself from its Owner so it is not freed a second time. If you go this approach, you should also add a call to FreeNotification() so that you are notified if the DataModule gets freed (by its Owner or anyone else) while your form is still referencing it:
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
FMyData.FreeNotification(Self);
end;
destructor TSecondPopup.Destroy;
begin
if FMyData <> nil then
FMyData.RemoveFreeNotification(Self);
inherited Destroy;
end;
procedure TSecondPopup.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FMyData) then
FMyData := nil;
end;
If you absolutely want to change the DataModule's Owner, that is also doable, via the TComponent.RemoveComponent() and TComponent.InsertComponent() methods:
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
begin
// InsertComponent() will call RemoveComponent() internally for you...
Self.InsertComponent(FMyData);
end;
end;
You want to let the form own the data module. Since ownership is most naturally specified at construction time, then the conclusion is that the form should be created first.
So, instead of passing the data module to the form's constructor, pass something that allows the form to invoke the instantiation of the data module. For instance, you could pass a function to the form's constructor that accepts the form as a parameter and returns the newly minted data module. For instance.
type
TCreateDataModule = function(Owner: TMyForm): TMyDataModule of object;
You could perfectly well create the data module outside the form but with no owner. That pass the object to the form constructor. The form can then destroy the data module in its destructor. That sounds the cleanest solution to me. I find it hard to see past this option.
I think you considered this option already but rejected it with this reasoning:
However that form has no way of knowing if the caller also passed the data module to a different form.
If that is so, then nothing can save you. You cannot have two objects both in charge of the lifetime, unless they use reference counting or similar.
Why create a Data Module that is meant to be used by a form, prior to creating the form?
1) Why not just add the Data Module unit to the interface uses list of the form, declare a private variable of the Data Module type in the form, and create the form's Data Module variable in the OnCreate event of the form...and then you can FreeAndNil the Data Module in the OnDestroy event.
Additionally, you can further declare a public property of the form's Data Module variable, with which you can access the Data Module from the calling unit (i.e. TestForm.DataModule)
2) If you are thinking you must create the Data Module outside of the form, perhaps to do a great number of Data Module-involved initializations, processes, etc. first, and then passing the Data Module off to the form and forgetting about it ... and assuming this form you are creating, that will be utilizing the Data Module, will be a NON-modal form (that bit of information would be of great help), you could always do 1) {above} first, then access the 'TestForm.DataModule' to apply all of your initializations, processes, etc. to before calling the form's Show method.
Is there a way to create forms dynamically by only their names;
The concept goes like this. I have a main form, and by some user selection, a number of predefined forms must be created and docked on tabitems on a pagecontols on the main form.
I do know the names of the forms and i do know when to create each one of those, but i would like to know if a better way of creating these forms by a single procedure call, and not having all of these information in my code.
Its Delphi XE3 firemonkey, on win 7.
Thanks in advance for any help
Apparently on Firemonkey Delphi doesn't automatically register form classes to be available by name, so you'll first need to add something like this to the end of the unit that holds your form class:
unit Form10;
[ ... ]
// Right before the final "end."
initialization
RegisterFmxClasses([TForm10]);
end.
This will automatically register TForm10 so it'll be available by name. Next you can use this kind of code to create a form at Runtime by it's class name:
procedure TForm10.Button1Click(Sender: TObject);
var ObjClass: TFmxObjectClass;
NewForm: TCustomForm;
begin
ObjClass := TFmxObjectClass(GetClass(ClassName));
if ObjClass <> nil then
begin
NewForm := ObjClass.Create(Self) as TCustomForm;
if Assigned(NewForm) then
NewForm.Show;
end
end;
You can only create objects when you have a class reference for it. To get a class reference for something given its string name, call FindClass. Call the constructor on the result. You may have to type-cast the result to a different metaclass before the compiler will allow you to access the constructor you want. In the VCL, you might use TFormClass, but plain old TComponentClass will work, too, since all FireMonkey objects are descendants of TComponent; the important part is that you have access to the right constructor, and that's where the one you need is introduced.
It only works for classes that have been registered. Your form classes should be registered by Delphi automatically, but if they're not, you can call RegisterClasses manually, or RegisterFmxClasses if you need to put your classes in groups.
Delphi.About.com has a VCL demonstration.
I have a custom component with some published properties which have been used for a while in many projects. I want to make some particular changes to this component which requires removing these old properties and replacing them with new ones. Or otherwise, I'm not necessarily removing the properties, but let's say I just simply change the name of a property from PropName to MyPropName instead. Well, the next time any project using that component is opened, it will not be able to find PropName.
Is there any way to automate conversion of this? Or is this something people will have to do manually? What would be the proper way to maintain component property values when the names of those properties are changed?
And I mean just in the DFM code, not necessarily inside the source code.
You can use the DefineProperties extension point to help migrate your .dfm files.
type
TMyComponent = class(...)
private
procedure ReadPropName(Reader: TReader);
protected
procedure DefineProperties(Filer: TFiler); override;
published
property MyPropName: string read ... write ...;
end;
procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineProperty('PropName', ReadPropName, nil, False);
end;
procedure TMyComponent.ReadPropName(Reader: TReader);
begin
MyPropName := Reader.ReadString;
end;
This will allow your new component to read in old .dfm files with the old property name. When the .dfm file is written again, the new property name will be used.
Note that such a technique results in the component being able to read .dfm files containing either the old property name or the new property name so you can migrate in a gradual fashion if you wish. Once you have migrated all your .dfm files then it would be worth removing such code for the sake of tidiness.
The Delphi documentation covers this subject area, albeit from a slightly different perspective, in the Storing and Loading Unpublished Properties section of the Component Writer's Guide.
I have some business objects written in Delphi with a custom scheme of database persistence which is finally working for my needs. Ok, great. Now it's time for GUI implementations. And here begins the problems.
How to bind my object to the GUI properly?
I cannot use Data Aware controls since I isolated all data access components into the ORM layer, so I start to write some "Object Aware" controls using the RTTI unit (I'm working with Delphi 2010), but I have the feeling I'm going the wrong way...
Some ideas on how to resolve this using only the VCL controls?
You have several patterns for linking ORM with User Interface.
See for instance the Model GUI Mediator pattern. In short, you write an observer which will reflect the ORM content into the UI components, and vice versa. This has been implemented for instance in the tiOpf framework for Delphi (this link has videos).
Another approach is to map your data at runtime: you design your form just like usual, then you fill the content in the OnShow event, then the "Save" or "OK" button will validate then save the content into the ORM record. This is what is done in the main Sample application of our framework. Easy to code in this simple sample, but could lead into spaghetti code if you have a lot of fields and validation to operate.
The last approach is to let your ORM creates the form.
In our framework, you can define some UI properties about each table, in a dedicated structure. Then a single unit will create a form with all editable fields of your ORM object. Links to other records will be displayed as a combo box, booleans as checkboxes, sets as radioboxes, and so on. Then the filtering (e.g. trim a text field from spaces on left or right side) and the validation (e.g. ensure that a field value is unique or a valid IP address) is handled not in the UI part, but in the business logic itself, i.e. the ORM.
IMHO it's mandatory to keep a true multi-tier architecture. That is, the UI has to rely mostly on the business logic. For instance, data validation must be part of the ORM, not of the UI. For instance, if you decide to add a web client to your Delphi client application, you won't have to code the validation another time: it will be common to both clients, separated from the UI implementation details.
What you could do (though I have no code samples) is use a combination of
class helpers or interceptor classes
binding interfaces for single domain objects and/or domain object lists
Class helpers have the disadvantage that they are not officially supported and you cannot add any fields to the class you are helping.
Interceptor classes are simply descendant classes with the same name as their ancestor:
uses
stdctrls;
type
TButton = class(stdctrls.TButton)
end;
You can put interceptor classes in their own unit and use that whereever you want. Just make sure these units are included AFTER the standard unit, so your descendant is used at run time.
Benefit of interceptor classes:
You can continue to design your UI using standard VCL or third party controls.
You get all the advantages of descendants.
You do not need to create or install your own controls.
No need for special mapper classes or use of RTTI.
Easily (well, relatively easily) integrated into a (DUnit-) testable user interface along the lines of Julian Bucknall's article on this in the (distinct) Delphi Magazine as referred to in this question/answer: Unit-testing mouse event handlers
Pseudo sample of interceptor control with binding interface / command interface:
uses
stdctrls;
type
ICommandAction = interface(IInterface)
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
IBindSingle = interface(IInterface)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
TButton = class(stdctrls.TButton, ICommandAction)
protected
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
TEdit = class(stdctrls.TEdit, IBindSingle)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
implementation could be along the lines of:
function TButton.IsEnabled: Boolean;
begin
Result := Self.Enabled;
end;
procedure TButton.Execute;
begin
Self.Action.Execute;
end;
procedure TButton.Update;
begin
Self.Action.Update;
end;
function TEdit.GetValueFromControl: string;
begin
Result := Self.Text;
end;
procedure LoadValueIntoControl(const aValue: string);
begin
Self.Text := aValue;
end;
My current customer have made their own "mapper" classes in the past (before I came).
Their data objects have fields (which are objects), and you can map these fields to a control. I extended the framework by using a MVC-like approach:
edtTarraCode: TAdvEdit;
procedure TframTarraTab.InitMapping;
begin
...
Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID);
...
end;
Per control a simple "mapping" class is created:
TMappingAdvEdit = class(TBaseEditMapping)
protected
procedure InitControl; override;
procedure AppData2Control; override;
procedure Control2AppData; override;
end;
No rocket sience, and maybe better solutions are available in the mean time (this worked in D6 and lower :-) ) but it works good enough for the customer.
Btw: also a data object generator is used. So if a field changes in the database (for example tarra.tarid is changed into tareID) we get a compiler error because "tarid" does not longer exist. This works much better than "fixed string" mapping (runtime errors).
There's currently no way to do this using only VCL controls. I know that Lazarus has a set of RTTI-based data-aware controls; you might want to look at them for some basic ideas. But it's more difficult than you might think at first. For example, unlike a dataset, an object has no built-in signaling mechanism when its members' values change. Which means that unless your data-binding controls own the object completely and nothing else has access to it, it's possible for some other code to change some value and then that change doesn't get reflected in the UI.
I've heard various things from the Delphi team in the last few years about extending the object model or the RTTI model to allow for better data binding, but whatever that's about is still a few years out.
Take a look at EverClassy Dataset at http://www.inovativa.com.br. It may meet your needs. EverClassy Dataset is a Delphi dataset designed to be populated by objects instead records from a database system.
With this component you will have the chance to interoperate your domain objects with dataware componentes, what will give you great power to build your GUI.
When trying a new component for which there's no documentation, I need to go through its methods, properties, and events to try to figure out what it can do. Doing this through the IDE's Object Inspector is a bit tedious.
Is there a utility that presents this list in a more readable format?
Thank you.
When I want to know what something can do, I read the source code. The class declaration will contain a succinct list of all the methods and properties, unless there is a lot of inheritance. The definitions will tell you want the methods do.
Another thing is to declare a variable of the type you're interested in, type its name and a period, and then press Ctrl+Space to let Class Completion show you everything you can do.
As the others said, use the source. Also an UML tool will help.
But if you don't want to use this, you can use this procedure (you need Delphi 2010 for this, and be sure to add RTTI to your 'Uses' clause):
procedure DumpProps(aObject: TObject; aList: TStringList);
var
RttiContext: TRttiContext;
RttiType: TRttiType;
I: Integer;
n: integer;
props: TArray<TRttiProperty>;
begin
aList.Clear; //it must be <> nil
RttiType := RttiContext.GetType(aObject.ClassType);
props:=RttiType.GetProperties;
with aList do
begin
Append('');
Append('==========');
Append('Begin Dump');
Append('----------');
for I := Low(props) to High(props) do
begin
try
Append(props[i].Name+': '); //odd construction to see if the Getter blows
n:=Count-1;
Strings[n]:=Strings[n]+props[i].GetValue(aObject).AsString;
except
on E: Exception do
Strings[n]:=Strings[n]+' >>> ERROR! <<< '+E.Message;
end;
end;
end;
end;
The above you can use either at run-time, either if you build a Menu Wizard, you can have your info at design time.
HTH
You can use the Class Browser that comes with GExperts.
I would also recommend to build a Model diagram with the IDE or ModelMaker. It helps to see the visual relations.
In the immortal words of Obi Wan Kenobi -- "Use the source".
There is no substitute for reading and understanding the source code of a component (or anything) to understand what it does and what it is up to.
Source code is the Lingua Franca of programming.
Look at the .hpp that is generated for C++Builder support. It resembles the interface section of a Delphi unit.
I just use code completion. If you can't figure out what the component does from the names of the properties and methods, then it's probably poorly designed anyway, and you're better off not using it. Also, since you're asking the question, I'm guessing you do not have the source. If you don't, again, I wouldn't use the component. You're only storing trouble for yourself.
There is RTTI...