This article looks at foundational modules briefly, and also discusses the pattern for handling names in the SCSF.
In part 18 of this series of articles we saw how we can use one of the Guidance Automation Packages to add a business module to our solution. There’s also a Guidance Automation package that lets us add a ‘foundational module’ to our solution. This is on the same submenu as the ‘Add Business Module (C#)’ option we saw above. So to add a foundational module to our solution right-click the solution folder in Solution Explorer, select Smart Client Factory/Add Foundational Module (C#) and then click through the two setup screens.
A foundational module is identical to a business module except that it does not have a root WorkItem. This is because it is intended to contain supporting functionality rather than core business functionality for our solution. It is not expected that we will create business objects and add them to the various WorkItem collections.
So we are expected to create fairly generic services and supporting code in foundational modules, rather than business code. Of course we could do this in the Infrastructure projects mentioned in part 18, but a foundational module allows us to separate out supporting code into its own CAB module.
Note that we can create an interface component, ‘Module.Interface’, for our foundational module in exactly the same way as for a business module. This allows other components in the solution to use the module’s functionality without referencing it directly, as described above.
In our examples above we have seen several Constants folders being set up. The main Smart Client solution has a Constants folder in both the Infrastructure.Interface component and the Shell component. Both the foundational modules and the business modules have Constants folders in both their main Module components and their Module.Interface components.
The Constants folders all contain four classes: CommandNames, EventTopicNames, UIExtensionSiteNames, and WorkspaceNames. In the Constants folders mentioned above most of these are empty by default, although Infrastructure.Interface has some constants set up in its classes.
The important thing to notice here is that the individual classes with the same name are arranged in an inheritance hierarchy. So if we have a business module in our Smart Client solution (as in the code example we have already seen) then CommandNames in the Module itself inherits from CommandNames in Module.Interface, which in turn inherits from CommandNames in Infrastructure.Interface. CommandNames in Shell also inherits from Infrastructure.Interface.
The reason these classes exist is to allow us to use standard constants throughout our solution for names, rather than having to use strings. The inheritance hierarchy lets us define these constants at the correct level in the hierarchy, but then to use any of them very simply by just accessing the class at the level the code is at.
The reason we don’t want to use strings in our code as names is they are prone to error in entry (since we can’t use intellisense) and can’t be checked by the compiler at compile-time: if we enter a string name wrongly we will get a run-time error. If we use constants to represent these strings we avoid both of these problems.
This will be clearer in an example:
We might call a Workspace on our Shell form “LeftWorkspace” when we add it to the Workspaces collection of the root WorkItem. Elsewhere in the code we may want to retrieve that workspace and interact with it, for example to call the Workspace’s Show method to display a SmartPart. Normally to do this the syntax would be, for example:
The obvious difficulty with this is that we are just entering the name “LeftWorkspace” as a string, which is prone to error and the compiler can’t check.
So we add the code below to the WorkspaceNames class in Infrastructure.Interface. We add it to the Infrastructure.Interface component because the Workspace is being defined in the Infrastructure part of the solution, but we want it to be available outside of that:
public const string LeftWorkspace = "LeftWorkspace";
Now suppose we want to use this Workspace name in code in a business module. The WorkspaceNames class in the business module inherits from the WorkspaceNames class in Infrastructure.Interface, and hence the constant is available in that class. All we need do is reference that class to access any Workspace name. So we just import the appropriate namespace:
And then we can do:
Now intellisense is available when we enter the ‘LeftWorkspace’ name, and the compiler can check that what we have entered is correct.
Note that if we have a Workspace name defined just for the module (say ‘LocalWorkspace’) we can still just do WorkspaceNames.LocalWorkspace to access it.
So these Constants folders provide us with an easy way of using named constants for items in the WorkItem hierarchy throughout our code.