7.4 WorkItem hierarchy

The core element of CAB is the WorkItem16. It represents the container which manages all the objects instantiated by Dependency Injection. Figure 14 presents the WorkItem hierarchy of the Test Suite.

Figure 14: The WorkItem hierarchy of the TestSuite.
Figure 14: The WorkItem hierarchy of the TestSuite.

Typically, every CAB module provides its own WorkItem. The services registered by a module are available at the module WorkItem or any sub WorkItems. Only the services registered at the root WorkItem can be accessed in the whole application. Due to this fact, the modules FunctionGen.Driver, FunctionGen.ControlView and SignalVisualizer register their module WorkItems in the TestDevice.Manager WorkItem. Hence, these WorkItems can access the services provided by the TestDevice.Manager.

Sub WorkItems are often used to handle a part of the use case. In Figure 14 the Documents are such sub WorkItems. A Document WorkItem manages the lifecycle of a document and it controls the UI window which shows the content to the user. The other sub WorkItems seen in Figure 14 are the Device Instances. Each of them represents a test device.

7.5 Implementation of the requirements

This chapter describes how the requirements are implemented by using the Composite UI Application Block. For most of the requirements the prototype only shows one of the possible ways to solve them.

Configuration of the module loader

The configuration of the module loader is done in an XML file. The prototype is using the built-in dependency module loader which is shipped with the Smart Client Software Factory. It allows the definition of dependencies inside the XML file.

Additionally, the configuration via command line arguments of the application is required. The first idea was to pass all information of the XML configuration file as command line arguments. This idea is not practical because all the information in a single line is not readable any more. Therefore, a command line argument parser is implemented to let the user choose which XML configuration file should be used. The different configurations can be defined in XML files. If the user does not set the command line argument, it reads the default file ProfileCatalog.xml.

Isolate the objects under test

The prototype includes a unit test project for the TestDevice.Manager module. It is using the Visual Studio 2005 unit test framework. The project tests all non-UI classes of the module. These are the ModuleController, the TestDeviceManager and the TestDevicesViewPresenter class. Additionally, the UI-class TestDevicesView is partially tested. A complete test of an UI-class requires special tools or frameworks because the input devices like a mouse have to be simulated.

The notable aspect is that all of these classes are completely isolated for the tests. This means that all the needed objects of these classes are replaced by mock objects. The needed objects are mostly services from the framework but they can also be collaborators of the same module. Before a unit test runs, the framework is initialized with these mock objects. The mock objects can be used to test the correctness of the interaction between them and the object under test. The abstract class FixtureBase initializes the framework with the test configuration. All the test classes derive from FixtureBase and reuse the framework setup. The unit test writing can be done with minor effort by reusing the framework setup.

Lazy loading

The Composite UI Application Block is able to load services on demand. The prototype is using this feature for the DocumentManager. The DocumentManager uses the OpenFileDialog and the SaveFileDialog class which uses native resources. With performance in mind these classes are typically lazy instantiated to save resources if they are not used. The framework already implements service loading on demand. Therefore, a software developer does not need to care about lazy loading inside the services.

Modules deployment

The projects that are created with the Smart Client Software Factory in Visual Studio build all their files in the same directory. This may become problematic because the resource filenames could be identical with the ones of other modules. It is also possible that the modules use different versions of the same assembly. If all the files are deployed in one directory, the installation of a new module could overwrite files from other modules. The Test Suite uses customized build paths for deploying every module in a separate directory to prevent the mentioned issues (Figure 15). Furthermore, the module loader configuration files have to be adapted to the new path names.

Figure 15: File structure of the Test Suite.
Figure 15: File structure of the Test Suite.

Beside the Modules directory tree, the following directories are created:

By using these sub directories, the common language runtime (CLR) has to be told where it is going to find the assemblies. A way to accomplish this, is to add the probing element in the application configuration file as it is shown in Listing 13.

 1  <runtime>
 2    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
 3      <probing privatePath="Libraries;Infrastructure" />
 4    </assemblyBinding>
 5  </runtime>

Listing 13: Extract of the application configuration file of the Test Suite.

Support for GUI extensions

CAB supports GUI extensions in two ways. The first one is via the Workspaces. They are responsible to host the views of the modules. The Test Suite provides a DockPanelWorkspace to host views inside the shell form. Furthermore, it contains the FormWorkspace to host views in an own modal dialog. Both Workspaces are able to host Windows Forms controls and WPF controls. Due to this feature, a step-by-step migration of the older UI technology to the newer one is supported. All the modules of the Test Suite prototype use Windows Forms controls for their views except of the SignalVisualizer. This module implements all its views as pure WPF controls.

The second way to support GUI extensions is done through UIExtensionSites. The Infrastructure.Layout and the TestDevice.Manager module register UI elements as extension sites. The other modules are able to extend these UI elements. For example, the SignalVisualizer adds a new menu item into the drop down list of the View menu item which is defined by the Infrastructure.Layout. The help topics of CAB and the reference application of SCSF show how to use UIElementSites. In their examples, the modules create the concrete UI elements and add them to a UIExtensionSite. The problem is that the modules have to know which concrete UI element is behind the UIExtensionSite. For example, the Infrastructure.Layout registers a MenuStrip instance. All modules, which have to extend the MenuStrip, create ToolStripMenuItem elements and add them to the UIExtensionSite. If the MenuStrip in the Infrastructure.Layout module is replaced with another similar control, all the modules have to be modified.

The Test Suite prototype solves this problem with the IUIElementCreationService. The modules use this service to create the necessary UI elements. Thus, the modules do not have any dependency to the UI technology which is used in the Infrastructure.Layout module. If the UI technology is replaced in the layout module, just the IUIElementCreationService class has to be updated.

Command service

The Test Suite uses the built-in command system of CAB. It can handle all the needed requirements for most use cases. One of the exceptions is the handling of the edit functions cut, copy and paste. Here, the command should be routed to the active UI element only. In Windows-based applications the active UI element is the one which has the focus. Besides command routing, the command states are depending on the active UI element. If the active UI element does not support these commands or no elements are selected, the according commands have to be deactivated. In the Test Suite, the EditManager takes care for the special treatment of these commands.

Loosely coupled events

The Test Suite uses loosely coupled events for various reasons. The modules can use the CAB event broker to update the text shown in the application status bar. Moreover, the communication between the Presenter / WorkItemController and the Presenter / Presenter classes is done with the loosely coupled events. A special case is the transmission of the signal samples over the event broker. The SignalVisualizer registers for this event topic and displays the samples. The event handling has to be synchronized since the SignalVisualizer runs in the UI thread and the signal samples are created by other threads. CAB provides a simple solution for thread synchronization in the event handler as it is shown in Listing 14.

 1  [EventSubscription(EventTopicNames.Sample,
 2    Thread = ThreadOption.UserInterface)]
 3  public void GotSample(object sender, SampleEventArgs sample)
 4  {
 5    ...

Listing 14: Thread synchronization in the event handler.

The synchronization with the WPF control of the SignalVisualizer does not work properly. During the application shutdown a NullReferenceException is thrown in the System.Windows.Forms.Control.WaitForWaitHandle method. The exception is only thrown if a virtual function generator is not turned off before the application is closed. Even then, the exception does not occur every time. This is a typical behavior for threading issues. Therefore, the prototype does not use the synchronization functionality of the CAB event broker. Instead, the view synchronizes the method call manually.

 1  public void AddSample(object device, double amplitude,
 2      double relativeTime)
 3  {
 4    if (Dispatcher.CheckAccess())
 5    {
 6      InnerAddSample(device, amplitude, relativeTime);
 7    }
 8    else
 9    {
10      Dispatcher.Invoke(DispatcherPriority.Normal,
11        new AddSampleDelegate(InnerAddSample), device, amplitude,
12        elativeTime);
13    }
14  }
16  private void InnerAddSample(object device, double amplitude,
17      double relativeTime)
18  {
19    signalView.AddSample(device, relativeTime, amplitude);
20  }

Listing 15: Shows how thread synchronization can be done in WPF controls.

Listing 15 shows the manual thread synchronization. The code extract is part of the VisualizerView class. The Dispatcher.CheckAccess method verifies if the call needs to be synchronized. Dispatcher.Invoke calls the InnerAddSample method synchronized with the UI thread.

The communication between the function generator and the visualizer via loosely coupled events is just exemplarily. Typically, signal samples have to be processed in real time and not in the way it is done in the prototype. The event broker is to slow for processing signals with high frequencies. However, it is a good example why synchronization can be necessary in association with loosely coupled events.

7.6 Summary

The Test Suite implementation presented in this chapter handles all the requirements which are defined in chapter 2. Nevertheless, many requirements are already handled by the .NET Framework or the Composite UI Application Block. The CAB framework successfully reduced the effort to create the prototype application. However, the learning time for understanding the framework cannot be disregarded. CAB is very powerful through its abstract and flexible design but it is also highly complex. Microsoft has seen that the complexity of the framework is a problem for many users. Therefore, they introduced the Smart Client Software Factory. SCFS assists the software developer in common tasks and it is delivered with extended documentation. Nevertheless, it does not help much in learning the concepts of the key parts, the Object Builder and the WorkItem of CAB.

16 See also WorkItem (p. 20).