I am currently designing a database interface in DELPHI 2010, and I have designed several form interfaces, all encapsulated in one Delphi project structure.
I began designing a particular interface form first, and, as a result, an irrelevant form first appears when I run the .exe file, whereas I want a menu form to appear when the program is loaded. My questions are:
How can I get my menu form to open first when and .exe of my compiled project is run?
What function do I need to use to program navigation buttons on this menu to open new forms up?
What function should I use on the "Exit" buttons each of my forms to close the forms down individually, without closing the whole program?
I have good knowledge in Pascal, but I am new to programming object orientated solutions in this platform.
You should determine if you forms would be auto-created or created on demand.
You should also decide which form would be main one.
The corner question would be if you can have several open forms of the same class, for example if you made "File Viewer" form there may be sense to have several of them open for different files.
For auto-created forms:
1.1 Open project source (.dpr file, Project/View Source menu) or open Project options in Forms section. Set MenuForm the 1st (topmost) one in creation list.
1.2 Check that all other forms have their .visible property false
2: Depending on the logic of your program you should use Form1.Show or Form1.ShowModal
3: Self.Close or Self.Hide or Self.Visible := false. Better 1st: http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.Close
If u use OnClose event of those forms - ensure you did not changed default caHide action for closing
For manually lifetime controlled forms:
1: Open project source (.dpr file, Project/View Source menu) or open Project options in Forms section. Set MenuForm the only one created.
2.1. some-temporary-variable := TFormClass.Create(Application);
2.2. Then you tweak some properties of some-temporary-variable like filename to open or some data source or whatever.
2.3. Then you do some-temporary-variable.Show or some-temporary-variable.ShowModal.
Beware: using ShowModal may freeze your application, in cases like TFormClass.Create(SomeAnotherForm), use Application for parent.
3: Self.Release http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.Release
or Self.Close and specify caFree action in OnClose event - http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.OnClose
Related
I have a frame:
TfrmMyFrame = class(TFrame)
...
end;
Which is placed on my main form as follows:
interface
uses Myframe;
type
TfrmMyMainForm = class(TForm)
FmyFrm: TfrmMyFrame;
...
end;
In design time I am unable to open my main form, unless I open the frame in the designer. I always have to open my frame form before I can open the main form. Please note that I am talking about design time only. Is this correct behavior? If not, how can I correct it? I am using XE5.
Frames must be included in the project for the designer to add them to or display them on a form. They can still be dynamically added to a form at runtime.
If you examine the dfm of your form you will notice frames are added using the word inline. This is translated by the VCL component streaming system to the enum value TComponentState.csInline and is used to indicate that the component is a top-level container that can be embedded into a form.
The form designer uses the same component streaming system as is used at runtime. My guess is it only parses forms and frames that are currently loaded into the IDE's internal cache. This would explain why you could open the form if you opened the frame first. By adding the frame to the project it will be loaded into a cache when the project is opened in the IDE.
Fun Fact
There is a trick used by IDE extension programmers to design forms that inherit from TDockForm, which is an Open Tools API
component that is only supplied in a precompiled binary package (no
source and no dfm). By implementing a fake TDockForm in a dummy
project that is part of the same project group as the descendent the
IDE can be fooled into loading the fake. This trick relies on the same
behavior you discovered by accident.
I am using Delphi XE6 and VCL styles. I have main application and dlls. My main application has enabled runtime themes and I am using vcl style files. I did quite similar thing to my DLLs. I enabled runtime themes and added VCL.Themes, VCL.Styles under uses and resource file with VCL style file within it. When DLL is loaded I load VCL style from resources and set it for DLL gui. Main app and DLL are not built with runtime packages.
Now I have main app GUI styled with own style and DLL gui styled with own style too. This seems to work fine until...
When I click on button in my main app which event opens TPopupMenu it's styled with same style as DLL GUI instead of main app style. If I navigate through menu I get AV too and program crashes. Take a look at the attached image.
What am I doing wrong? The only workaround I currently see would be to make my own customized TPopupMenu derived from some other control.
As I promised I prepared simple demo program which is similar to my application. It consists of host application with own style and DLL with style added to resource. Run it and click on button Popup then try select something from popup. It will crash and stop in some StdWindowProc or something like that. Also if you go to window system menu (left top corner) when you try to select something from that menu you will notice that system menu is styled as DLL gui and crashes too. Link to rar file: dropbox.com/sh/f2jmbsmw18akpyg/AAA6SWdBmVhf6n6K-mvYLLmua?dl=0
Thanks for your help.
This is a fundamental problem with VCL styles and the way that they style menus. The styling is implemented with a process wide hook. Specifically a CBT hook installed by a call to SetWindowsHookEx from TCustomStyleEngine.CreateSysHook in the Vcl.Themes unit. In fact, the hook applies just to the GUI thread, but that is process wide in the sense that there is exactly one GUI thread in the process.
Since you have multiple instances of the VCL in your application (one in the DLL and one in the application), two hooks are installed. That is one too many. The hook installed most recently (the DLL as it happens) wins, and that's why the DLL menu styling infects your executable. And why you encounter an access violation. The DLL is trying to operate on a menu that belongs to the executable. And so, in spite of your best efforts, you've ended up with the DLL code accessing VCL objects from the host executable.
There's no simple way to work around this and support styles fully in both modules. What we have here is a fundamental consequence of the design. The system was not designed to support multiple VCL instances. If you wish to use VCL styles in multiple modules, then the designers expect you to use runtime packages.
I suppose that you might be able to get some traction by operating the DLL out of a completely different thread. That would involve loading the DLL from that different thread so that the VCL is initialized in the thread. And all calls to the DLL would have to be from that thread. And you'd need to run a message loop in that thread. It's possible that you might be able to make that work, but I doubt it. Even with all the provisos mentioned you still have to handle the fact that you have two GUI threads which presents all sorts of issues with the input queue handling.
Perhaps another approach would be to uninstall the hook from the DLL. So long as your DLL is not showing menus then you may well be able to get away with uninstalling that hook. It would disable styling for menus shown by the DLL, but perhaps that's acceptable.
This version of your DLL (after I simplified it somewhat also) uninstalls the hook.
library VCLStyleDLL;
{$R 'Style.res' 'Style.rc'}
uses
VCL.Styles,
VCL.Themes,
VCL.SysStyles; // to gain access to TSysPopupStyleHook
{$R *.res}
begin
TStyleManager.TrySetStyle('Glossy', false);
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
end.
With this version of the DLL, the host executable does not suffer the problems your describe in your question.
As David says this is caused because each VCL instance install a hook to detect when a popup menu (#32768) is created. So there is two hook instances working at the same time.
As workaround you can disable the popupmenu style hook in the dll (or in the app) using the UnRegisterSysStyleHook function defined in the Vcl.SysStyles unit.
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
Let's say that I have a datamodule with an ImageList. I also have a form with a button. If I wanted to hook up the ImageList property of the button, I would add the DataModule to the forms uses then select the ImageList in the Image drop-down in the button properties.
However, I can now remove the DataModule from the Forms uses list and things still seem to work fine.
Additionally, if the DataModule is open in the IDE and not in the forms uses list, I can manually type into the images property of the button the name of the image list, and it seems to correctly display the image on the button.
The Question: Will there be any detrimental effects if I accidentally remove something from a forms uses list which a control is referencing?
I am currently in the process of cleaning up the uses lists of hundreds of forms in a large project (using cnPack and Icarus) and I want to know how careful (or ruthless) I need to be.
For the IDE being able to find a DataModule (for properties not once set) at design time, the following must be true:
The DataModule must be opened/created at least once during the session of the IDE (it may be closed afterwards during the session), see (*), and
The DataModule unit must be present in the uses clause of the Form unit.
For the IDE being able to find a DataModule again (for properties already set) at design time, the following must be true:
The DataModule must be opened/created at least once during the session of the IDE (it may be closed afterwards during the session), or
The DataModule unit must be present in the uses clause of the Form unit, or
The DataModule unit must be added to the project file.
For the program being able to find a DataModule (for properties already set) at runtime:
The DataModule must be created, or
The DataModule unit must be present in the uses clause of the Form unit.
For the program being able to find a DataModule (for properties not set) at runtime:
The DataModule unit must be present in the uses clause of the Form unit.
Thus, theoretically, if all the right conditions are met, your DataModule unit could be omitted from the uses clause of the Form. But to be confident of runtime linkage, I would conclude that there is no safe or at least no convenient escape from adding the DataModule unit to the Form unit's uses clause.
(*) In large projects with many Forms and DataModules, it is very common to not open every DataModule in the IDE, and settings easily can get lost. Being dependent on the designtime binding by DataModule name then may result in your Forms never showing any data (or Images in this case). This is a bug which is hard to foresee whithout checking every Form's DataModule settings.
A solution to that is to set all properties which refer to a DataModule's components manually at runtime, preferably in an overriden constructor or in an OnCreate event handler. This also prevents duplicate naming issues with multiple DataModule instances, as this question deals with.
The linking of component properties (like Images) is independent of a suitable entry in the uses list. The magic is hidden inside the streaming system (look for xxxFixUpReferences). As long as the compiler is not complaining you can safely remove those entries.
It is still possible that the IDE might not be happy with that.
I've got a base form in Delphi 2007 that I'd like to reuse in another project, adding some other buttons and such.
I'm familiar with subclassing a non-GUI object, but it is possible to subclass a Form in the same fashion? Can you make changes to the subclass Form in design mode?
How do you go about doing this and what are some things to look out for?
As idursun said (he was a little quicker than me):
Add the base form to your new project.
Then choose "File\New\More..."
Form the "Delphi-Projects" branch select "Inheritable Elements", find your base form and click "OK".
(The actual names of the menu items may vary since I am using a german version of Delphi 2009)
You can then make changes to your new or your base form.
If I do so I make sure to have both forms open in the IDE since sometimes it seems Delphi doesn't get all the changes if I don't.
You should add your base form to your new project and in the new form window there should be an Inherit tab where you can select a base form for the new form.
I've been sharing image lists across multiple forms in Delphi for years now. I have a TImageList on the main form of my app and then I have other forms that have components where I set the Images property to the image list from the main form (e.g. MyMainForm.MyImageList) at design time.
The problem I'm having is that randomly the Images property gets cleared on those forms that reference the image list on the main form.
This seemed to have only start recently, but I haven't been able to pinpoint the exact cause yet. I can't seem to find a way to reproduce the issue at will; it just seems to happen randomly. I did notice it happens right when a form is opened.
One thing that I did change recently in Delphi is I enabled the Autosave Project desktop and symbols feature. So now when I open this project it always remembers what forms were open. So because this issue happens when a form is opened, and because it now reopens all forms that were opened the last time the project was opened, it happens more often.
We have a few developers working on this project, and we are using SVN for version control. One issue that enabling the Autosave Project desktop and symbols feature has led to is that when we do an SNV update on the project, the symbol file can become invalid (because we don't have the symbol file under version control). This just leads to an error message from Delphi, but other than that it seems harmless. We just recompile the project and it fixes the symbol file.
Btw, I'm using Delphi 7.
If you have questions or want clarifications, leave a comment, and I'll update the question.
I ran into the same problem many years ago. Ever since, I've had the habit of putting image lists that are shared by several forms onto a TDataModule. Then Delphi doesn't lose the link. Putting non-visual components on data modules also reduces design time clutter on your forms. At runtime, the TDataModule is instantiated before the main form.
When I encountered this type of behaviour, it was always because I'd opened the "other" form without the main form open in the IDE... the IDE would try to resolve the MyMainForm.MyImageList reference, fail, and remove the reference.
We worked around this by manually assigning the imagelist in code (usually in AfterConstruction).
Ah - and the "AutoSave Project desktop" feature will sometimes open files (and, hence, forms) in "reverse" order - exacerbating the IDE-doesn't-know-about-MyMainForm "feature".
I put kind of shared things to Datamodules instead of forms. Try with this way.
I did a bunch of experimenting with some really weird results. But I did make some changes so I can work on my project without this issue occurring. I had to disable the AutoSave feature (this prevents secondary forms which may reference the main form from opening up before the main form). Then I had to make sure to delete the .dsk file of the project (the project desktop AutoSave file). So now when I open up the project it always opens the main form, so I never get the issue of references to the main form getting cleared. But I do still get the issue if after opening the project I close the main form and then open a form that references the main form.
Also I was unable to reproduce this issue of references to another form getting cleared in a brand new project with the AutoSave feature disabled, even though I can reproduce it w/AutoSave disabled in my current project. I have no idea why, but I don't feel like this is worth investigating further.
Below is the data on some of my experiments performed on my existing project. WORKS means the reference to the main form is fine. BUG means the reference to the main form got cleared. This was done w/AutoSave on and always closing all forms manually before I closed the project.
Open project
Open main form
Open secondary form
Result: WORKS
Open project
Open secondary form
Result: BUG
Open project
Open main form
Close main form
Open secondary from
Result: BUG
Open project
Open Secondary form
Result: BUG
Set Images property of component on secondary form to image list on main form
Close secondary form
Open secondary form
Result: WORKS ("caches" reference for the secondary form only; see below for "caches" explanation)
Open project
Open magic form (see below for what a "magic" form explanation)
Result: WORKS ("caches" any reference to main form for all forms; see below for "caches" explanation)
Other interesting findings:
When you open up the form and the image list does not show up, the .dfm is modified however delphi does not say that the file is modified (e.g. if you close the form it won't warn you about unsaved changes).
Only once you press save on the form will the dfm clear the imagelist reference.
I noticed there are some "magic" forms in my project (for lack of a better term). Magic forms reference my project's main form in their .dfm and they don't lose their references to the main form after you Open them, even if they are opened before any other form.
These magic forms cause the "caching" of all image list references from the main form. Once you open one of these forms, then you can open up any form that references the main form, and it won't lose it's references during this project session (even if the magic form is then closed).
Do you have mainform.pas included in project? Not only as "uses mainform.pas" somewhere in .pas, but as
fMainForm in 'fMainForm.pas' {MainForm},
in your Application**.dpr**. That comment {MainForm} is necessary for Delphi - for resolving inherited forms and links between them.