Introduction
The image seen above should be familiar to folks who have read, or glanced the cover of Edward Tufte's The Visual Display of Quantitative Information. It is a time series, showing the schedule of two routes of Santa Clara VTA's (Valley Transportation Authority) graphically. The image was generated by the first sample application (VTAView), which uses Timeline, a customizable, interactive, WinForms control that I wrote for displaying time series diagrams. Timeline can display static information known beforehand, like that of a timetable, as well as information that changes as time progresses.
ProcessMonitor, the second sample application, makes use of a timer and the Timeline control to create a dynamic time series diagram to show variation in resources used by a process across time. This article describes the design of the control and its usage in applications.
Design
The implementation of Timeline is based on the idea of a time window showing a section of the total time (world time). The time window shows the visible portion of the total time and all operations like scrolling and zooming work on the time window. Data items are added to the time series by providing a data provider to the control. The control manages the display of data items, depending on the current time window. This design allows the world time to be potentially infinite and this allows creation of time series diagrams that dynamically add information as time progresses. The class diagram below shows the classes and interfaces in the control.
The main class, Timeline, has the following properties:
The methods in the control are:
AddDataProvider : This is the single most important method in the class, taking an implementation of the ITimelineDataProvider interface as its parameter. The data provider is responsible for adding data items to the time series.
MoveTimeWindowLeft : Moves the time window to the left. The overload without a parameter moves it left by one timezone, (the time represented by the time zone depends on the current zoom level), and the other overload moves it left by the specified number of minutes. Dragging the mouse across the control also moves the time window.
MoveTimeWindowRight : Moves the time window to the right. Dragging the mouse across the control also moves the time window.
SetInitialTimeWindow : Sets the time window's starting time and the zoom factor to be applied (or the ending time) when the control is initialized for the first time.
SetTotalTimeWindow : Sets the world start and end time. The time window cannot be moved outside the world time.
ZoomIn : Decrements the zoom factor index by one, resulting in each time zone representing a smaller unit of time.
ZoomOut : Increments the zoom factor index by one, resulting in each time zone representing a larger unit of time.
Interfaces
Using the control requires implementation of the following interfaces:
ITimelineDataItem
ITimelineDataItem represents one data item in the time series. Timeline gets a list of ITimelineDataItems from the data provider and renders them on the screen if they lie within the current time window. In the image shown at the beginning of the article, each stop is a data item and is given to the control as a TrainStopItem class, implementing the ITimelineDataItem interface. The interface has three getters:
Axis : returns the axis that this data item belongs to. In the sample image, each train station is an axis
ItemStartTime : returns the starting time of this item
ItemEndTime: returns the ending time of this item
It has one method, Draw, that implementors use to generate the visual representation of the item. Here's how TrainStopItem's Draw method is implemented:
public void Draw(System.Drawing.Graphics g,
TimeToXCoordinateDelegate timeToLocationConverter, float yStart,
float height, DateTime currentWindowStart, DateTime currentWindowEnd)
{
float startX = timeToLocationConverter(start);
float endX = timeToLocationConverter(end);
float y = yStart + height / 2;
using (Pen p = new Pen(Color.Red, 2))
{
g.DrawLine(p, startX, y, endX, y);
}
using (SolidBrush brush = new SolidBrush(Color.DarkGray))
{
g.DrawString(this.end.ToString("HH:mm"), new Font("Calibri", 7),
new SolidBrush(Color.DarkGray), endX, y);
}
if (nextStop != null)
{
using (Pen p = new Pen(Color.Gray, 1))
{
g.DrawLine(p, endX, y, timeToLocationConverter
(nextStop.ItemStartTime), nextStop.Top);
}
}
}
ITimelineDataProvider
This interface is used to provide information about the axes and data items to Timeline. The interface consists of two events, AxisFound and TimelineItemFound, that implementors can fire to inform the control about the discovery of a new axis or a data item. In the VTAView sample application, MainForm implements the interface and fires the events when loading the schedule from disk.
The reason why the data provider has events, instead of methods like GetAxes and GetTimelineItems, is that in certain kinds of applications using the control, the list of items and axis will not be available statically. The second sample application, ProcessMonitor, is a good example of those kinds of applications. ProcessMonitor shows dynamically changing parameters like processor usage and handle count for a process, and informing Timeline about new data points is best achieved through events.
Putting it all together
Here's a step by step guide to using the control in an application:
- Write classes implementing
ITimelineItem to represent data items.
- Write one or more data provider classes implementing
ITimelineDataProvider. Use the AxisFound event to return instances of IAxis derived classes and the TimelineItemFound event to provide instances of ITimelineItem derived classes to the control.
- Drag and drop the
Timeline control onto a Form. In the Form class's InitializeComponent, use the SetTotalTimeWindow and SetInitialWindow methods to set the starting and ending times.
- Use the
AddDataProvider method to link data providers created in (2) with Timeline.
History
- Initial version - 8/26/2007