WPF/CAB Shell is a sample that is intended to serve as a starting point for many WPF/CAB applications. It is not meant to be a 100% fit for all applications. You may need to supplement it or tailor it to your particular needs. However, it will almost certainly
have something of value to you if you're writing a WPF application that leverages CAB.
Primarily, it provides a plugin system and a number of services that are typical for most LOB applications. The key goal of the WPF/CAB Shell is to reduce the amount of work necessary for module developers to integrate successfully with the shell.
The WPF/CAB Shell sample can be found under the Samples
folder in the SCSFContrib source download.
The backbone of the WPF/CAB Shell is its plugin design. If you're familiar with CAB, you're probably aware of its ability to dynamically enumerate and load modules. The plugin system provided with the WPF/CAB Shell extends this notion and provides additional
functionality on top.
All DLLs under the Plugins
directory in the application deployment directory will be treated as plugins. Directories are searched recursively, so you can use any naming scheme you like for directories under the
directory. It also means that you can deploy multiple versions of the same plugin.
Plugins in the WPF/CAB Shell come in two basic varieties:
- Implicitly trusted
Implicitly trusted plugins are those that have been signed with a special key. These plugins will be automatically loaded by the shell if they are found. Untrusted plugins are those that are not signed with the special key. These plugins are not loaded by default,
and will only be loaded if the user explicitly indicates that they would like to do so.
The key used to sign trusted plugins is included in the download and is called
, but you should generate your own. Otherwise, anyone will have the ability to create implicitly trusted plugins for your application. To change it, simply use the
SDK tool and replace the TrustedModule.snk
file, and then update the
field in the source code.
The WPF/CAB Shell provides an options pane that allows users to view and action plugin changes, pictured below:
The plugin pane provides a list of all plugins, grouped by their basic type (implicitly trusted and untrusted) and sorted by name. Each plugin has its name, description, version and path displayed. Untrusted plugins have an additional item of information called
status, which indicates whether the plugin is currently loaded or not.
In order to customize a plugin's name, description and version, you simply use assembly attributes. Please see the
for any of the plugins that come with the shell for an example.
WPF/CAB Shell includes a number of plugins, most of which are aimed at demonstrating the various services provided by the shell.
WPF/CAB Shell provides a number of useful services that allow module developers to more quickly and effectively do their job. These services are discussed in this section, but you should always bear in mind that they may need to be supplemented or tailored
according to the requirements of your application.
The command service provides a means of obtaining an instance of
. Instances of this type are used throughout the shell to represent commands. For example, when adding a tool bar item you will need an instance of this
CAB itself ships with command support, but it is limited compared to WPF's command support. Therefore, the shell provides its own abstraction over commands that encompasses both CAB and WPF commands. The command service allows you to easily wrap a CAB or
WPF command in an instance of WPFCABShell.Shell.Interface.Services.Command.ICommand
Once you have an ICommand
instance, you can use it when creating menu items or tool bar items.
The menu service allows menu items to be added to menu extensibility points in the application, including the main menu and context menus. It does so in such a way that the caller has no dependency on the actual UI widgets in use. This means you could change
the UI widgets being used by the shell without affecting any of your plugins.
One of the major features of the menu service is automatic grouping and sorting. When you add menu items with the menu service, you can optionally specify a group index and a regular index. Items in the same group are kept together, and are separated from other
groups by instances of System.Windows.Controls.Separator
. Within a group, the regular index (or, simply, index) is used to order the items. For example, the default Edit menu of the shell uses grouping and sorting to arrange
its menu items:
Other intelligence is provided by the menu service. For example, a menu item that is not associated with a command is presumed to be a container for other menu items. Therefore, it is only visible if it has one or more visible child menu items.
Tool Bar Service
The tool bar service allows you to add tool bar items to either an existing or a new tool bar. If you add an item to a tool bar that does not yet exist, the service will automatically add the tool bar for you. Similarly, if you remove the last item from a tool
bar, the service will automatically remove the tool bar itself.
The same grouping and sorting functionality provided with the menu service is also provided with the tool bar service. For example, the Edit tool bar uses grouping and sorting to arrange its tool bar items:
In addition, the shell integrates menu support to allow users to show and hide tool bars:
Of course, this is all automatic from the perspective of the module developer. The module developer need only use the tool bar service and this functionality will be provided for free.
Status Bar Service
The status bar service mediates access to the application status bar, which is a piece of screen real estate that must be shared between many application components. It does this via a prioritization mechanism. Status bar operations are assigned one of three
priorities when they are created:
- Normal (the default)
High priority messages trump normal priority, which trumps low priority. Operations of the same priority are ordered by creation time - newer operations trump older ones.
Status bar operations come in two basic varieties:
- Message operations
- Progress operations
The status bar provides three message operation locations:
The primary location supports text only, whilst the secondary and ternary support both text and images. An example of a primary message operation is the "Ready" text that is there by default:
An example of a secondary message operation is:
The other form of operations - progress operations - are just as simple to use as message operations. They can be used to show the progress of an application function. They can be determinate:
Or they can be indeterminate:
The settings service provides a simple API that modules can use to hydrate and dehydrate simple user settings. For example, the Shortcuts plugin uses this service to persist information about the shortcuts set up by the user.
The options service provides a means for plugins to add panes to the options window. In other words, the options window itself is extensible and dynamic. The code required to integrate with this window is minimal. Plugins need only provide a serializable data
class (used to persist settings), and a view (a.k.a. pane) that allows users to edit those settings.
The skinning service provides a means for skins to be registered with the shell, and allows users to switch between those skins. An example skin is included with the WPF/CAB Shell. As an example of the power of skinning, here is the Plugins options pane prior
And here is that same pane after the user has selected the Bellucci skin:
When a skin is registered via the skinning service, it is made available via the
Visualizers are an important developer aid when building a CAB application. WPF/CAB Shell comes with visualizers that help developing an application that is based on it. You may need to modify the shell's
file to enable the visualizers.
The command visualizer is pictured below:
It consists of two areas of information:
- Registered commands
- Command execution history
Registered commands are those commands that have been registered via the command service, as opposed to being created but not registered. Registered commands are commands that are likely to be useful for the user to access as shortcuts throughout the application.
In the registered commands section, the command visualizer shows additional information about the command including an execution count and last execution time stamp.
The command execution history section shows a running list of command executions - both registered and unregistered commands. Each time a command is executed in the application, an entry will be added to this list.
The log visualizer is pictured below:
It consists of two sections
The logs section lists all available logs and allows you to choose what level of logging is desired for that log. The log output section shows all output from logs and is filtered according to the selected log level.
In order to have your log output appear in this visualizer, simply use the
class. For example:
public class MyClass
private static readonly Log _log = Log.CreateForType(typeof(MyClass));
public void Save()