I allways see people asking how to create graphics without Active-X controls.
In my opinion, the most simple way to do it is to use labels. Labels are versatile, we can change their BackColor, Caption, Width, Height. These four properties are all we need to create a simple graphic, like in this picture below.

The graphic is drawn in a container. We can set the label color to the bar color, in its caption we can store the values, its height is proportional to the value, and the width depends on the division between the container's width and the wuantity of bars.
The bars auto adjust themselves according to the data received, and the dimensions of the container.
Everything is done in a single method. In the form example, I've put this code in the refresh Method of the container. A cursor is needed to store the data, where the first field, FIELD(1) receives the values and the second field FIELD(2) receives the Caption. The name of the cursor is stored in the Tag property of the container. And that's all !
Some extra code was added to allow the resizement of the form.
Put this code in the Load Event of the form :
CREATE CURSOR
sales (amount n(8,2), cName c(6))
INSERT INTO
sales VALUES (250,"JAN")
INSERT INTO
sales VALUES (128,"FEB")
INSERT INTO
sales VALUES ( 90,"MAR")
INSERT INTO
sales VALUES (330,"APR")
INSERT INTO
sales VALUES (190,"MAY")
INSERT INTO
sales VALUES (250,"JUN")
INSERT INTO
sales VALUES ( 50,"JUL")
INSERT INTO
sales VALUES ( 80,"AUG")
INSERT INTO
sales VALUES ( 50,"SEP")
INSERT INTO
sales VALUES ( 19,"OCT")
INSERT INTO
sales VALUES (160,"NOV")
INSERT INTO
sales VALUES (199,"DEC")
And this in the Refresh Event:
*!* Create array with 12 colors to be used
DIMENSION
laColors(12)
laColors(1) =
RGB(255,128,128) && RED
laColors(2) =
RGB(0,255,0) && GREEN
laColors(3) =
RGB(128,128,255) && BLUE
laColors(4) =
RGB(255,0,255) && PINK
laColors(5) =
RGB(0,255,255) && CYAN
laColors(6) =
RGB(255,255,0) && YELLOW
laColors(7) =
RGB(160,160,210) && BLUE2
laColors(8) =
RGB(255,160,30) && ORANGE
laColors(9) =
RGB(200,140,140) &&
laColors(10) =
RGB(96,196,96) && GREEN2
laColors(11) =
RGB(255,200,200) && ROSE
laColors(12) =
RGB(200,200,200) && GREY
lnBlack =
RGB(28,28,28) && BLACK
lnWhite =
RGB(255,255,255) && WHITE
lcTable =
This.Tag
SELECT
"&lcTable"
*!* Calculate the width of each bar
lnlargura = INT(This.Width / RECCOUNT())
lnAltura = This.Height
lcFldValor =
This.Tag + "." + FIELD(1)
IF FCOUNT
() > 1
lcFldLegenda =
This.Tag + "." + FIELD(2)
ELSE
lcFldLegenda = ""
ENDIF
CALCULATE MAX
(EVALUATE(lcFldValor)) TO lnMax
*!* Add the labels that will be the bars and the Captions
SCAN
n = RECNO()
lcObj = "label" +
TRANSFORM(n)
lcLeg = "lname" +
TRANSFORM(n)
*!* Check if object already exists to avoid errors
IF TYPE("This."+lcObj) <> "O"
This.AddObject(lcObj,"label")
This.AddObject(lcLeg,"label")
ENDIF
WITH This.&lcObj.
.
BackStyle = 1 && opaque
.Backcolor = laColors(IIF(n>12,n-12,n))
.
Width = lnLargura
lnValor =
EVALUATE(lcFldValor)
lnBarra = ( lnValor / lnMax) * (
This.Height - 1 - 17)
.
Height = lnBarra
.
left = ((n-1) * lnLargura) + 2
.
Top = lnAltura - This.&lcObj..Height - 17
.
Tag = TRANSFORM(lnValor)
.
Caption = TRANSFORM(lnValor)
.
FontSize = 8
.
FontBold = .T.
.
Alignment = 2
.
Visible = .T.
ENDWITH
WITH This
.&lcLeg.
.
BackStyle = 1 && opaque
.Backcolor = lnWhite
.
ForeColor = lnBlack
.
Width = lnLargura - 2
IF NOT EMPTY(lcFldLegenda)
.
Caption = ALLTRIM(EVALUATE(lcFldLegenda))
ENDIF
.left = ((n-1) * lnLargura) + 3
.
Top = This.Height - 17
.
Height = 17
.
FontSize = 8
.
Alignment = 2
.
Visible = .T.
ENDWITH
ENDSCAN
This
.Width = ((n) * lnLargura) + 3
You may use this technique to create bar graphics at the horizontal, with very slight adaptations in the code.
In the attached file, bargraphics.zip, you'll find bargraphics.scx/sct from this example, and also 3 more files, callbar.prg that calls barras.scx/sct that create a modal form with bargraphics that can be resized.