QCChart2DNetFAQs
Frequently Asked Questions QCChart2D for .NetFAQs2. How do you create a chart with multiple coordinate systems and axes? 4. How do you zoom charts that use multiple coordinate systems? 6. How do you handle missing data points in a chart? 7. How do you update a chart in real-time? 8. How do I prevent flicker when updating my charts on real-time? 9. How do you implement drill down, or data tooltips in a chart? 10. I do not want to my graph to auto-scale. How do I setup the graph axes for a specific range? 12. When I use the auto-scale and auto-axis routines my semi-log chart has the logarithmic axis scaled using powers of 10 (1, 10,100, 1000, etc.) as the starting and ending values, or as the major tick interval for labeling. How do I make my log graphs start at 20 and end at 50,000, with major tick marks at 20, 200, 2000 and 20000? 13. How do I create and use custom, multi-line string labels as the axis labels for my graph? 14. How do I place more than one graph in a view? 15. How do I use your software to generate GIF files? 16. Sometimes the major tick marks of an axis are missing the associated tick mark label ? 17. How do I change the order the chart objects are drawn? For example, I want one of my grid objects to be drawn under the charts line plot objects, and another grid object to be drawn top of the charts line plot objects. 18. How to I use a Forms scrollbar object to control horizontal scrolling of the data in my chart? 20. How do I get data from my database into a chart? 21. How do I use this charting software to generate chart images “on-the-fly”? 22. Can QCChart2D for .Net be used to create programs that run like Java applets in web browsers? 23. Are you going to add additional real-time routines to the QCChart2D for .Net library? 24. Are you going to add 3D routines to the QCChart2D for .Net library? 25. Can QCChart2D for .Net be used with Managed C++ (MC++) ? 26. Can QCChart2D for .Net be used to create SPC (Statistical Process Control) charts?
No, the QCChart2D for .Net software is not backward compatible with earlier Quinn-Curtis products. It was developed explicitly for the new .Net programming object oriented programming framework. You should have no problems recreating any charts that you created using our older Windows software; in most cases it will take far fewer lines of code.
A chart can have as many coordinate systems and axes as you want. A single coordinate system can have one or more x- and/or y-axes. The most common use for multiple axes in a single coordinate system is to place y-axes on both the left and the right sides of a chart, and x-axes above and below. The left and bottom axes usually have numeric or date labels, and the top and right axes just tick marks. This does not have to be the case though; every axis can have axis labels if you want. In general, the axis position in the chart is determined by its intercept. The default value of the intercept is set to the minimums of the coordinate system that the axis is placed in. Adjusting the intercept using the SetAxisIntercept method changes the position of the axis in the chart. The axis intercept value is set using units of the coordinate system at right angles to the axis. The example below, extracted from the LineFill example, places y-axes on both the left and right of the chart. [C#] TimeAxis xAxis = new TimeAxis(pTransform1); chartVu.AddChartObject(xAxis);
TimeAxis xAxis = new TimeAxis(pTransform1); xAxis.SetColor(Color.White); chartVu.AddChartObject(xAxis);
LinearAxis yAxis = new LinearAxis(pTransform1, ChartObj.Y_AXIS); // Default places y-axis at miniumum of x-coordinate scale yAxis.SetColor(Color.White); chartVu.AddChartObject(yAxis);
LinearAxis yAxis2 = new LinearAxis(pTransform1, ChartObj.Y_AXIS); yAxis2.SetAxisIntercept(xAxis.GetAxisMax()); yAxis2.SetAxisTickDir(ChartObj.AXIS_MAX); yAxis2.SetColor(Color.White); chartVu.AddChartObject(yAxis2);
[VB] Dim xAxis As New TimeAxis(pTransform1) xAxis.SetColor(Color.White) chartVu.AddChartObject(xAxis)
Dim yAxis As New LinearAxis(pTransform1, ChartObj.Y_AXIS) ‘ Default places y-axis at miniumum of x-coordinate scale yAxis.SetColor(Color.White) chartVu.AddChartObject(yAxis)
Dim yAxis2 As New LinearAxis(pTransform1, ChartObj.Y_AXIS) yAxis2.SetAxisIntercept(xAxis.GetAxisMax()) yAxis2.SetAxisTickDir(ChartObj.AXIS_MAX) yAxis2.SetColor(Color.White) chartVu.AddChartObject(yAxis2)
The other common reason to have multiple axes in a chart is to delineate the simultaneous use of different coordinate systems in the chart. In this case each coordinate system has an x- and/or y-axis to differentiate it from the other coordinate systems. When the different coordinate systems are created, they usually overlay the same area of the chart. The default positioning of the axes for each coordinate system will all overlay one another, making the axes unreadable. In the y-axis case you will want to offset additional axes to the left, or to the right of the default axis position, using the SetAxisIntecept method. When using the SetAxisIntercept method, make sure you specify the position using the units of the coordinate system scale at right angles to the axis. Specify an intercept value outside of the normal scale range to offset the axes so that they do not overlap. The example below, extracted from the MultiAxes example, creates one x-axis, common to all of the charts because the x-scaling for all of the coordinate systems match, and five y-axes, one for each of the five different coordinate systems. [C#] CartesianCoordinates pTransform1; CartesianCoordinates pTransform2; CartesianCoordinates pTransform3; CartesianCoordinates pTransform4; CartesianCoordinates pTransform5; . . // Initialize datasets, coordinate system ranges . // The x-scale range for pTransform1 to pTransform5 are all the same, 0 – 100 // The y-scale range for pTransform1 to pTransform5 are all different
// The plotting area for each pTransform is indentical, leaving a large open // to the left for extra axes. pTransform1.SetGraphBorderDiagonal(0.35, .15, .9, 0.65) ; pTransform2.SetGraphBorderDiagonal(0.35, .15, .9, 0.65) ; pTransform3.SetGraphBorderDiagonal(0.35, .15, .9, 0.65) ; pTransform4.SetGraphBorderDiagonal(0.35, .15, .9, 0.65) ; pTransform5.SetGraphBorderDiagonal(0.35, .15, .9, 0.65) ;
ChartAttribute attrib1 = new ChartAttribute (Color.Blue, 2,DashStyle.Solid); ChartAttribute attrib2 = new ChartAttribute (Color.Red, 2,DashStyle.Solid); ChartAttribute attrib3 = new ChartAttribute (Color.Green, 2,DashStyle.Solid); ChartAttribute attrib4 = new ChartAttribute (Color.Orange, 2,DashStyle.Solid); ChartAttribute attrib5 = new ChartAttribute (Color.Magenta, 2,DashStyle.Solid);
xAxis = new LinearAxis(pTransform1, ChartObj.X_AXIS); xAxis.SetLineWidth(2); chartVu.AddChartObject(xAxis);
yAxis1 = new LinearAxis(pTransform1, ChartObj.Y_AXIS); yAxis1.SetAxisIntercept(0.0); yAxis1.SetChartObjAttributes(attrib1); // axis color matches line color chartVu.AddChartObject(yAxis1);
yAxis2 = new LinearAxis(pTransform2, ChartObj.Y_AXIS); yAxis2.SetAxisIntercept(-18); yAxis2.SetChartObjAttributes(attrib2); // axis color matches line color chartVu.AddChartObject(yAxis2);
yAxis3 = new LinearAxis(pTransform3, ChartObj.Y_AXIS); yAxis3.SetAxisIntercept(-35); yAxis3.SetChartObjAttributes(attrib3); // axis color matches line color chartVu.AddChartObject(yAxis3);
yAxis4 = new LinearAxis(pTransform4, ChartObj.Y_AXIS); yAxis4.SetAxisIntercept(-52); yAxis4.SetChartObjAttributes(attrib4); // axis color matches line color chartVu.AddChartObject(yAxis4);
yAxis5 = new LinearAxis(pTransform5, ChartObj.Y_AXIS); yAxis5.SetAxisIntercept(xAxis.GetAxisMax()); yAxis5.SetAxisTickDir(ChartObj.AXIS_MAX); yAxis5.SetChartObjAttributes(attrib5); // axis color matches line color chartVu.AddChartObject(yAxis5);
NumericAxisLabels xAxisLab = new NumericAxisLabels(xAxis); xAxisLab.SetTextFont(theFont); chartVu.AddChartObject(xAxisLab);
NumericAxisLabels yAxisLab1 = new NumericAxisLabels(yAxis1); yAxisLab1.SetTextFont(theFont); yAxisLab1.SetAxisLabelsFormat(ChartObj.BUSINESSFORMAT); chartVu.AddChartObject(yAxisLab1);
NumericAxisLabels yAxisLab2 = new NumericAxisLabels(yAxis2); yAxisLab2.SetTextFont(theFont); chartVu.AddChartObject(yAxisLab2);
NumericAxisLabels yAxisLab3 = new NumericAxisLabels(yAxis3); yAxisLab3.SetTextFont(theFont); chartVu.AddChartObject(yAxisLab3);
NumericAxisLabels yAxisLab4 = new NumericAxisLabels(yAxis4); yAxisLab4.SetTextFont(theFont); chartVu.AddChartObject(yAxisLab4);
NumericAxisLabels yAxisLab5 = new NumericAxisLabels(yAxis5); yAxisLab5.SetTextFont(theFont); chartVu.AddChartObject(yAxisLab5);
Font axisTitleFont = new Font(“SansSerif”, 10, FontStyle.Bold); AxisTitle xaxistitle = new AxisTitle( xAxis, axisTitleFont, “Event Partition”); chartVu.AddChartObject(xaxistitle);
Grid xgrid = new Grid(xAxis, yAxis1,ChartObj.X_AXIS, ChartObj.GRID_MAJOR); chartVu.AddChartObject(xgrid);
SimpleLinePlot thePlot1 = new SimpleLinePlot(pTransform1, Dataset1, attrib1); chartVu.AddChartObject(thePlot1);
SimpleLinePlot thePlot2 = new SimpleLinePlot(pTransform2, Dataset2, attrib2); chartVu.AddChartObject(thePlot2);
SimpleLinePlot thePlot3 = new SimpleLinePlot(pTransform3, Dataset3, attrib3); chartVu.AddChartObject(thePlot3);
SimpleLinePlot thePlot4 = new SimpleLinePlot(pTransform4, Dataset4, attrib4); chartVu.AddChartObject(thePlot4);
SimpleLinePlot thePlot5 = new SimpleLinePlot(pTransform5, Dataset5, attrib5); chartVu.AddChartObject(thePlot5);
[VB] Dim pTransform1 As CartesianCoordinates Dim pTransform2 As CartesianCoordinates Dim pTransform3 As CartesianCoordinates Dim pTransform4 As CartesianCoordinates Dim pTransform5 As CartesianCoordinates Dim xAxis As LinearAxis Dim yAxis1 As LinearAxis Dim yAxis2 As LinearAxis Dim yAxis3 As LinearAxis Dim yAxis4 As LinearAxis Dim yAxis5 As LinearAxis . . ‘ Initialize datasets, coordinate system ranges . ‘ The x-scale range for pTransform1 to pTransform5 are all the same, 0 – 100 ‘ The y-scale range for pTransform1 to pTransform5 are all different
pTransform1.SetGraphBorderDiagonal(0.35, 0.15, 0.9, 0.65) pTransform2.SetGraphBorderDiagonal(0.35, 0.15, 0.9, 0.65) pTransform3.SetGraphBorderDiagonal(0.35, 0.15, 0.9, 0.65) pTransform4.SetGraphBorderDiagonal(0.35, 0.15, 0.9, 0.65) pTransform5.SetGraphBorderDiagonal(0.35, 0.15, 0.9, 0.65)
Dim background As New Background(pTransform1, ChartObj.GRAPH_BACKGROUND, Color.White) chartVu.AddChartObject(background)
Dim attrib1 As New ChartAttribute(Color.Blue, 2, DashStyle.Solid) Dim attrib2 As New ChartAttribute(Color.Red, 2, DashStyle.Solid) Dim attrib3 As New ChartAttribute(Color.Green, 2, DashStyle.Solid) Dim attrib4 As New ChartAttribute(Color.Orange, 2, DashStyle.Solid) Dim attrib5 As New ChartAttribute(Color.Magenta, 2, DashStyle.Solid)
xAxis = New LinearAxis(pTransform1, ChartObj.X_AXIS) xAxis.SetLineWidth(2) chartVu.AddChartObject(xAxis)
yAxis1 = New LinearAxis(pTransform1, ChartObj.Y_AXIS) yAxis1.SetAxisIntercept(0.0) yAxis1.SetChartObjAttributes(attrib1) ‘ axis color matches line color chartVu.AddChartObject(yAxis1)
yAxis2 = New LinearAxis(pTransform2, ChartObj.Y_AXIS) yAxis2.SetAxisIntercept(-18) yAxis2.SetChartObjAttributes(attrib2) ‘ axis color matches line color chartVu.AddChartObject(yAxis2)
yAxis3 = New LinearAxis(pTransform3, ChartObj.Y_AXIS) yAxis3.SetAxisIntercept(-35) yAxis3.SetChartObjAttributes(attrib3) ‘ axis color matches line color chartVu.AddChartObject(yAxis3)
yAxis4 = New LinearAxis(pTransform4, ChartObj.Y_AXIS) yAxis4.SetAxisIntercept(-52) yAxis4.SetChartObjAttributes(attrib4) ‘ axis color matches line color chartVu.AddChartObject(yAxis4)
yAxis5 = New LinearAxis(pTransform5, ChartObj.Y_AXIS) yAxis5.SetAxisIntercept(xAxis.GetAxisMax()) yAxis5.SetAxisTickDir(ChartObj.AXIS_MAX) yAxis5.SetChartObjAttributes(attrib5) ‘ axis color matches line color chartVu.AddChartObject(yAxis5)
Dim xAxisLab As New NumericAxisLabels(xAxis) xAxisLab.SetTextFont(theFont) chartVu.AddChartObject(xAxisLab)
Dim yAxisLab1 As New NumericAxisLabels(yAxis1) yAxisLab1.SetTextFont(theFont) yAxisLab1.SetAxisLabelsFormat(ChartObj.BUSINESSFORMAT) chartVu.AddChartObject(yAxisLab1)
Dim yAxisLab2 As New NumericAxisLabels(yAxis2) yAxisLab2.SetTextFont(theFont) chartVu.AddChartObject(yAxisLab2)
Dim yAxisLab3 As New NumericAxisLabels(yAxis3) yAxisLab3.SetTextFont(theFont) chartVu.AddChartObject(yAxisLab3)
Dim yAxisLab4 As New NumericAxisLabels(yAxis4) yAxisLab4.SetTextFont(theFont) chartVu.AddChartObject(yAxisLab4)
Dim yAxisLab5 As New NumericAxisLabels(yAxis5) yAxisLab5.SetTextFont(theFont) chartVu.AddChartObject(yAxisLab5)
Dim axisTitleFont As New Font(“SansSerif”, 10, FontStyle.Bold) Dim xaxistitle As New AxisTitle(xAxis, axisTitleFont, “Event Partition”) chartVu.AddChartObject(xaxistitle)
Dim xgrid As New Grid(xAxis, yAxis1, ChartObj.X_AXIS, ChartObj.GRID_MAJOR) chartVu.AddChartObject(xgrid)
Dim thePlot1 As New SimpleLinePlot(pTransform1, Dataset1, attrib1) chartVu.AddChartObject(thePlot1)
Dim thePlot2 As New SimpleLinePlot(pTransform2, Dataset2, attrib2) chartVu.AddChartObject(thePlot2)
Dim thePlot3 As New SimpleLinePlot(pTransform3, Dataset3, attrib3) chartVu.AddChartObject(thePlot3)
Dim thePlot4 As New SimpleLinePlot(pTransform4, Dataset4, attrib4) chartVu.AddChartObject(thePlot4)
Dim thePlot5 As New SimpleLinePlot(pTransform5, Dataset5, attrib5) chartVu.AddChartObject(thePlot5)
3. Can I add new axes, text objects, plot objects, and images to a chart after it is already displayed; or must I create a new chart from scratch taking into account the additional objects? There are two ways to add new objects to a chart. The first way is to create all objects when the chart is initially created, but disable the ones that you do not want to show up when the chart is initially rendered. Enable the objects when you want them to show up. Use the chart objects SetChartObjEnable method to enable/disable the object. This is useful if you are creating an animated chart where you want the chart to sequence through a predefined series of steps. The second way you add new chart objects to the ChartView using the ChartView.AddChartObject method. In both cases you need to call the ChartView.UpdateDraw() method after any changes are made. The example below, extracted from the the CustomChartDataCursor class, creates a new Marker object and NumericLabel object each time a mouse button clicked. [C#] Marker amarker = new Marker(GetChartObjScale(), MARKER_BOX, nearestPoint.GetX(), nearestPoint.GetY(), 10.0, PHYS_POS); chartVu.AddChartObject(amarker); rNumericLabelCntr += 1.0; // Add a numeric label the identifies the marker pointLabel = new NumericLabel(GetChartObjScale(), textCoordsFont, rNumericLabelCntr, nearestPoint.GetX(), nearestPoint.GetY(), PHYS_POS, DECIMALFORMAT, 0); // Nudge text to the right and up so that it does not write over marker pointLabel.SetTextNudge(5,-5); chartVu.AddChartObject(pointLabel); chartVu.UpdateDraw();
[VB] Dim amarker As New Marker(GetChartObjScale(), MARKER_BOX, nearestPoint.GetX(), nearestPoint.GetY(), 10.0, PHYS_POS) chartview.AddChartObject(amarker) rNumericLabelCntr += 1.0 ‘ Add a numeric label the identifies the marker pointLabel = New NumericLabel(GetChartObjScale(), textCoordsFont, rNumericLabelCntr, nearestPoint.GetX(), nearestPoint.GetY(), PHYS_POS, DECIMALFORMAT, 0) ‘ Nudge text to the right and up so that it does not write over marker pointLabel.SetTextNudge(5, -5) chartview.AddChartObject(pointLabel) chartview.UpdateDraw() The ChartZoom class will zoom one or more simultaneous coordinate systems. The example program SuperZoom zooms a chart that has one x-axis and five y-axes. Use the ChartZoom constructor that accepts an array of coordinate system objects.
The QCChart2D for .Net package does not include predefined dialogs for editing chart object properties. The look, feel and details of such dialogs are application specific and it is up to the application programmer to provide these. The property editor tables common to many packages are designed to be used by developers, not end users. You can add your own dialogs that edit the characteristics important to your end users. If you want to select the chart object by pressing a mouse button while the cursor is on the object, use the FindObj class. Override the OnMouseDown method and invoke the appropriate dialog panel there. The following example is extracted from the EditChartExample example program. [C#] public class LinePlot : EditChartExample.UserChartControl1 { private System.ComponentModel.IContainer components = null; public TimeAxis xAxis = null; public LinearAxis yAxis = null; class CustomFindObj: FindObj { LinePlot currentObj = null; public CustomFindObj(LinePlot component): base(component) { currentObj = component; }
public void InvokeLineDialog(GraphObj graphobj) { EditLineDialog linedialog = null; linedialog = new EditLineDialog(graphobj); if (liedialog.ShowDialog(this.GetChartObjComponent()) ==
DialogResult.OK ) { selectedObj.SetColor( linedialog.GetLineColor()); selectedObj.SetLineStyle(linedialog.GetLineStyle()); selectedObj.SetLineWidth(linedialog.GetLineWidth()); } }
public void InvokeTextDialog(ChartText textobj) { EditTextDialog textdialog = null; textdialog = new EditTextDialog(textobj); if (textdialog.ShowDialog(this.GetChartObjComponent())== DialogResult.OK ) { textobj.SetTextString(textdialog.GetString()); textobj.SetTextFont(textdialog.GetFont()); } }
public override void OnMouseDown (MouseEventArgs mouseevent) { base.OnMouseDown(mouseevent); GraphObj selectedObj = GetSelectedObject(); if (selectedObj != null) { // Check for a specific object if ( (selectedObj == currentObj.xAxis) || (selectedObj == currentObj.yAxis) || // or check for for all classes inheriting from a specific type (ChartSupport.IsKindOf(selectedObj,”SimpleLinePlot”)) || // or Check for a specific object type ChartSupport.IsType(selectedObj,”com.quinncurtis.chart2dnet.Grid”))) { InvokeLineDialog(selectedObj); } else if (ChartSupport.IsKindOf(selectedObj,”ChartText”)) InvokeTextDialog((ChartText)selectedObj); this.GetChartObjComponent().UpdateDraw(); } } }
[Visual Basic] Public Class LinePlot Inherits EditChartExample.UserChartControl1
Public xAxis As TimeAxis Public yAxis As LinearAxis
Class CustomFindObj Inherits FindObj Dim currentObj As LINEPLOT
Public Sub New(ByVal component As ChartView) MyBase.New(component) currentObj = component End Sub ‘New
Sub InvokeLineDialog(ByVal graphobj As GraphObj) Dim linedialog As EditLineDialog linedialog = New EditLineDialog(graphobj) If (linedialog.ShowDialog(Me.GetChartObjComponent()) = _ DialogResult.OK) Then selectedObj.SetColor(linedialog.GetLineColor()) selectedObj.SetLineStyle(linedialog.GetLineStyle()) selectedObj.SetLineWidth(linedialog.GetLineWidth()) End If End Sub
Public Sub InvokeTextDialog(ByVal textobj As ChartText) Dim textdialog As EditTextDialog textdialog = New EditTextDialog(textobj) If (textdialog.ShowDialog(Me.GetChartObjComponent()) = DialogResult.OK) Then textobj.SetTextString(textdialog.GetString()) textobj.SetTextFont(textdialog.GetFont()) End If End Sub
Public Overrides Sub OnMouseDown(ByVal mouseevent As MouseEventArgs)
MyBase.OnMouseDown(mouseevent) Dim selectedObj As GraphObj = GetSelectedObject() If (selectedObj Is Nothing) Then Return
‘ Check for a specific object If ((selectedObj Is currentObj.xAxis) Or _ (selectedObj Is currentObj.yAxis) Or _ (ChartSupport.IsKindOf(selectedObj, “SimpleLinePlot”)) Or _ (ChartSupport.IsType(selectedObj, “com.quinncurtis.chart2dnet.Grid”))) Then InvokeLineDialog(selectedObj) ElseIf (ChartSupport.IsKindOf(selectedObj, “ChartText”)) Then InvokeTextDialog(selectedObj) End If Me.GetChartObjComponent().UpdateDraw() End Sub
End Class
The LineDialog and TextDialog classes are derived from System.Windows.Forms.Form and need to be written by the programmer. Sample classes are found in the EditChartExample example. The sample LineDialog class uses the System.Windows.Forms.ColorDialog to select the color of line objects. The TextDialog class uses the System.Windows.Forms.FontDialog to select the text attributes associated with a font.
There are two ways to handle missing, or bad data. The first is to mark the data point in the dataset invalid, using the datasets SetValidData method. The second is to set the x- and/or y- value of the bad data point to the designated bad data value, ChartObj.rBadDataValue. Currently this value is set equal to the value of System.Double.MaxValue. Either method will prevent the data point from being displayed in a chart. If the bad data value is part of a line plot, a gap will appear in the line plot at that point. Bad data points are not deleted from a dataset.
n general, real-time updates involve adding new objects to a chart, or modifying existing objects that are already in the chart. Once the object is added or changed, call the ChartView.UpdateDraw() method to force the chart to update using the new values. Objects can be added or modified based on some external event, or in response to a timer event created using System.Timers.Timer. Make all changes for a given event and call the ChartView.UpdateDraw method once. The position of most GraphObj derived objects is set or modified using one of the objects SetLocation methods. New data points can be added to an existing dataset using one of the datasets AddDataPoint, AddTimeDataPoint, AddGroupDataPoints or AddTimeGroupDataPoints methods. ChartPlot derived objects that use datasets will update to reflect the new values when the ChartView.UpdateDraw method is called. If the coordinates of the new data points are outside of the x- and y-limits of the current coordinate system it may be necessary to rescale the coordinate system so that the new points show up; otherwise the new data points will be clipped. The new scale values can be set explicitly, or calculated using one of the auto-scale methods. The example programs SpectrumAnalyzer, DataLogger, DynPieChart and ScrollingMixedPlot all demonstrate various ways to update charts in real-time. If you want to change points in an existing dataset, but not the size of the dataset, call the datasets appropriate SetXDataValue, SetYDataValue, or SetDataPoint methods. The dataset has its own copy of the data so you must change these values, not the original values you used to initialize the dataset. If you plan to change every value in the dataset, you can do that point by point, or create a new dataset and swap that in for the old dataset using the plot objects SetDataset or SetGroupDataset method. Call the ChartView.UpdateDraw method to force the chart to update using the new values.
Flicker is the result of erasing and redrawing all or part of a chart in the current display buffer. Double buffering of screen images can minimize any flicker. The ChartView class does the actual work of rendering a chart image to the underlying UserControl display buffer. The UserControl class uses double buffering for the display of all screen images. When a chart is updated it is automatically rendered to an off-screen bitmap. When drawing is complete the off-screen bitmap is copied to the screen display buffer, minimizing the effect of flicker.
Implementing drill down or tooltips consists of three major parts: · Trapping a mouse event and determining the mouse cursor position in device and physical coordinates. · Identifying the chart object that intersects the mouse event. · Displaying appropriate information about the chart object. There are many classes that aid in one or more of these functions. The MouseListener class will trap a mouse event in the chart view. The FindObj class will filter and return the chart object, if any, that intersects the mouse cursor when a mouse button is pressed. The MoveObj class will filter, select and move a chart object as the mouse is dragged across the chart. The DataToolTip class will find the data point in a chart nearest the mouse cursor and display xy information about the data point as a popup ChartText display. The DataToolTip can also be customized for the display of custom information about the selected data point. It only takes a few lines to add a simple y-value tooltip to an existing chart.
[C#] DataToolTip datatooltip = new DataToolTip(chartVu); datatooltip.SetEnable(true); chartVu.SetCurrentMouseListener(datatooltip);
[Visual Basic] Dim datatooltip As New DataToolTip(chartVu) datatooltip.SetEnable(True) chartVu.SetCurrentMouseListener(datatooltip)
Some of the example programs that include tooltips include LineFill, Multiline, LinePlotSegments, StackedLineChart, Logarithmic, SimpleBarChart, GroupBarPlotChart, DoubleBarPlot, OpeningScreen, OHLCFinPlot and LabeledPieChart.
Auto-scaling has two parts. The first is the auto-scaling of the coordinate system based on one or more datasets. The second part is the auto-scaling of the axes that reside in the coordinate system. Manually scale the coordinate system and axes by calling the appropriate constructors. For example:
[C#] ChartCalendar xMin = new ChartCalendar(1996, ChartObj.FEBRUARY, 5); ChartCalendar xMax = new ChartCalendar(2002, ChartObj.JANUARY, 5); double yMin = 0; double yMax = 105;
TimeCoordinates simpleTimeScale; simpleTimeScale = new TimeCoordinates(xMin, yMin, xMax, yMax); // Create the time axis (x-axis is assumed) TimeAxis xAxis = new TimeAxis(simpleTimeScale); // Create the linear y-axis LinearAxis yAxis = new LinearAxis(simpleTimeScale, ChartObj.Y_AXIS);
// Create the ChartView object to place graph objects in. ChartView chartVu = new ChartView();
// Add the x- and y-axes to the chartVu object chartVu.AddChartObject(xAxis); chartVu.AddChartObject(yAxis);
[Visual Basic] Dim xMin As ChartCalendar = New ChartCalendar(1996, ChartObj.FEBRUARY, 5) Dim xMax As ChartCalendar = New ChartCalendar(2002, ChartObj.JANUARY, 5) Dim yMin As Double = 0 Dim yMax As Double = 105
Dim simpleTimeScale As TimeCoordinates simpleTimeScale = New TimeCoordinates(xMin, yMin, xMax, yMax) ‘ Create the time axis (x-axis is assumed) Dim xAxis As TimeAxis = New TimeAxis(simpleTimeScale) ‘ Create the linear y-axis Dim yAxis As LinearAxis = New LinearAxis(simpleTimeScale, ChartObj.Y_AXIS)
‘ Create the ChartView object to place graph objects in. Dim chartVu As ChartView = New ChartView()
‘ Add the x- and y-axes to the chartVu object chartVu.AddChartObject(xAxis) chartVu.AddChartObject(yAxis)
The documentation for the various coordinate system and axis classes includes examples of manual scaling.
Updating data was discussed in FAQ # 6. If you want the chart to rescale based on the new data, call the appropriate coordinate systems auto-scale method, followed by the auto-axis methods of all related axes. Then call the ChartView.UpdateDraw method. For example: [C#] // Create the ChartView object to place graph objects in. TimeSimpleDataset Dataset1 = new TimeSimpleDataset(“Sales”,x1,y1);
TimeCoordinates simpleTimeCoordinates = new TimeCoordinates(); simpleTimeCoordinates.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR , ChartObj.AUTOAXES_FAR); ChartView chartVu = new ChartView(); // Create the time axis (x-axis is assumed) TimeAxis xAxis = new TimeAxis(simpleTimeCoordinates); // Create the linear y-axis LinearAxis yAxis = new LinearAxis( simpleTimeCoordinates, ChartObj.Y_AXIS); . . . // The following code would be in the code handling the rescale event // Rescale chart based on a modified Dataset1 datset simpleTimeCoordinates.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR , ChartObj.AUTOAXES_FAR); xAxis.CalcAutoAxis(); yAxis.CalcAutoAxis(); // Redraw the chart using the rescaled coordinate system and axes chartVu.UpdateDraw();
[Visual Basic] Dim Dataset1 As TimeSimpleDataset = New TimeSimpleDataset(“Sales”, x1, y1) Dim simpleTimeCoordinates As TimeCoordinates = New TimeCoordinates() simpleTimeCoordinates.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR, _ ChartObj.AUTOAXES_FAR) Dim chartVu As ChartView = New ChartView() ‘ Create the time axis (x-axis is assumed) Dim xAxis As TimeAxis = New TimeAxis(simpleTimeCoordinates) ‘ Create the linear y-axis Dim yAxis As LinearAxis = New LinearAxis(simpleTimeCoordinates, ChartObj.Y_AXIS)
‘ The following code would be in the code handling the rescale event ‘ Rescale chart based on a modified Dataset1 datset simpleTimeCoordinates.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR, _ ChartObj.AUTOAXES_FAR) xAxis.CalcAutoAxis() yAxis.CalcAutoAxis() ‘ Redraw the chart using the rescaled coordinate system and axes chartVu.UpdateDraw()
The auto-scale routines for logarithmic coordinate systems will always select a power of 10 for the minimum and maximum value of the scale. You can use the auto-scale routine and then override the minimum and/or maximum values for the logarithmic scale. The default LogAxis constructor will pick up on the minimum of the coordinate system and use that as the axis tick mark origin. Or you can leave the coordinate system unchanged, and change the starting point of the axis tick marks using the axis SetAxisTickOrigin method. The example below is derived from the Logarithmic example code.
[C#] GroupDataset Dataset1 = new GroupDataset(“First”,x1,y1);
CartesianCoordinates pTransform1 = new CartesianCoordinates(ChartObj.LOG_SCALE, ChartObj.LINEAR_SCALE); pTransform1.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR, ChartObj.AUTOAXES_FAR);
pTransform1.SetScaleStartX(20); // Force start of scale at 20, AutoScale will // always choose a power of 10 decade. LogAxis xAxis = new LogAxis(pTransform1, ChartObj.X_AXIS); xAxis.SetAxisTickOrigin(20); chartVu.AddChartObject(xAxis);
[Visual Basic] ‘ Create the ChartView object to place graph objects in. Dim Dataset1 As GroupDataset = New GroupDataset(“First”, x1, y1)
Dim pTransform1 As CartesianCoordinates = _ New CartesianCoordinates(ChartObj.LOG_SCALE, ChartObj.LINEAR_SCALE) pTransform1.AutoScale(Dataset1, ChartObj.AUTOAXES_FAR, ChartObj.AUTOAXES_FAR)
pTransform1.SetScaleStartX(20) ‘ Force start of scale at 20, AutoScale will ‘ always choose a power of 10 decade. Dim xAxis As LogAxis = New LogAxis(pTransform1, ChartObj.X_AXIS) xAxis.SetAxisTickOrigin(20) chartVu.AddChartObject(xAxis)
The StringAxisLabels class should be used to create multi-line axis labels. Insert the “\n” new line character to add additional lines to each string used to define the string axis labels. The example below is from the AxisLabels example program.
[C#] String []xstringlabels = { “”, “Western”+”\n”+”Sales”+”\n”+”Region”, “Eastern”+”\n”+”Sales”+”\n”+”Region”, “Southern”+”\n”+”Sales”+”\n”+”Region”, “Northern”+”\n”+”Sales”+”\n”+”Region”};
StringAxisLabels xAxisLab5 = new StringAxisLabels(xAxis5); xAxisLab5.SetAxisLabelsStrings(xstringlabels,5); xAxisLab5.SetTextFont(graph5Font); chartVu.AddChartObject(xAxisLab5);
[Visual Basic] Dim xstringlabels As [String]() = {“”, “Western” + ControlChars.Lf + “Sales” + _ ControlChars.Lf + “Region”, “Eastern” + ControlChars.Lf + “Sales” + _ ControlChars.Lf + “Region”, “Southern” + ControlChars.Lf + “Sales” + _ ControlChars.Lf + “Region”, “Northern” + ControlChars.Lf + “Sales” + _ ControlChars.Lf + “Region”}
Dim xAxisLab5 As New StringAxisLabels(xAxis5) xAxisLab5.SetAxisLabelsStrings(xstringlabels, 5) xAxisLab5.SetTextFont(graph5Font) chartVu.AddChartObject(xAxisLab5)
One way to create multiple charts is to create multiple instances of the ChartView class and add each ChartView object to a container object such as a UserControl. A layout manager manages the position and size of each ChartView. Another way is to place multiple charts in the same ChartView object. This makes it easier to guarantee alignment between the axes of separate graphs. The trick to doing this is to create separate coordinate system objects (CartesianCoordinates, TimeCoordinates or PolarCoordinates) for each chart, and to position the plot area of each coordinate system so that they do not overlap. Use one of the coordinate systems SetGraphBorder… methods. Many of the examples use this technique, including GroupBarPlotChart, DoubleBarPlot, OHLFinPlot, FinOptions, DynPieChart, PieAndLineChart and PieAndBarChart. The example below was extracted from the OHLCFinPlot class. [C#] pTransform1 = new TimeCoordinates(); pTransform1.SetGraphBorderDiagonal(0.1, .15, .90, 0.6) ;
pTransform2 = new TimeCoordinates(); pTransform2.SetGraphBorderDiagonal(0.1, .7, .90, 0.875) ;
[Visual Basic] pTransform1 = new TimeCoordinates() pTransform1.SetGraphBorderDiagonal(0.1, .15, .90, 0.6)
pTransform2 = new TimeCoordinates() pTransform2.SetGraphBorderDiagonal(0.1, .7, .90, 0.875)
Unlike the JPEG image file format, the GIF file format uses a proprietary data compression algorithm known as LZW. The patent on the LZW compression algorithm is owned by the large computer/data processing company Unisys. Programmers who write commercial applications that use this file format may be subject to paying Unisys royalties. For this reason we do not intent to write any code that explicitly supports the GIF file format. If you, after having considered all of the alternatives, still require GIF files then create a System.Drawing.Imaging.ImageFormat instance that uses a GIF image format and use that to initialize the chart2dnet.BufferedImage class. The same holds true for all image formats.
The axis labeling routines are quite intelligent. Before the label is drawn at its calculated position, the software does a check to see if the bounding box of the new axis label intersects the bounding box of the previous axis label. If the new label is going to overlap the previous label, the label is skipped. You can override this default behavior by calling the objects SetOverlapLabelMode method. SetOverlapLabelMode (ChartObj.OVERLAP_LABEL_DRAW);
Another option, for horizontal axes only, is to stagger the tick mark labels. A stagger automatically alternates the line on which the tick mark label is placed. SetOverlapLabelMode (ChartObj.OVERLAP_LABEL_STAGGER);
There are two ordering methods used to render chart objects. The first method renders the objects in order, as added to the ChartView object. Objects added to the view last are drawn on top of objects added first. The second method renders the objects according to their z-order. Objects with the lowest z-order values are rendered first. Objects with equal z-order values are rendered in the ordered they are added to the ChartView object. The second method (z-order rendering) is the default method of object rendering used by the ChartView class. This default behavior can be changed by call the ChartView.SetZOrderSortEnable(false) method. You can change the default z-order value on an object-by-object basis. Call the GraphObj.SetZOrder method to change the z-order for any given object. See the section in the manual titled Rendering Order of GraphObj Objects for information about the default z-values for all chart objects The example below sets the z-order value of grid1 to something less than the default value (50) of ChartPlot objects, and the z-order value of grid2 to something greater than the default value. [C#] ChartView chartVu = new ChartView(); . . .
Grid grid1 = new Grid(xAxis, yAxis, ChartObj.Y_AXIS, ChartObj.GRID_MAJOR); grid1.SetZOrder(40); // This is actually the default value for the grid z-order chartVu.AddChartObject(grid1);
Grid grid2 = new Grid(xAxis, yAxis, ChartObj.Y_AXIS, ChartObj.GRID_MINOR); grid2.SetZOder(150); // Grid is drawn after ChartPlot objects // which have default z-value of 50 chartVu.AddChartObject(grid2);
[Visual Basic] Dim chartVu As ChartView = new ChartView() . . .
Dim grid1 As Grid = new Grid(xAxis, yAxis, ChartObj.Y_AXIS, ChartObj.GRID_MAJOR) grid1.SetZOrder(40) ‘ This is actually the default value for the grid z-order chartVu.AddChartObject(grid1)
Dim grid2 As Grid = new Grid(xAxis, yAxis, ChartObj.Y_AXIS, ChartObj.GRID_MINOR) grid2.SetZOder(150) ‘ Grid is drawn after ChartPlot objects ‘ which have default z-value of 50 chartVu.AddChartObject(grid2)
Since the ChartView class is derived from UserControl, you can place the scroll bar in the ChartView object, or you can place the ChartView object and the scroll bar in a parent container and use a layout manager to position everything. If you place the scroll bar in the ChartView you can still position it using a layout manager. The ChartView will always use the entire content area of the underlying UserControl for its canvas and the scroll bars will sit on top of this, not side by side. The example program LinePlotScrollBar uses two scroll bars, a horizontal scroll bar to control scrolling of the x-axis, and a vertical scroll bar that controls the magnitude of the y-axis. You need to add hScrollBar1_Scroll and vScrollBar1_Scroll event listeners to the ChartView class to process changes in scroll bar values.
[C#] public void UpdateXScaleAndAxes(int index) { int startindex = index; pTransform1.SetScaleStartX( (double) startindex); pTransform1.SetScaleStopX( (double) (startindex + 100)); xAxis.CalcAutoAxis(); yAxis.CalcAutoAxis(); xAxisLab.CalcAutoAxisLabels(); yAxisLab.CalcAutoAxisLabels(); this.UpdateDraw(); }
public void UpdateYScaleAndAxes(int index) { int startindex = index; pTransform1.SetScaleStartY( (double) -startindex); pTransform1.SetScaleStopY( (double) startindex); xAxis.CalcAutoAxis(); yAxis.CalcAutoAxis(); xAxisLab.CalcAutoAxisLabels(); yAxisLab.CalcAutoAxisLabels(); this.UpdateDraw(); }
private void hScrollBar1_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e) { UpdateXScaleAndAxes(hScrollBar1.Value); }
private void vScrollBar1_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e) { UpdateYScaleAndAxes(vScrollBar1.Value); }
[Visual Basic]
Public Sub UpdateXScaleAndAxes(ByVal index As Integer) Dim startindex As Integer = index pTransform1.SetScaleStartX(CDbl(startindex)) pTransform1.SetScaleStopX(CDbl(startindex + 100)) xAxis.CalcAutoAxis() yAxis.CalcAutoAxis() xAxisLab.CalcAutoAxisLabels() yAxisLab.CalcAutoAxisLabels() Me.UpdateDraw() End Sub ‘UpdateXScaleAndAxes
Public Sub UpdateYScaleAndAxes(ByVal index As Integer) Dim startindex As Integer = index pTransform1.SetScaleStartY(CDbl(-startindex)) pTransform1.SetScaleStopY(CDbl(startindex)) xAxis.CalcAutoAxis() yAxis.CalcAutoAxis() xAxisLab.CalcAutoAxisLabels() yAxisLab.CalcAutoAxisLabels() Me.UpdateDraw() End Sub ‘UpdateYScaleAndAxes
Private Sub HScrollBar2_Scroll(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.ScrollEventArgs) If Not (Me.IsDesignMode) Then UpdateXScaleAndAxes(HScrollBar1.Value) End If End Sub
Private Sub VScrollBar2_Scroll(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.ScrollEventArgs) If Not (Me.IsDesignMode) Then UpdateYScaleAndAxes(VScrollBar1.Value) End If End Sub
There are many other examples of Form components interacting with charts. The ContourLinePlot example program uses a CheckBox object to specify which contours are to be displayed. The CandlestickWithTable example continuously updates a DataGrid (the base class for the MyDataGrid class) table with open-high-low-close data and simultaneously updates a chart with the same data. The scrolling action of the DataGrid is synchronized with the chart. In the OHLCFinPlot example a Scrollbar controls the time axis of a stock market OHLC chart. The MultiAxes example uses Button objects to select the x-axis range.
The software runs as fast as we can make it. We do not have any hidden switches that will speed up the software. What you need to do is to step back and think about the best way to display your data. A fundamental issue that many programmers fail to consider is the relationship between the resolution of the rasterized screen image of the plot and the resolution of the data. A typical chart image will have 500-1000 pixels as the horizontal resolution of the plotting area. This would imply that in the 100M data point example above, every horizontal pixel would represent 50K to 100K data points. Obviously this is a terrible mismatch. In fact it is a bad match for datasets that have more than a couple of thousands points. So what you do is compress the data before it is displayed. Take the 100M data points and compress them down to 2K data points. The data compression can take several forms. You can take an average of every N points. The resulting dataset will be reduced by a factor of N. You can also find the sum for every N points, the minimum value of every N points, the maximum of every N points, or both the minimum and maximum of every 2N points. The last compression method, minimum and maximum, will always capture any minimums and maximum in the data. The result is that a 2000 point compressed dataset, where there are at least two data points per pixel of horizontal resolution, will look just like the 100,000,000 point dataset, only display hundreds of times faster. The Datset classes all include compression methods (SimpleDataset.CompressSimpleDataset, GroupDataset.CompressGroupDataset, TimeSimpleDataset.CompressTimeSimpleDataset and TimeGroupDataset.CompressTimeGroupDataset, TimeGroupDataset.CompressTimeFieldSimpleDataset, TimeGroupDataset.CompressTimeFieldGroupDataset) that operate on the existing dataset and return a new, compressed dataset. The CompressTimeFieldSimpleData and CompressTimeFieldGroupDataset are particular useful because they do not use a fixed sample size of N, instead they compress data so that adjacent time values are an increment of a specific time field (ChartObj.DAY_OF_YEAR, ChartObj.WEEK_OF_YEAR, ChartObj.MONTH, ChartObj.Year). Compressing data by month and year obviously requires a varying sample size. Once created, connect the compressed dataset to the ChartPlot object used to display the dataset. [C#] nNumPnts = 1000000; TimeSimpleDataset RawDataset = new TimeSimpleDataset(“Raw”, xtimedata, ydata,nNumPnts); int compressXmode = ChartObj.DATACOMRESS_AVERAGE; int compressYmode = ChartObj.DATACOMRESS_MINMAX; int compressTimeField = Calendar.MONTH; TimeSimpleDataset CompressedDataset = RawDataset.CompressTimeFileSimpleData( compressXmode, compressYmode, compressTimeField, 0, nNumPnts,”Compressed”);
[Visual Basic] nNumPnts = 1000000 Dim RawDataset As TimeSimpleDataset = new _ TimeSimpleDataset(“Raw”, xtimedata, ydata,nNumPnts) Dim compressXmode As Integer = ChartObj.DATACOMRESS_AVERAGE Dim compressYmode As As Integer = ChartObj.DATACOMRESS_MINMAX Dim compressTimeField As Integer = Calendar.MONTH Dim CompressedDataset As TimeSimpleDataset = _ RawDataset.CompressTimeFileSimpleData( compressXmode, compressYmode, compressTimeField, 0, nNumPnts,”Compressed”)
The real question is: How do you get data from your database into a simple .Net program, storing sequential data values in data array variables. This is up to you and is independent of the charting software. We recommend that you use the SQL database classes that are part of .Net and study the documentation provide by Microsoft and other sources, such as the O’Reilly programming books. Once you can read individual data elements of your data base it is a trivial matter to place the numeric and calendar data into simple .Net array variables and from there plot the data.
The BufferedImage class creates chart images independent of the.any physical display context. The BufferedImage class uses a System.Drawing.Imaging.ImageFormat object to control the format of the resulting image bitmap. You can use this image as an image object in an HTML page. See the previous chapter on ASP.Net programming for examples.
According to Microsoft documentation this is the goal of .Net. In theory you should be able to display controls derived from System.Windows.Forms.UserControl in Internet Explorer. Since the ChartView class in the QCChart2DNet control library is derived from System.Windows.Forms.UserControl you should be able to get it to work. Microsoft sponsored articles discussing these techniques are found at:
http://msdn.microsoft.com/msdnmag/issues/02/06/rich/default.aspx http://www.msdnaa.net/Resources/display.aspx?ResID=1704
We didn’t have any luck getting the examples in those articles to work. The likeliest problems are the permissions on the host server, and the security permissions on the client running IE. Because of this we did not spend much time trying to get it to work since we do not want to end up supporting these types of server related issues. It seems that people experimenting had more luck getting it to work in an Intranet, rather than as an application exposed to the entire Internet. We expect that users more familiar with these issues will have more luck than we did. We found that streaming JPEG images to an image (or Picture in FrontPage) tag in an HTML form, or to a System.Web.UI.WebControls.Image control of an ASP.NET application is a reliable and fast technique. See our web pages at: https://quinn-curtis.com/Chart2DAspWebApp.htm https://quinn-curtis.com/Chart2DDynWebApp.htm https://quinn-curtis.com/Chart2DHTMLAspApp.htm
The disadvantage is that the image of a chart does not have the mouse event user interface features of a ChartView based control hosted on the client computer. User interface features would need to be implemented using HTML or APS.NET controls such as scroll bars, text fields, drop down lists, etc. A discussion of using the QCChart2DNet with ASP.Net is found in the previous chapter.
Yes. See this page for more information.
In December 2006 will be introducing a dedicated 3D library, QCChart3D, that has the same API as QCChart2D (different namespace though). See this page for more information. The QCChart2D library is optimized for speed. That makes it most useful for real-time applications, or applications that use a large number of data points. Rather than slow it down by adding 3D routines to it, QCChart2D will remain a dedicated 2D charting package.
Yes. See our application note at https://quinn-curtis.com/QCNetMCPPWhitePaper.pdf Download the source to the MC++ examples programs described in the application note at https://quinn-curtis.com/QCNetMCPPExamples.zip This is a subset of the examples used in the C# and VB examples subdirectories. If you require other examples for MC++ at this time you will need to translate them from the C# code on your own.
Yes, Variable Control charts and Attribute Control charts can easily be created using a simple combination of SimpleLinePlot, SimpleLineMarkerPlot, SimpleBarPlot and SimpleScatterPlot classes. A couple of specialized SPC chart types (probability plots) require specialized coordinate system and axis routines and QCChart2D would not be able create these types of plots unless these specialized classes were added to the software. Our special projects group was hired last Spring by a large manufacturer to create templates for the most common SPC control charts, including Variable Control, Attribute Control, Frequency Histograms, Probability and Pareto charts. We used QCChart2D as the kernel and structured the template classes so the QA programmers could add these types of charts easily to their SPC applications.
|