Transfer data between forms in borland c++ builder - c++builder

I designed two forms in c++ builder:
TfrmMain
TfrmChooseName
In TfrmMain class I have button named btnNext. when btnNext is clicked, code below runs and creates new TfrmChooseName.
frmChooseName = new TfrmChooseName(this);
this->Hide();
frmChooseName->ShowModal();
this->Show();
delete frmChooseName;
frmChooseName = NULL;
also in TfrmMain I have TEdit control named txtInput.
In costructor of TfrmChooseName I want to get text of txtInput and set it as a caption of form but access volation error occured!
I also made both classes friend!

The best way to handle this is to pass the desired Caption value to the constructor itself, rather than code it to hunt for the value, eg:
__fastcall TfrmChooseName(TComponent *Owner, const String &ACaption)
: TForm(Owner)
{
Caption = ACaption;
}
.
frmChooseName = new TfrmChooseName(this, txtInput->Text);
Alternatively, you can set the Caption after the constructor exits, eg:
frmChooseName = new TfrmChooseName(this);
frmChooseName->Caption = txtInput->Text;

I think it's not possible to detect the exact problem without seeing more of the code. Making the classes friends shouldn't be necessary, since components added using the form designer have public access anyway.
Have you removed TfrmChooseName from Auto-Create forms? If not, and if frmChooseName is the global variable pointing to the auto-created form, that might cause the Access Violation.
The RADStudio Documentation article Creating Forms Dynamically says:
Note: If you create a form using its constructor, be sure to check that the form is not in the Auto-create forms list on the Project > Options > Forms page.
Specifically, if you create the new form without deleting the form of the same name from the list, Delphi creates the form at startup and this event-handler creates a new instance of the form, overwriting the reference to the auto-created instance. The auto-created instance still exists, but the application can no longer access it. After the event-handler terminates, the global variable no longer points to a valid form. Any attempt to use the global variable will likely crash the application.
You may also want to take a look at Creating a Form Instance Using a Local Variable.

Related

How to make an old form inherit from another?

I have an existing form and i want this form now, to inherit from another form.
For a new form i know how to do this, just go on File > New > Other > Inheritable items and select the father form. But what about an already existent form ?
Here is what i tried, change this :
type
TFrmMyForm = class(TForm)
To this :
type
TFrmMyForm = class(TFrmFatherForm)
But it doesn't seem to work, as FrmMyForm isn't importing FrmFatherForm components.
Is there a way to achieve this ?
Thanks
Open dfm file in some other text editor and replace object with inherited
object FrmMyForm : TFrmMyForm
to
inherited FrmMyForm : TFrmMyForm
However, Delphi has issues with opening such forms if they don't belong to the same project. For instance, if you have base form declared in a package and you are using it to inherit forms in application or another package.
If you have problem opening such forms, make sure that you first open base form and then inherited.

Creating a form that has not been declared works - I don't know why

I'm using Delphi 7 (I know it's antique) and am a bit confused by a form I'm creating as needed and destroying when done with it.
From my main form I create another form requesting a Username and Password. The newly created form properties etc are contained in a another unit and is included in the Uses clause.
In my main form I previously "had" the following code;
var
MyOtherForm: TMyotherform;
Begin
MyOtherForm := TMyotherform.create(Nil);
{ Then I do stuff - blah blah }
MyOtherForm.free;
End;
My question is, when I remove the declaration for MyOtherForm in my main unit it still works without error. For example;
{ var // removed
MyOtherForm: TMyotherform; // removed }
Begin
MyOtherForm := TMyotherform.create(Nil);
{ Then I do stuff }
MyOtherForm.free;
End;
The same result, the form is created as usual and destroyed. What I cannot understand is why. Have I been doing it wrong in the past by declaring my form in the main unit, or does having it declared in a separate unit sufficient?
By default, Delphi creates a global variable for the form. It is added just below the class declaration of the form.
The name for that variable is the class name minus the 'T', so it's the same name you used for your local variable, which is why the code still works: you just stored a new reference in that global variable.
If you have an auto-create form, Delphi will create an instance of the form on start-up of the application, and store the reference in that global. You can manage auto-created forms and data modules in the project options, or you can simply edit the dpr file, in which you will find a line like:
Application.CreateForm(TMyotherform, Myotherform);
But even if your form is not auto-created, Delphi still adds that global variable.
Personally, I don't like those global variables at all, and I always remove them manually when I create a form or a data module. Unfortunately, there doesn't seem to be a possibility to configure this.
So: Remove the global and declare the local variable like you did in your original code. That is the right way to do it. The global is there to make it easier for beginners, but it's not helping the maintainability of your application.

Binding a second instance of a form to a second instance of a data module?

I have a Form which has data aware controls bound to datasets in a datamodule. I need to create additional instances of both the form and datamodule in the same application.
I deleted the global variable that was automatically created when the data module was first added to the project. To my delight, controls in the designer could still be bound to datasets in the data module without this global variable. I'm assuming the IDE is parsing the dfm of the datamodule so the designer can still "see" the datamodule. (without the data module loaded in the IDE the list of data sources is empty)
When I create two instances of the form and two instances of the datamodule at runtime both instances of the form appear to be bound to only the first data module that was created. Inspecting the second instance of the data module reveals that the Name property has a number suffix that wasn't there at design time.
The form depends on a lot of datasets in the data module. Is there an easier way to bind the second form instance to the second data module's datasets without resorting to hand coded SomeControl.DataSource := Module2.dsSomeData for every single control?
I'm open to alternative suggestions as well. One solution would be to move the datasets to the form itself. Still it seems a shame if design time data binding only works on singletons.
Take a look at this question:
separate dataset instances using datamodules in delphi
Basically the answer is to create your DataModule, then your Form, then set the name of the created DataModule to an empty string. That will let the initial data binding happen but prevent other forms from seeing that Module.
In addition the next version that is created will still have the original name (no need for the numeric suffix).
I have a Form which has data aware controls bound to DataSets in a DataModule.
That is not possible. Indirectly, ok agreed, but there has to be a necessary DataSource in between. (From the rest of your question we have to distill the information that those DataSources are on the DataModule, but the question could certainly be more transparent about that.)
I deleted the global variable that was automatically created when the DataModule was first added to the project.
Good, stick to that custom!
To my delight, controls in the designer could still be bound to DataSets in the DataModule without this global variable. I'm assuming the IDE is parsing the dfm of the DataModule so the designer can still "see" the DataModule.
Same incorrectness/confusion between DataSource and DataSet, but you are partly right: for the IDE being able to find a DataModule, the following must be true:
The DataModule must be created/opened at least once during the session of the IDE (it may be closed afterwards during the session), see (*),
The unit of that DataModule must be present in the uses list of the Form unit.
When I create two instances of the Form and two instances of the DataModule at runtime, both instances of the Form appear to be bound to only the first DataModule that was created.
That is because you are depending on the automatic design time binding which does not work at runtime. That binding depends on the name of the DataModule. But this is not the only disadvantage of depending on design time binding, see (*).
Inspecting the second instance of the DataModule reveals that the Name property has a number suffix that wasn't there at design time.
Plus an underscore in front of that sequential number suffix. It seems to be by design. There cannot be multiple DataModules (nor Forms) with the same name, which is comparable with Components not capable of having the same name of that of a sibling or child Component. It is a little strange though, because when giving no Owner or even different Owners, the same rule still applies for DataModules and Forms, which is unlike default TComponent behaviour. I cannot find evidence nor an explanation in the RTL/VCL code. Maybe it has something to do with all DataModules and Forms being kept in the Screen variable. We just have to accept, but it is a no-problem.
The Form depends on a lot of DataSets in the DataModule. Is there an easier way to bind the second Form instance to the second DataModule's DataSets without resorting to hand coded SomeControl.DataSource := Module2.dsSomeData for every single control? ... One solution would be to move the DataSets to the Form itself.
Wherein dsSomedata being the DataSource!
Yes, there is an easier way. Instead of placing the DataSets on the Form, place the DataSources on the Form. Typically, a Form often has only a single or few DataSources compared to the number of data controls. That way the data control - DataSource binding remains intact (because both are read from the same DFM), and only the DataSet settings of the DataSources remain to be set manually:
TCustomerForm = class(TForm)
DataSource: TDataSource;
procedure FormCreate(Sender: TObject);
private
FData: TCustomerDataModule;
end;
procedure TCustomerForm.FormCreate(Sender: TObject);
begin
FData := TCustomerDataModule.Create(Self);
DataSource.DataSet := FData.Query;
end;
Or, when you want to create the Form from the DataModule:
TCustomerForm = class(TForm)
DataSource: TDataSource;
private
FData: TCustomerDataModule;
procedure SetData(Value: TCustomerDataModule);
public
property Data: TCustomerDataModule read FData write SetData;
end;
procedure TCustomerForm.SetData(Value: TCustomerDataModule);
begin
if FData <> Value then
begin
FData := Value;
DataSource.DataSet := FData.Query;
end;
end;
(*) In large projects with many Forms and DataModules, it is very common to not open every DataModule in the IDE, and (DataSource.)DataSet settings easily can get lost. Being dependent on the designtime binding by DataModule name then may result in your Forms never showing any data. This is a bug which is hard to foresee whithout checking every Form's (DataSource.)DataSet settings.
Doing it all in the above outlined manner ensures correct DataModule creation at runtime, and ensures all designtime capabilities and cleverness of the IDE providing DataSets, Fields, etc...
I have the same issue. I used the following solution.
My 2 instances of the form share the same DataModule instance. The 2 forms don't show up at the same time. I have the advantage, that the 2 forms show always the same data, since my data is in memory cached, with the TCLientDataSet.
e.g.
_dmSachkonto := TCachedDataModules.Instance.Add(TdmSachkonto) as TdmSachkonto;
TdmSachkonto is my DataModule.

How to explicitly release GDI handles allocated by TForm derived class owned by the Application?

A single class derived from TForm appears to hold onto GDI handles until the application is closed.
class TTestForm : public TForm {
public:
TTestForm(TComponent*);
};
std::auto_ptr<TTestForm> test(new TTestForm(NULL));
test->ShowModal();
I'm quite new to VCL, so please bear with me. This test was done with a form that contains no controls. As far as I udnerstand, all objects are owned by the Application if no owner is specified.
My application creates (and destroys) a lot of forms dynamically. 3-4 new GDI handles are allocated each time a form is displayed. Is there a way to explicitly release those GDI handles during application lifetime?
Caveat: I'm a Delphi programmer, not C++, but the VCL is basically the VCL. You can try the form's Release() method instead of free(). Or alternatively, in the OnClose event set the Action parameter passed to caFree - thats supposed to tell the VCL to free the window's resources when the form closes, rather than hiding it.
I guess another question is - do you need to keep creating/destroying the forms? Can you create them once and then reuse them?
It turns out that the leak was caused by an incorrectly set TImageList.ShareImages property.

Passing parameters to a delphi TFrame

To avoid singletons and global variables I'd like to be able to pass parameters to a TFrame component. However since a TFrame normally is included on form at design time it is only possible to use the default constructor.
The parent form can of course set some properties in the OnCreate callback after the TFrame has been created. However this does not ensure that a property is not forgotten, and the dependencies are not as clear as using a constructor.
A nice way would be if it was possible to register a factory for creating components while the dfm file is being read. Then the required parameters could be passed to the TFrame constructor when created by the factory. Is there a way of accomplishing this?
Or does anyone have a better solution on how to pass parameters to a TFrame?
All components, including descendants of TFrame, need to be able to be constructed using the constructor inherited from TComponent. Otherwise, they can't be used properly at design time. If the restriction of design-time use is acceptable to you, then you could override that constructor and raise an exception. That would prevent the component from being placed on a form at design time. Simply provide some other constructor that requires other parameters.
Because of the design-time requirement, all components need to be able to exist with some or all of their properties still at their default values. That doesn't mean the components have to do useful things while they're in that state, but they do need to be able to stay in that state indefinitely. It should be OK, for example, to place a component on a form, save the form, and close Delphi, with the intention of resuming the form-designing at a later time. The component should allow itself to be saved and restored, even if all its properties haven't been set up for final use yet.
My preferred option is to enforce the component's rules only at run time. Check that all the properties are set to sensible values before you allow them to be used. You can use assertions to enforce the proper use of your components. Consumers of your classes will learn very quickly if they haven't finished setting up your components on their forms.
I would normally add a public, non-virtual "Initialise" or (Initialize to Americans) procedure which requires all parameters to be provided. This will then set the properties.
Make the properties protected or private if possible, so the only way they can be set is from calling Initialise(AFoo, ABar : integer).
Then in TFormXXX.FormCreate or TformXXX.Create, have:
inherited;
Initialise(foo, bar);
could you create/registercomponent your own tFrame component and
place that on the form - it's create could have anything passed to it.
If a factory could provide the parameters that you need, why don't you just override the default constructor for your frame, and ask the factory-class for parameters?
I usually make my own constructor. I don't like to create frames at designtime anyway.
a) a frame can be created dynamically when required and destroyed when not needed
b) give the frame a public property with either the parameter data type or a data structure and pass the values to the form through the property.
Example:
TAddress - a class to hold the usual elements of an address.
TAddressFra - a frame with the visual controls to display the address
populate an instance of TAddress with values
create an instance of TAddressFra
assign the TAddressFra.address property with the TAddress instance
use the procedure setAddress(o_address : TAddress) to assign the values of the TAddress attributes to the corresponding visual components on the TAddressFra

Resources