Untitled Document
We were in the middle of shipping the first component suite by ComponentScience
Inc, a subsidiary of Falafel Software Inc., called Elements(Ex) where we needed
to integrate the components right into the C#Builder IDE upon install. Because
of its name, we figured that the interface called “IOTAComponentInstallService“
would be important to us.
Because of a lack of documentation, we decided to open the assembly “Borland.Studio.ToolsAPI”
ourselves and snoop around.
We used Reflector, available from http://www.aisto.com/roeder/dotnet/

As you can see the methods of the Interface seemed very interesting, especially
the “Add” method which takes a ToolBoxItem and a Category.
We also wanted to use the IOTASplashScreenService and IOTAAboutBoxService as
described in Erik
Berry's excellent article on the BDN.
If you read Erik’s article already you know by now that you need to implement
a static void function called “IDERegister” that will be called
by the C#Builder IDE upon starting if a correct path of your assembly is found
in the registry under KnownIDEAssemblies.
Great! So we decided to implement the IOTAComponentInstalService inside of
the IDERegister as well, besides the other 2 interfaces for the Splash Screen
and About Box.
This, by the way, proved to be the wrong route, because we needed the Splash
Screen and About box code to be called every time the IDE starts but that is not necessary
for installing the components onto the ToolBox.
So after talking to Allen Bauer and Corbin Dunn on the IDE architecture team
at Borland and getting some feedback from Ray Konopka, (King of Component Design),
Sean Winstead, ComponentScience’s Senior Architect, decided to create
2 design time packages. One for the SplashScreen and AboutBox that always gets
called from inside of the IDERegister, in the main assembly for the components,
and another design time assembly that runs only once after installation so that
the C#Builder IDE creates a category and installs all components from Elements(Ex)
into it.
It was the best way to accomplish the task knowing that we will need to integrate
into Visual Studio using the VSIP and also Delphi 8 when it becomes available.
It is cleaner and more maintainable that way.
Ok, now let’s tackle the challenges we had to overcome for installing
these components onto the ToolBox:
First, the “Add” method of the IOTAComponentInstallService had
a parameter of type ToolBoxItem which needed to be passed in for each component
we wanted to “Add” defining the ToolBoxItem object.
Well, this is where “reflection” comes in handy.
While writing components in .NET you will find a special attribute specifying
whether the component is supposed to be a ToolBoxItem component.
[ToolboxItem(true),
ToolboxBitmap(typeof(Bitmaps.Utilities), "CsLabel.bmp")]
public class CSLabel : ExBaseControl
{
}
The first attribute for the component above specifies that it is a ToolBoxItem
component.
So if we mark all our components with this attribute, we can easily load the
assembly when C#Builder starts and use reflection to request all components
with that attribute set to true and start creating an array holding all references
to these ToolBoxItems, like in the following code:
private ArrayList GenerateToolboxItems()
{
// Generate one item per component in each product assembly.
ArrayList items = new ArrayList();
// _assemblies is a member of the class containing all the names of the
// assemblies we need to check for components.
foreach (Assembly assembly in _assemblies) {
// Use reflection to get the controls and components in this assembly
// having a ToolboxItem attribute.
Type[] types = assembly.GetExportedTypes();
foreach (Type type in types)
{
object[] attribs = type.GetCustomAttributes(typeof(ToolboxItemAttribute), false);
if (attribs.Length > 0)
{
ToolboxItemAttribute toolboxItemAttrib = (ToolboxItemAttribute) attribs[0];
if (toolboxItemAttrib.ToolboxItemType != null)
{
ToolboxItem item = new ToolboxItem();
item.AssemblyName = type.Assembly.GetName();
item.DisplayName = type.Name;
item.TypeName = type.FullName;
object[] bitmapAttribs = type.GetCustomAttributes(typeof(ToolboxBitmapAttribute), false);
if (bitmapAttribs.Length == 1) {
ToolboxBitmapAttribute bitmapAttrib = (ToolboxBitmapAttribute) bitmapAttribs[0];
item.Bitmap = (Bitmap) bitmapAttrib.GetImage(null);
}
items.Add(item);
}
}
}
}
return items;
}
So as you can see from the previous code, the return of the GenerateToolBoxItems
function is an ArrayList of items that contain the assembly name, display name,
type name and bitmap of each component.
Now that we have that ArrayList, we are much closer to calling the Add method
on the IOTAComponentInstallService for each item in the array.
foreach (ToolboxItem item in GenerateToolBoxItems())
componentInstallService.Add(item, Elements(Ex));
Pretty cool! Huh?
Ok, so we did that! Still 2 problems:
First, how can we limit the execution of the code for the integration into the
toolbox to once instead of having to fire this code everytime the IDE starts?
What if the user changes the name of the category in C#Builder after the first
run, he or she will end up having 2 categories of the same components? Not cool!
Thanks to Corbin Dunn, I was told to make the value of the entry to the design
time assembly under HK_CurrentUserSoftwareBorlandBDS1.0Known IDE Assemblies
equal to “RunOnce”, then the IDE will delete the entry from the
registry after the first run. It worked perfect! Thanks Corbin!
The final problem was what happened when we added the registry entry to C#Builder,
started C#Builder and found the Elements(Ex) category with all of our components
installed (it was a happy moment ?) but when we shut down the IDE and brought
it back up again, no more Elements(Ex) category or components ? (Not a happy moment)
This is when Corbin helped one more time to let us know that calling “SaveState”
on the IOTAComponentInstallService causes the IDE to stream all of its ToolBox
to an XML file under C:Documents and Settings<UserName>Application
DataBorlandBDS1.0ApplicationSettings.xml and the IDE calls “LoadState”
every time it loads to loads the ToolBox state from that file.
// Tell the component install service to save its state so that
// our changes are persistent.
componentInstallService.SaveState();

Hope this information will save you some time in the future implementing similar
integration routine..
Till next article, have fun!
Falafel Software is all about making the most of software development technology
in order to complete the project on time and on budget with best possible user
experience. Falafel Software offers a comprehensive suite of software development
solutions ranging from strategy to design to implementation that businesses
need in order to realize high returns on their investment.


Copyright
) 2003 Alain Tadros, Falafel Software Inc.
ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT
THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR.
Connect with Us