After you have created a few components you probably want to put them into their own package rather than having them all in the dclusr package.
Here are a few hints about packages:
Types of Packages
Delphi knows two different types of packages, runtime packages and designtime packages.
Runtime packages are meant to be distributed with applications, just like dlls are, to keep down the executable size and prevent distributing lots of duplicate code.
Designtime packages are meant to be loaded by the Delphi IDE. They contain code to register components with the component palette, also they usually contain the component's icon since that also is only needed for the IDE.
Creating a package
Creating a new package is as simple as creating a new application. From the File menu select New -> Package - Delphi for Win32. In the Project Manager, you will now find a tree node called "package1.bpl" which has the sub nodes "Contains" and "Requires". If you expand the "Requires" node, you will find that Delphi has automatically added "rtl.dcp" to that.
Setting up a runtime package
Now that we have created a package, let's tell Delphi that it is a runtime package: Open the project options (either from the main Project menu or from the context menu of the Project Manager, shortcut: Alt+P, O). On the "Description" page do the following:
- Into the field called "Description" enter a short description of the package like "My first package".
- Under "Usage options" select "Runtime only"
- Under "Build control" select "Explicit rebuild"
- In the "Package name" group box, enter a LIB Suffix that corresponds to your Delphi version:
- Delphi 2006: "100"
- Delphi 2005: "90"
- etc; you get the picture (See Package Suffixes for a list of Delphi versions and the package suffixes they use)
Click OK to close that dialog.
So, what have we done?
- We have set a description for the package. This description will never really show up for a runtime package, but give one anyway just to get into the habit. It is more important for a designtime package.
- We set the package to be a runtime only package. This means that the IDE will tell you that it is a runtime package and refuse to install it. That's a good thing.
- We set the package to be built only explicitly. That's actually a matter of taste. I for one don't like the IDE trying to rebuild every bloody package if I only change one line in another package.
- We set the LIB Suffix to e.g. 100 which will make the package file always have a compiler version dependant suffix to help users to determine which package to use for their version of Dephi. This is not a requirement, but Borland does it for their own packages so why shouldn't we? If you look closely at the Project Manager, you will find that the new package is no longer called "Package1.bpl" but "Package1100.bpl". There's the suffix we just added.
Now it's time to save this package to an actually meaningful name. Select "Save Project As" in the File menu and call it e.g. "MyPackage.bdsproj". Look closely at the Project Manager and you will see that it is now called "MyPackage100.bpl".
If you open an explorer window and open the folder into which you just saved your package, you will find the following files:
- MyPackage.dof (Delphi <=7), MyPackage.bdsproj (Delphi 2005/2006), MyPackage.dproj (Delphi >=2007)
- contains your settings; as with any Delphi project, it is in XML format (INI format with Delphi <=7)
- a copy of the first one (I have no idea for what purposes, but the IDE recreates it every time a project is loaded; ignore it for now)
- contains the settings from MyPackage.bdsproj in a different format suitable for use with the commandline compiler, you can ignore it for now
- your project's resources, pretty much empty
- your project's main source file, you may notice that it has a .dpk extension rather than .dpr that is normal for an application. dpk stands for Delphi PacKage.jjj
Compile your new package, we will need that for the next step.
Setting up a Design-time Package
As pointed out above, a designtime package is meant to be loaded into the Delphi IDE. You should always keep your designtime code separated from your runtime code, so it is best to always create two packages for your components from the start.
To create a designtime package you just create a new package as described above. The only difference is in the project options:
- Under "Usage options" select "Designtime only"
You can use the same description as for the runtime package, e.g. "My first package".
There is another difference to a runtime package: A designtime package must interact with the IDE and therefore needs to know about its internals. To make that possible, it needs the package "DesignIDE", which comes with any Delphi version. So we must add this package to the Requires section:
Right click on the "Requires" node of your new package and select "Add Reference". You get a dialog for entering or selecting the package. It is easier to just type in "DesignIDE" rather than using the file select dialog to find it, but just for the record: It is located in "c:\program files\borland\bds\4.0\lib" and called "designide.dcp".
Also, since we want to use this package to install the components that will be put into our runtime package, it must reference that package as well. So repeat the above to add "MyPackage" to the list of Required packages. Note that here you do not include the LIB suffix ("100"), but just the package name. When you press OK you will get an error message "File MyPackage.dcp not found.". The IDE does not know where to look for it, so this time we need the browse button. Normally the .dcp files the IDE generates are put into "<my documents>\Borland Studio Projects\bpl".
Now save your new package into the same directory as the runtime package. You could give it any name you like, but the convention here is to use the runtime package's name with a "Dcl" prefix. I guess the D stands for designtime but I haven't got a clue what the prefix means. But Borland does it and pretty much any component vendor does, so why not follow that example and call it "DclMyPackage"?
(My guess is: DCL = Designtime Component Library --Dummzeuch 15:43, 1 July 2007 (UTC))
Saving the Project Group
Since we are at it, let's save the project group as well. From its context menu select "Save Project Group" and save it to the same directory as your packages. You can call it whatever you like, maybe "MyPackageGroup"?
Now you can open both, the runtime and the designtime package, by just opening the project group.
Adding a component - the right way
If you haven't created a package before, but have created your own component(s), you have probably added them to the default dclusr package. If you haven't created any component, yet, here is how to do it:
Adding a component using the wizard
First, make the runtime package the current project by double clicking on it in the Project Manager.
From the context menu select "Add New -> Other" to open the "New Items" dialog. Since you are adding to a package, the possibilities are limited. Select the "Delphi Files" category and double click the "Component" icon.
You now get yet another dialog, the "New VCL Component" wizard. We don't want to make it difficult, so let's just create a new component based on TButton. Select it from the list (it supports incremental search, btw.) and click "Next".
On the next page, give your new button a class name. It must be unique within the whole IDE, including any 3rd party components. Use your imagination (I haven't got much of that, so I'll call it "TMyButton" for now). Select a palette page, or rather enter a new name like "MyComponents".
The Unit name also must be unique within all run- and designtime packages loaded by the IDE. From Delphi 7 on, a unit name can contain dots, so let's follow the Java tradition and use your domain name as a prefix, to make sure we don't get any clashes. (You haven't got any domain? Right, then let's just assume one like http://www.dummzeuch.de [That's my own domain, so please don't distribute any packages with that prefix!], so the unit would be called "de.dummzeuch.MyButton.pas".)
Save it to the same directory as your packages. Click "Finish" to close the wizard.
Select "Save All" from the "File" menu to save your work.
Look at the unit that the IDE has just created. It contains two things:
- A new class declaration "TMyButton", which derives from "TButton"
- A procedure "Register", which calls "RegisterComponents" to register it in the IDE.
So what's wrong here? Yes? The unit contains runtime code (the class declaration) and designtime code (the register procedure) in one unit.
This is not the correct way to write packages, as even Borland says somewhere in the online help (look it up, it's there!).
Splitting runtime and designtime code
So we must split the code from that unit. Since we added the unit to the runtime package, let's keep the runtime code there.
Now, make the designtime package the current one by double clicking on it in the Project Manager. Select "Add New -> Unit" from its context menu, and save that unit as "de.dummzeuch.MyPackageRegister" into the folder with your packages.
Move the designtime code to the new unit:
- the declaration of the procedure "Register"
- the implementation of the procedure "Register"
The IDE will now flag the "RegisterComponents" call as an error. That's because the unit is missing something from its uses clause. Do you know where it is declared? Let's have a look at the original unit again (I was about to show you the power of Refactoring here, but the IDE couldn't find the declaration :-( ). It uses the units SysUtils, Classes, Controls and StdCtrls. Guess? No, it's not SysUtils, it's Classes, where it is declared. So we add it to the uses list of our new unit.
The IDE is still complaining, now about TMyButton, so we add de.dummzeuch.MyButton as well, to make it shut up.
"What?", you probably think, "we add the runtime unit to the uses clause of the designtime unit? That must be wrong!" OK, you probably didn't think that, but let's just assume you did, so I can explain: Earlier on, we have added the runtime package to the Requires list of the designtime package. This means that any unit from the runtime package can be used in the designtime package without including it there as well. And since we want to register the new component, we must be able to reference it.
Compiling and Installing
It's about time something gets added to the IDE, so let's compile and install the packages. First, select "Build" from the context menu of the runtime package and after that do the same with the designtime package.
The IDE will compile both and create 4 files:
- The runtime package (.bpl = Borland Package Library).
- The (.dcp = Delphi Compiled Package) .dcp file for the runtime package contains the headers of all units in that package so the compiler can easily access them without actually accessing the units themselves.
- The designtime package.
- The .dcp file for the designtime package.
To add the new component to the Component Palette, we must install the designtime package into the IDE. That's easily done using its context menu: "Install". The IDE will now load the designtime package and tell you that a new component called "TMyButton" has been registered.
Switch to the Component Palette (Ctrl-Alt-P) to verify that, yes, there is a new group called "My Components" and that it contains (drum roll...) TMyButton! Hmm, no, something is wrong... There is no such group and no such component! Has the IDE been lying?
If you look closer, you will see that there aren't any components at all. The reason for this is that the IDE is (trying to be) clever: You have not selected any form so you don't really need any components.
Let's just create a new VCL application to verify this theory: Select "New -> VCL Forms Application - Delphi for Win32" from the "File" menu. The IDE will ask you to save your changes to the packages (do it) and then create the project, automatically creating the first form for us. Now, look at the Component Palette again! (Drum roll....) There we go: A group called "My Components" containing "TMyButton".
An Example Application
So now we have got the packages and just created a new application. Let's use that to demonstrate how to deploy an application with our runtime packages.
Add a TMyButton component to the form. Add an OnClick event with the following code
procedure TForm1.MyButton1Click(Sender: TObject); begin MyButton1.Caption := 'clicked'; end;
(Of course in a real life application you would give Form1 and MyButton1 some meaningful names.)
... with Runtime Packages
Save it, compile it, run it. Stop! No, it doesn't compile, why is that?
Even though we have added the packages to the IDE we have not added the MyPackage directory to the search path so the compiler cannot find the unit "de.dummzeuch.MyButton".
Now is the time for a decision: Do we want to compile using runtime packages or not? If we use runtime packages, everything will work, but we must deploy the executable with the packages otherwise it won't even start. If we do not use runtime packages, we will have to add to the search path.
I'll decide for you: You want runtime packages. So open the Project Options, switch to the "Packages" page and set the checkmark for "Build with runtime packages". Have a look at the packages listed below that (click the "..." button). It should contain "MyPackage", probably right at the end. Close both dialogs with OK.
Save it, compile it, run it. Yes, it works now, but why? If you build with runtime packages the compiler will use the .dcp files of all included packages to search for missing units. Our MyPackage.dcp contains de.dummzeuch.MyButton, so now it can compile.
As said before, since we built with runtime packages, we must deploy them with the application. In our simple case it should be enough to include the packages rtl100, vcl100 and MyPackage100. To test this, you need a computer that does not have Delphi on it. Copy the packages and the executable to one directory and start it. If something is missing, the program loader will complain and tell you which other packages are needed.
You do not need any designtime code, so the DclMyPackage package is not deployed. Since it contains references to the DesignIDE package, which you are not allowed to distribute, that would violate the licence.
This is the real reason why you should from the beginning divide your packages and units into designtime and runtime code. If you try that at the end, you will probably find it much more difficult to do.