Introduction

Quite often, developers need to plot various data. They expect to use a light control with minimal dependencies.

Background

NTGraph control is a powerful ActiveX control which plots multiple data sets. Unfortunately, it depends on MFC libraries.

This new 2D Graph ActiveX control, named DMGraph, is based on the NTGraph drawing engine but eliminates the MFC dependency. For DMGraph, ATL 3.0 was used as the framework. The only dependencies are some Microsoft Windows DLLs (the C runtime library msvcrt.dll is part of the OS starting with Windows 2000). This means there are no deployment issues - DMGraph works on Windows 2000 or later.

Another major change compared with old NTGraphCtrl is the exposed COM interface architecture. Instead of putting everything together under one interface, DMGraphCtrl exposes a hierarchy of interfaces which represents entities used on drawing.

Using the Code

The main interface IDMGraphCtrl contains collections of items (managed by the IDMGraphCollection interface). This collection interface exposes the usual methods (such as Add, Delete, Count, Item). What is specific is the concept of "selected item". One item in the collection can be the "selected" one. Sometimes user operations (such as mouse drag) apply to the "selected" item (if any). The IDMGraphCollection::Selected property gets/sets the index of the selected item.

When the user double clicks the graph area, a modal dialog with property pages is displayed. This dialog may be invoked programmatically as well using the ShowProperties method. Modifying data in these property pages has an immediate effect on the displayed graph.

The CDMGraphCtrl class implements the IDMGraphCtrl interface. At runtime, some properties can be viewed or changed using the DM Graph property page:

The CDMGraphCtrl class keeps the following collections exposed by the IDMGraphCtrl interface:

1. Element Collection

The get_Elements property exposes the elements collection.

Each item is an instance of the CGraphElement class which exposes the IDMGraphElement interface. A graph element is a collection of points which need to be plotted. The graph element has various properties which define its drawing style. For example, the Linetype property defines what kind of line should be used to connect the points (including "Null" - no lines at all). Color, width, shape can be set for points; the entire set of points can be enabled/disabled for drawing, etc. Each graph element is identified by a "name". All these are accessible through COM properties exposed by the IDMGraphElement interface. When such a property is set, the entire graph is re-drawn to reflect the changes.

The set of points (data to be plotted) is supplied by the client using several methods:

Plot - Two one-dimensional arrays with same size (one for X, the other for Y) will set the entire point collection for a specific graph element.

PlotXY - Appends just one point to the point collection (both X and Y coordinates are specified).

PlotY - Appends just one point to the point collection (only Y is specified, the X is the index of the added point in the points collection).

Each time the point collection is modified, the graph is updated to reflect the changes but the range is not updated. If the new point(s) go(es) out of range, then the SetRange or AutoRange methods need to be called.

New elements can be added to the collection, existing ones removed, selected element index can be changed, and selected element properties can be viewed/changed from the Elements property page.

2. Annotation Collection

The get_Annotations property exposes the annotations collection.

One annotation is a piece of text which is displayed on a specific position on the graph. This collection keeps instances of the CGraphAnnotation class which exposes the IDMGraphAnnotation interface. Using this interface, various properties can be accessed - such as caption (the displayed text), position, color, text orientation, background enable/disable. When such a property is set, the entire graph is re-drawn to reflect the changes.

New annotations can be added to the collection, existing ones removed, selected annotation index can be changed, and selected annotation properties can be viewed/changed from the Annotations property page.

3. Cursor Collection

The get_Cursors property exposes the cursors collection.

A cursor is made of one or two lines which are parallel with the X or Y axis. The IDMGraphCursor interface deals with cursor specific properties. If the Style property (type is the Crosshair enum) is set to "XY" then the cursor will have two lines: one parallel with X axis and the other parallel with Y axis. If the cursor Mode is set to Snap, then the selected cursor will snap to the closest point of the selected graph element during mouse drag.

New cursors can be added to a collection, existing ones removed, selected cursor index can be changed, and selected cursor properties can be viewed/changed from the Cursors property page.

4. Axis Objects

Two objects are exposed by the get_Axis property: one for X (horizontal) axis and the other for Y (vertical) axis. The objects are instances of the CGraphAxis class which exposes the IDMGraphAxis interface. Various properties can be get/set for each axis. If the put_Time property is set to VARIANT_TRUE, then the double values for that axis are considered to be date/time values. These values are interpreted like the DATE type (used in OLE Automation VARIANT union). The values are displayed according to the format string set by the Format property. For date/time, possible format strings are documented in the strftime function in MSDN. Otherwise, for non logarithmic axis, the usual sprintf format strings are accepted. Some axis properties are available in the DM Graph property sheet (see above) while others are available in the Format property page (see below).

From the Axis combo box, the X (bottom) or Y (left) axis can be selected. Then the data type can be set for the selected axis. For each type, the Templates list box is filled with the available format templates. When a template item is selected from the left, the Format string on the right is updated.

History

Added the "control" flag for the type library (as attribute in IDL). In this way, DMGraph is listed as an ActiveX in Visual Basic Component Control List / Excel Control Toolbox

Changed the DrawGraphOffScreen function to behave properly even if the supplied HDC is a metafile DC. This is useful when COM client invokes IDataObject::GetData method. The ATL implementation for this method uses a metafile DC.

Version 5.0.0.2

Added UI in DM Graph property page to allow grid step changes.

Version 5.0.0.1

Fixed a minor defect which raised an Assert in the 'Debug build' when the ActiveX was not in place active (example: when inserted into a dialog by the C++ resource editor).

Updated help file with MFC client sample code.

Version 5.0

MFC dependency is removed; the old .OCX library is now a .DLL using ATL. The DLL depends only on standard Windows libraries and on the standard C++ library (msvcrt.dll) which is part of the OS starting with Windows 2000. This means there are no deployment issues.

Major re-design in exposed COM interfaces. More interfaces were added. They group together methods and properties which apply to the same kind of objects. As a consequence, there is no backward compatibility with the COM exposed by the old NTGraphCtrl.ocx. Hence all GUIDs and ProgIDs are new.

The new DLL exposes dual interfaces (as opposed with the old dispinterfaces).

Fixed GDI and memory leaks when printing.

Added new API to pass an array of points using just one method call.

Added selective zoom (zoom just on X or just on Y).

Added a new tracking mode which displays in a tooltip the value of the closest point near the mouse.

1. About the way to describe a defect. The following information is expected: a. What COM methods/properties were invoked, in which order and what values have been passed?b. The steps followed by user in UI (mouse click, where, keyboard etc.)

I am using this software for myself having time set for x-axis and I did not have this error. Probably a. and b. above are different for me.

There is nothing special with this ActiveX control. If you know how to handle a standard ActiveX then it should not be a problem. I've just tested with VB6. Here are the basic steps:1. Add the DMGraph Ctrl in VB control toolbox (right click, choose "Components..." from context menu, check the box near 'DMGraph ActiveX Control module' in Controls tab).The control's icon should be added in toolbox to the end.2. Click on control's icon on toolbox. Then drag a rectangle on the form client area to specify the position for the ActiveX control in the form. When you finish this you should see the ActiveX added as a control in the VB form. 3. In properties window change the control name to dmGraphCtrl.3. Add a push button. Double click in the form to add/edit its "click" event handler.4. Copy an paste the code from above VB script sample inside the "click" event handler.5. Do minor adjustments to port from VBScript for Html page to VB6. Example: -remove Html specific: Dim dmGraphCtrl Set dmGraphCtrl = document.getElementById("DMGraphCtrl")

I've just searched in the orginal source code of NT Graph (just to doublecheck). Again, I did not see any code related to legend. The original code is available here, you can check yourself: http://www.codeproject.com/KB/miscctrl/ntgraph_activex/NTGraph_src.zip

Never mind, I figured, I forgot to set pDMGraphCtrl->PutElementIdentify

What would be nice though is to have a Legend coclass with frame, background, font, placement properties, etc. + align nicely the series captions. Right now the captions for multiple elements are all misalligned. In my case I wanted them left-aligned, so I ended up modifying in CDMGraphCtrl::DrawElementLabel() the following:

I found the cause and the fix. A new version 5.0.0.3 is awaiting approval to be published.These are the changes I did:

1.Corrected a registration error (wrong LIBID was used inside CLSID key)2.Added the "control" flag for the type library (as attribute in IDL). In this way DMGraph is listed as an ActiveX in Visual Basic Component Control List / Excel Control Toolbox.3.Changed the DrawGraphOffScreen function to behave properly even if the supplied HDC is a metafile DC. This is useful when COM client invokes IDataObject::GetData method. The ATL implementation for this method uses a metafile DC

Thanks for your feedback.

[08 March 2012]The version 5.0.0.3 which contains above changes is published now. Please download the source and/or binaries. The problem you mentioned should be gone.

This control accepts input data in two ways:1. The whole data set is provided as input parameter for IDMGraphElement::Plot. This method replaces all previously added points. 2. New points are added one by one; existing ones are preserved (see IDMGraphElement::PlotXY and IDMGraphElement::PlotY).

Which method you choose depend of your type of "live" data.

If you want to display new "live" data without wiping the already added one, then second way is the good one. While live data continues to arrive, the graph will be more and more crowded. At one point you might want to erase accumulated data (you may call Plot with an empty array).

If the newly arrived "live" data is coming in batches and you want to display one batch at a time then first way can be used. The graph will contain just the last batch of "live" data.

Do you plan to improve:- drawing axis with user selectable start value and step value (so the user could select e.g. X axis will start at value "2" and step of X-axis increment is "4"). This will lead to moving axes when panning the graph (insted of showing X axis starts at value 2.0231 ). THis is very useful especially when showing only date format on X axis - all values in one day are shown starting with the same day and is not possible to estimate if the value was measured at 6:00 12:30 or 21:00.- drawing ticks with subaxis (user could select step value of subaxis or number of regions in one region of main axis)?

1. User selectable step for "grid lines" (I think you were thinking to grid lines not to axes - we have just two axes: one X and other Y). The grid lines are dividing the range in 5 regions.

The code already has the API to change the step between grid lines. Use IDMGraphAxis::GridNumber method to get/set the step (default value is 5 as you noted). The property pages did not expose UI to get/set this property. The COM client can do it. Still, I added some new UI to allow access to this property from DM Graph property page as well (version 5.0.0.2).

2. The grid should stick with the graph when user is doing pan (eventually zoom as well ?).

No, the code was not designed to do that. With existing behavior user can use the grid lines to explore the graph values while doing zoom/pan. Personally I enjoyed the frozen grid lines: I used pan to drag the points over the grid lines to see their values. Well, in this case I think the code should keep the existing behavior. If you want to add alternate behavior then a new API should be added to switch between the two modes. This was not in the scope for this project. But since the source code is available, feel free to add & test the new code for this feature (if you need it).

Regarding second suggestion: what are the "subaxis" ? I do not see this term in the code. When user disables the grid, small ticks (with values and without values) are drawn on axes. The ticks follow the same logic as grid: they do not stick with points while zoom/pan.

The grid should stick with the graph when user is doing pan (eventually zoom as well ?).

No, the code was not designed to do that...feel free to add & test the new code for this feature

Yes, that was my original question, if you plan to do it (or have I to modify it myself). Because I plan to have date type of X-axis, I think the sticking graph with grid is better (start of the day still remain ticked). So I will try it myself.

Marius Samoila wrote:

what are the "subaxis" ?

Sorry for my English. With "subaxis" I mean ticks on axis inside grid lines. Eg. the grid is drawn with 1-day step and 3 ticks inside this step will help distinguish 6-hours intervals (quarters of day).

I work as a Sr. Software Engineer in Robert Bosch Engineering and Business Solutions Ltd in Bangalore, India.

We found your article about 2D Graph Control to be very useful for our current project which is an open source. Please refer the link below to know more about our OSS project:https://github.com/rbei-etas/busmaster[^]

Usage of your Graph Control will save us substantial efforts. We would like to know about the licencing policies that as the author, you attach to it. May we have your permission to use the source code in our open source product?