A Quick Recap
In my last post I started talking about the implementation of the UI for the CCTray replacement. I covered the main window, plus the splash and about windows. In this post I’ll start to delve into the details of the main window – especially how the various parts interact and how they are populated with data.
Data From the Source
All the data to be displayed comes from the controller, which in turn receives them from the communications channel. The channel has a MessageReceived event, which passes a CommunicationsMessageEventArgs instance. This contains a string telling the type of message, the id of the source and a object instance containing any additional data. To facilitate the understanding of messages I’ve also added a class called ServerCommands which contains a number of string constants for the different types of messages:
This class contains commands that will be passed in both directions on the channel. Since the controller is interested in what has changed on the server, it waits for the following commands:
- ProjectChanged
- QueueChanged
- ServerChanged
When any of these commands are received, it triggers the event of the same name on the controller class. It also converts the data object into an instance of the changed type (e.g. server/queue/project) and adds a status message so the user can know the item has changed.
Updating the Main Window
When the main window started up, it added listeners to the three change events on the controller. This handler class InvokeOnUIThread() to handle any cross-thread marshalling and then passes on the require to a method called UpdateItemDisplay(). This is where I need to side-track a bit.
When an item is first loaded, it generates an instance of an IItemDisplay. This interface has a method called UpdateDisplay(), which is responsible for actually updating what the user sees. This way the main window doesn’t need to know about all the various controls and components in itself. Additionally, each node in the server explorer is also associated with an IItemDisplay, which allows the same update mechanism to be used.
Each data object has a list of these IItemDisplay instance associated with it (these are held in a dictionary), and the main window provides access to these lists via the GetItemDisplayList(). When a new item is added, the class doing the add calls GetItemDisplayList() to retrieve the correct list and then adds the new item. The same process is also followed for removing an item. Then, when a update event is received and UpdateItemDisplay() is called, all it needs to do is iterate through this list and call UpdateDisplay() on each IItemDisplay.
To implement this, there are a number of different implementations of this interface. Some of these implementations are:
- ItemDisplayBase: an abstract implementation that provides some base details
- TreeNodeItemDisplay: a default implementation for a tree node, used mainly by server explorer implementations
- ServerTreeNodeItemDisplay: an updater for servers in the server explorer
- QueueTreeNodeItemDisplay: an updater for queues in the server explorer
- ProjectTreeNodeItemDisplay: an updater for projects in the server explorer
- ProjectListViewItemDisplay: an updater for for the projects display tab (more on this later)
- PropertyGridItemDisplay: an updater for the property grid display tab (more on this later)
Each implementation know the details of the item it is updating, and it knows which details to display – thus simplifying the code in the main window.
Selecting an Item
Since the display has been abstracted away, there needs to be a way to detect which item is currently selected. To complicate matters, my design currently allows for multiple items to be selected – thus raising the possibility that items on different servers could be selected.
To work around these issues I’ve added another interface called ISelectionControl:
This interface has two methods – one for first determining which processes have been selected, and then a second one for determining which projects have been selected for a process.
When a control on the form is activated (receives focus) the ISelectionControl instance is stored in the main window. Then, when a command is activated that requires a process/project selection the implementation is used.
The server explorer has a implementation called TreeViewSelection, which returns the process id and project name of the currently selected node. If the node is a display-only node (e.g. the holder node for Projects or Queues) then it goes up the tree until it finds a node that is a non-display-only node. Some of the display tabs also have their own implementations.
That covers the displaying of data and the selection of projects, the final area I’ll cover in this post is the display tabs.
Customising the Details
In the main window I have added an area for display tabs. These will replace the projects view area in the current CCTray. While this will offer similar functionality, it also allows the replacement to display a lot more details. For example, as well as a projects list display I have implemented a details display (using the PropertyGrid control) and a queues list.
All display tabs must implement IDisplayTab:
The Initialise() method is called when the display tab is first added to the window. This will generate and return the actual control to be added (the main window then adds it to the tab control and associated any required event handlers). When the selected item in the server explorer changes, the DisplayItem() or ClearItem() methods are called – these are resposible for changed the display (and generating any IItemDisplays as required).
The Text property is the title of the tab – this is only used when the tab is first added. The MainImageList provides a way of hooking the ImageList on the main window into the display tab (an attempt to reduce memory usage and allow common images). Finally the Selector property an ISelectionControl - which can then be used to select processes and projects. If the display tab doesn’t allow selections, then this property should return null.
I’ve already produced three implementations:
- DetailsDisplayTab: displays the details on the currently selected item using a PropertyGrid
- ProjectListDisplayTab: displays a list of projects for the currently selected item
- QueueListDisplayTab: displays a list of queues for the currently selected item
These have their own associated implementations of IItemDisplay and ISelectionControl.
What’s Next?
This post and the previous have covered most of the details on the main user interface. The next area I’ll cover is how to configure the application. This will also involve some UI windows, but also require examining classes in the other components of the applications.
Stay tuned…
RSS - Posts













