provided by: 
Originally published at Internet.comHave you ever used Visual Basic's graphing controls before? If so, you will have noticed the huge amount of functionality packed in: 7 different types of graph, different colours for each axis, colouring for each part of the graph, special text and font styles and many more features. Now, if you have ever tried distributing the MS Chart control then you will have found that the OCX adds over 900KB to your setup package, not something that most of us want. Find out inside-
So, when you only need a simple set of display capabilities, where do you turn? Do you go out and buy a third party control which could be bulky and expensive or do you write your own in VB, allowing great customisation? In this article, you will learn how to write your own light version of the MS Chart control.
This light version is only 44KB when complied, a mere 1/20th of the size of the MS Chart control. You might thinking that this control only includes limited functionality, but your wrong. This control supports both line and bar graphs, point plotting, a legend and up to 15 data series (with different colours for each one).
Well, where do we begin? Properties would be a good start. VB makes adding properties to a usercontrol easy by providing us with the ActiveX Control Interface Wizard. To load this, click Project, Add User Control and select VB ActiveX Control Interface Wizard. Follow the steps through and add the following properties:
Backcolor - OLE_COLOR (Get and Let)
DisplayLegend - BOOLEAN (Get and Let)
Enabled - BOOLEAN - (Get and Let)
Font - FONT (Get and Let)
ForeColor - OLE_COLOR (Get and Let)
HighScale - DOUBLE (Get and Let)
HorizontalTickFrequency - VARIANT (Get and Let)
LowScale - DOUBLE (Get and Let)
PlotPoints - BOOLEAN (Get and Let)
Title - STRING (Get and Let)
TitleFont - FONT (Get and Let)
VerticalTickInterval - VARIANT (Get and Let)
You will also need to manually add the following Get and Let procedures:
ChartType - CHARTTYPES
If you aren't sure what these procedures look like, here is the one for the ChartType property: Public Property Get ChartType() As ChartTypes 'Property Code Here End Property Public Property Let ChartType(ByVal New_ChartType As ChartTypes) 'Property Code Here End Property
(NOTE: Don't worry about the Property Code here comments, as you will learn later what you need to place in there.)
OK. Now the properties have been built, we need to write some methods to actually perform the drawing of the graph. But before we can do that, we need to declare some variables in our control. The first thing we need to do is to add an enumeration of the available chart types. Add the following code to the General Declarations procedure of the user control: Enum ChartTypes Bar = 0 Line = 1 End Enum
We now need to add an internal reference to this enumeration by the means of a variable. Add this declaration just below the ChartTypes enum: Dim m_ChartType As ChartTypes
We now need some events, but fortunately these are standard ones. Just load up the ActiveX Control Interface Wizard again (Project, Add User Control, VB ActiveX Control Interface Wizard) and add the following events:
Click DblClick
KeyDown
KeyPress
KeyUp
MouseDown
MouseMove
MouseUp
There's two more things we need to do. First, we need to change some of the default values for the properties we made earlier. The HighScale property needs changing to 100, the HorizontalTickFrequency to 1 and the VerticalTickInterval to 1. The complete set of default variables should now look like this: 'Default Property Values: Const m_def_DisplayLegend = 0 Const m_def_HighScale = 100 Const m_def_LowScale = 0 Const m_def_PlotPoints = 0 Const m_def_Title = "0" Const m_def_VerticalTickInterval = 1 Const m_def_HorizontalTickFrequency = 1 Const m_def_ChartType = 0
(Notice the addition of the ChartType default.)
Secondly, we have to add a few of our own internal variables to help our methods pass data to each other. I will explain what each variable does after you have coded them: Dim PlotData() As Variant Dim PlotColors(15) As Long Dim Legends(15) As Long Dim ChartWidth As Long Dim ChartHeight As Long Dim TitleOffset As Long
Right. The first is a variant array that gets set when a call to the RegisterData method. This array becomes a two dimensional array holding information about each piece of data. The PlotColors and Legends variables simply hold the number of items each can hold (this is 15 because we use colours from the QBColor function). ChartWidth and ChartHeight specify the width and height of the chart, and TitleOffset holds the distance that the title is from the chart. Simple.
As I have already mentioned the RegisterData method, it would make sense to move onto that next. When calling the RegisterData method you will need to pass a two dimensional variant array. The array should look something like this: Redim MyData(2, 20) As Variant MyData(0, 1) = 8
This sets the first point on the data series 0, to have a value of 8. You can use a loop to easily set up the array: For i = 0 To 20 MyData(0, i) = (Rnd - 0.5) * 10 + I Next I
This generates one data series with some 20 random values in it. You pass the array like this: GraphLite1.RegisterData MyData()
The RegisterData procedure does some clever things with the array, and stores it in the internal array variable.
You can easily manipulate different parts of the graph control using the properties built in. You can change the title and its font, using the default Font property to set the style for everything else (the legend and axis measurements). You can change the colour and description of a data series on the legend using the SetSeriesOptions procedure.
Well, apart from some extra code in the Write and Read Properties methods, the only code left is to actually draw the graph.
The main method is PlotChart, although it is called internally from the Refresh method. Mostly the Line method is used for drawing the graph, and some clever maths calculations are performed to work out where everything goes. The code gets quite involved here, so I won't try and explain how it all works. Take a look at the procedure below and see what you make of it: Private Sub PlotChart() Dim Columns As Long Dim n As Long, i As Long, d As Double Dim x As Long, y As Long Dim x1 As Long, y1 As Long Dim PlotTop As Long, PlotBottom As Long, _ PlotLeft As Long, _ PlotRight As Long Dim TickString As String Dim LowTick As Double Dim BarWidth As Intege
n Error GoTo PlotError 'determine horizontal extent Columns = UBound(PlotData, 2) + 1 'adjust vertical scale if necessary For n = 1 To UBound(PlotData) For i = 0 To UBound(PlotData, 2) If m_HighScale < PlotData(n, i) Then m_HighScale = PlotData(n, i) If m_LowScale > PlotData(n, i) Then m_LowScale = PlotData(n, i) Next i Next n 'define plot area PlotLeft = 120 'may be overridden later PlotRight = ChartWidth PlotTop = TitleOffset PlotBottom = UserControl.ScaleHeight - (UserControl.TextHeight("X") * 2) 'determine vertical tick scale If m_LowScale / m_VerticalTickInterval = Int(m_LowScale / m_VerticalTickInterval) Then LowTick = m_LowScale Else LowTick = Int(m_LowScale / m_VerticalTickInterval) * m_VerticalTickInterval End If 'determine left spacing 'check vertical captions For d = LowTick To HighScale Step m_VerticalTickInterval If PlotLeft < (UserControl.TextWidth(Format$(d)) + 120) Then PlotLeft = (UserControl.TextWidth(Format$(d)) + 120) End If Next d 'check caption for first horizontal tick If PlotLeft < (UserControl.TextWidth(PlotData(0, 0)) / 2) + 60 Then PlotLeft = (UserControl.TextWidth(PlotData(0, 0)) / 2) + 60 End If 'draw row ticks For d = LowTick To HighScale Step m_VerticalTickInterval y = PlotBottom - (PlotBottom - PlotTop) * ((d - LowTick) / (m_HighScale - LowTick)) UserControl.Line (PlotLeft, y)-Step(60, 0) UserControl.CurrentX = PlotLeft - (UserControl.TextWidth(Format$(d)) + 60) UserControl.CurrentY = y - (UserControl.TextHeight("X") * 0.5) UserControl.Print Format$(d) Next d 'draw plot box UserControl.Line (PlotLeft, PlotTop)-(PlotRight, PlotBottom), , B 'draw column ticks and captions For i = 0 To Columns - 1 Step m_HorizontalTickFrequency x = PlotLeft + (((PlotRight - PlotLeft) / (Columns - 1)) * i) UserControl.Line (x, PlotBottom)-Step(0, -60) UserControl.CurrentX = x - (UserControl.TextWidth(PlotData(0, i)) / 2) UserControl.CurrentY = PlotBottom + (UserControl.TextHeight("X") * 0.5) UserControl.Print PlotData(0, i) Next i 'base barwidth on series and points If m_ChartType = Bar Then BarWidth = (PlotRight - (PlotLeft + 60)) / (Columns * UBound(PlotData)) - 30 If BarWidth <= 15 Then BarWidth = 30 End If 'plot graph For n = 1 To UBound(PlotData) For i = 0 To UBound(PlotData, 2) 'determine coordinates x = PlotLeft + (((PlotRight - PlotLeft) / (Columns - 1)) * i) - 15 If PlotData(n, i) = LowTick Then y = PlotBottom - 15 Else y = (PlotBottom - ((PlotData(n, i) - LowTick) / (m_HighScale - LowTick) _ * (PlotBottom - PlotTop))) - 15 End If Select Case m_ChartType Case Bar 'adjust x for series x = PlotLeft + (((PlotRight - PlotLeft) / Columns) * i) - 15 x = x + 30 + ((n - 1) * (BarWidth + 30)) UserControl.Line (x, y)-(x + BarWidth, PlotBottom - 15), PlotColors(n - 1), BF Case Line 'draw data point If m_PlotPoints Then UserControl.Line (x, y)-Step(30, 30), PlotColors(n - 1), BF End If 'draw data graph If i <> 0 Then UserControl.Line (x + 15, y + 15)-(x1 + 15, y1 + 15), PlotColors(n - 11) End If x1 = x y1 = y End Select Next i Next n PlotExit: Exit Sub PlotError: If Err = 9 Then 'fail silently, not initialized yet Else Err.Raise 32007, "GraphLiteProject.GraphLite", _ "Error " & Err & " plotting graph: " & Error$(Err) End If Resume PlotExit End Sub
Gets rather confusing eh? Oh well, the DrawTitle method shouldn't be too bad. Basically after the position of the text has been calculated using the ScaleWidth and TextWidth properties, all that's left to do is to use the Print function to apply the text: Private Sub DrawTitle() Dim f As Font Set f = UserControl.Font Set UserControl.Font = m_TitleFont UserControl.CurrentX = (UserControl.ScaleWidth - UserControl.TextWidth(m_Title)) / 2 UserControl.Print m_Title TitleOffset = UserControl.CurrentY Set UserControl.Font = f End Sub
The DrawLegend method is a bit more complicated, although it works on much the same principle of calculating the positions, and using the Line and Print methods to apply the data.
Well, that concludes the steps for making a very lightweight graphing control. You should now be able to drop this straight into your VB apps with ease, set a few properties and you're off! If you feel up to it, you could always add a few more properties and graph types, but don't go too far otherwise you'll be competing with the MS Chart control in terms of size, which is why this control was written in the first place!
But if you do make any nice changes, please let me know so I can post an updated version. All that's left now to do is download a full working source code copy - we are so generous :)
Download the graph control complete with demo form!
Author: Sam Huggill
Read article at Internet.com site