Platforms to show: All Mac Windows Linux Cross-Platform
/ChartDirector/ChartDirector Control
Required plugins for this example: MBS Main Plugin, MBS ChartDirector Plugin, MBS Images Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /ChartDirector/ChartDirector Control
This example is the version from Fri, 9th Feb 2023.
Project "ChartDirector Control.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub Open()
// you can register the plugin here
// CDBaseChartMBS.Set License Code "xxx", 201612, 123, 123
End EventHandler
End Class
MenuBar MenuBar1
MenuItem FileMenu = "&File"
MenuItem PrintA1 = "Print Symbolline..."
MenuItem PrintA2 = "Print Bars..."
MenuItem PrintA3 = "Print surface..."
MenuItem PrintA4 = "Print Pie..."
MenuItem UntitledSeparator = "-"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem UntitledMenu1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem UntitledMenu0 = "-"
MenuItem EditSelectAll = "Select &All"
End MenuBar
Class PicWindow Inherits Window
Control a1 Inherits ChartDirectorControl
ControlInstance a1 Inherits ChartDirectorControl
EventHandler Sub ItemClicked(XLabel As String, X As Integer, DataSet As Integer, DataSetName As String, Value As Double, Title As String, Sector As Integer, Label As String)
// we switch symbols for clicked values here
Select case DataSet mod 3
case 0
Data0clicked(x) = not Data0clicked(x)
case 1
Data1clicked(x) = not Data1clicked(x)
case 2
Data2clicked(x) = not Data2clicked(x)
else
Return
end Select
a1.Redraw
End EventHandler
EventHandler Sub Open()
Dim P As Picture
// Ghost the overlay image
P = LogoMBS(200)
P.Mask.Graphics.ForeColor = &cDDDDDD
P.Mask.Graphics.FillRect(0, 0, P.Width, P.Height)
// Set the overlay image
Me.OverlayPicture = P
End EventHandler
EventHandler Function Render(ViewPort As CDViewPortManagerMBS, Width As Integer, Height As Integer) As CDBaseChartMBS
// The data for the line chart
viewport.validateViewPort
// Create a XYChart object of size 600 x 375 pixels
Dim c As New CDXYChartMBS(width, height)
'C.setBackground(C.kTransparent)
C.setTransparentColor(-1)
C.setClipping(0)
C.setAntiAlias(True, C.kAntiAlias)
// Add a title to the chart using 18 pts Times Bold Italic font
call c.addTitle("Product Line Global Revenue", "timesbi.ttf", 18)
// Set the plotarea at (50, 55) and of 500 x 280 pixels in size. Use a vertical
// gradient color from light blue (f9f9ff) to sky blue (aaccff) as background.
// Set border to transparent and grid lines to white (ffffff).
Call c.setPlotArea(50, 55, (width-100), (height-100), c.linearGradientColor(0, 55, 0, 335, &hf9fcff, &haaccff), -1, c.kTransparent, &hffffff)
call c.setClipping
// Add a legend box at (50, 28) using horizontal layout. Use 10pts Arial Bold as
// font, with transparent background.
c.addLegend(50, 28, false, "arialbd.ttf", 10).setBackground(c.kTransparent)
// Set the x axis labels
'call c.xAxis.setLabels(labels)
// Set y-axis tick density to 30 pixels. ChartDirector auto-scaling will use this
// as the guideline when putting ticks on the y-axis.
c.yAxis.setTickDensity(30)
c.yAxis2.setTickDensity(30)
// Set axis label style to 8pts Arial Bold
call c.xAxis.setLabelStyle("arialbd.ttf", 8)
call c.yAxis.setLabelStyle("arialbd.ttf", 8)
call c.yAxis2.setLabelStyle("arialbd.ttf", 8)
// Set axis line width to 2 pixels
c.xAxis.setWidth(2)
c.yAxis.setWidth(2)
c.yAxis2.setWidth(2)
// Add axis title using 10pts Arial Bold Italic font
call c.yAxis.setTitle("Revenue in US millions", "arialbi.ttf", 10)
call c.yAxis2.setTitle("Revenue in US millions", "arialbi.ttf", 10)
// scroll
dim axisLowerLimit as double
dim axisUpperLimit as double
axisLowerLimit = 100.0-100.0 *(viewport.getViewPortLeft + viewport.getViewPortWidth)
axisUpperLimit = 100.0-100.0 * viewport.getViewPortLeft
c.xAxis.setLinearScale(axisLowerLimit,axisUpperLimit)
c.xAxis.setRounding(false, false)
axisLowerLimit = 100.0-100.0 *(viewport.getViewPortTop + viewport.getViewPortHeight)
axisUpperLimit = 100.0-100.0 * viewport.getViewPortTop
c.yAxis.setLinearScale(axisLowerLimit,axisUpperLimit)
c.yAxis.setRounding(false, false)
c.yAxis2.setLinearScale(axisLowerLimit,axisUpperLimit)
c.yAxis2.setRounding(false, false)
// Add a line layer to the chart
dim layer as CDLineLayerMBS
layer = c.addLineLayer
// Set the line width to 3 pixels
layer.setLineWidth(3)
// Add the three data sets to the line layer, using circles, diamands and X
// shapes as symbols
// lines only
dim d as CDDataSetMBS
d=layer.addDataSet(data0, &hff0000, "Quantum Computer")
'd.setDataSymbol(c.kCircleSymbol, 9)
d.setLineWidth(1)
d=layer.addDataSet(data1, &h00ff00, "Atom Synthesizer")
'd.setDataSymbol(c.kDiamondSymbol, 11)
d.setLineWidth(1)
d=layer.addDataSet(data2, &hff6600, "Proton Cannon")
d.setLineWidth(1)
'd.setDataSymbol(c.Cross2Shape, 11)
// first color values
d=layer.addDataSet(FilterValues(data0, Data0clicked, false), &hff0000)
d.setDataSymbol(c.kCircleSymbol, 9)
d.setLineWidth(0)
d=layer.addDataSet(FilterValues(data1, Data1clicked, false), &h00ff00)
d.setDataSymbol(c.kDiamondSymbol, 11)
d.setLineWidth(0)
d=layer.addDataSet(FilterValues(data2, Data2clicked, false), &hff6600)
d.setLineWidth(0)
d.setDataSymbol(c.Cross2Shape, 11)
// second color values
d=layer.addDataSet(FilterValues(data0, Data0clicked, true), &h00ff00)
d.setDataSymbol(c.kCircleSymbol, 9)
d.setLineWidth(0)
d=layer.addDataSet(FilterValues(data1, Data1clicked, true), &hff0000)
d.setDataSymbol(c.kDiamondSymbol, 11)
d.setLineWidth(0)
d=layer.addDataSet(FilterValues(data2, Data2clicked, true), &h0000ff)
d.setLineWidth(0)
d.setDataSymbol(c.Cross2Shape, 11)
Return c
End EventHandler
End Control
Control a2 Inherits ChartDirectorControl
ControlInstance a2 Inherits ChartDirectorControl
EventHandler Function Render(ViewPort As CDViewPortManagerMBS, Width As Integer, Height As Integer) As CDBaseChartMBS
dim data0(-1) as double = array(6, 12.5, 18.2, 15.0)
dim angles0(-1) as double = array(45, 96, 169, 258.0)
Dim size0(-1) As Double = Array(41, 105, 12, 20.0)
dim data1(-1) as double = array(18, 16, 11, 14.0)
dim angles1(-1) as double = array(30, 210, 240, 310.0)
dim size1(-1) as double = array(30, 45, 12, 90.0)
// Create a PolarChart object of size 460 x 460 pixels
dim c as new CDPolarChartMBS(width, height)
// Add a title to the chart at the top left corner using 15pts Arial Bold Italic
// font
call c.addTitle(CDPolarChartMBS.kTopLeft, "<*underline=2*>EM Field Strength", "arialbi.ttf", 15)
// Set center of plot area at (230, 240) with radius 180 pixels
c.setPlotArea(width/2, height/2, min(width,height)*0.35)
// Use alternative light grey/dark grey circular background color
c.setPlotAreaBg(&hdddddd, &heeeeee)
// Set the grid style to circular grid
c.setGridStyle(false)
// Add a legend box at the top right corner of the chart using 9 pts Arial Bold
// font
c.addLegend(width-2, 0, true, "arialbd.ttf", 9).setAlignment(CDPolarChartMBS.kTopRight)
// Set angular axis as 0 - 360, with a spoke every 30 units
c.angularAxis.setLinearScale(0, 360, 30)
c.radialAxis.setLinearScale 0, 20,5
// Set the radial axis label format
c.radialAxis.setLabelFormat("{value} km")
c.angularAxis.setLabelGap 8
call c.angularAxis.setLabelStyle "",8
call c.radialAxis.setLabelStyle "",8
// Add a blue (&h9999ff) line layer to the chart using (data0, angle0)
dim layer0 as CDPolarLineLayerMBS
layer0 = c.addLineLayer(data0, &h9999ff, "Cold Spot")
layer0.setAngles(angles0)
// Disable the line by setting its width to 0, so only the symbols are visible
layer0.setLineWidth(0)
// Use a circular data point symbol
layer0.setDataSymbol(CDPolarChartMBS.kCircleSymbol, 11)
// Modulate the symbol size by size0 to produce a bubble chart effect
layer0.setSymbolScale(size0)
// Add a red (&hff9999) line layer to the chart using (data1, angle1)
dim layer1 as CDPolarLineLayerMBS
layer1 = c.addLineLayer(data1,&hff9999, "Hot Spot")
layer1.setAngles(angles1)
// Disable the line by setting its width to 0, so only the symbols are visible
layer1.setLineWidth(0)
// Use a circular data point symbol
layer1.setDataSymbol(CDPolarChartMBS.kCircleSymbol, 11)
// Modulate the symbol size by size1 to produce a bubble chart effect
layer1.setSymbolScale(size1)
// Output the chart
Return c
End EventHandler
End Control
Control a4 Inherits ChartDirectorControl
ControlInstance a4 Inherits ChartDirectorControl
EventHandler Function Render(ViewPort As CDViewPortManagerMBS, Width As Integer, Height As Integer) As CDBaseChartMBS
// The data for the pyramid chart
dim data(-1) as double = array(156.0, 123, 211, 179)
// The labels for the pyramid chart
dim labels(-1) as string = array("Funds", "Bonds", "Stocks", "Cash")
// The semi-transparent colors for the pyramid layers
dim colors(-1) as integer
colors.Append &h60000088
colors.Append &h6066aaee
colors.Append &h60ffbb00
colors.Append &h60ee6622
// Create a PyramidChart object of size 480 x 400 pixels
dim c as new CDPyramidChartMBS(width, height)
// Set the cone center at (280, 180), and width x height to 150 x 300 pixels
c.setConeSize(width/2, height/2, min(height,width)*0.333, height*0.75)
// Set the elevation to 15 degrees
c.setViewAngle(15)
// Set the pyramid data and labels
c.setData(data, labels)
// Set the layer colors to the given colors
c.setColors(c.kDataColor, colors)
// Leave 1% gaps between layers
c.setLayerGap(0.01)
c.setJoinLine(&h000000, 1)
// Add labels at the left side of the pyramid layers using Arial Bold font. The
// labels will have 3 lines showing the layer name, value and percentage.
call c.setLeftLabel("{label}"+EndOfLine.unix+"US ${value}K"+EndOfLine.unix+"({percent}%)", "arialbd.ttf",8)
Return c
End EventHandler
End Control
Control a3 Inherits ChartDirectorControl
ControlInstance a3 Inherits ChartDirectorControl
EventHandler Sub ItemClicked(XLabel As String, X As Integer, DataSet As Integer, DataSetName As String, Value As Double, Title As String, Sector As Integer, Label As String)
// Manage UI interaction with chart rendering variables
If (Sector = HighLightSector) Then
self.HighLightSector = -1
Else
self.HighLightSector = Sector
End If
Me.Redraw
End EventHandler
EventHandler Sub Open()
Dim P As Picture
// Ghost the overlay image
P = LogoMBS(200)
P.Mask.Graphics.ForeColor = &cDDDDDD
P.Mask.Graphics.FillRect(0, 0, P.Width, P.Height)
// Set the overlay image
Me.OverlayPicture = P
End EventHandler
EventHandler Function Render(ViewPort As CDViewPortManagerMBS, Width As Integer, Height As Integer) As CDBaseChartMBS
// The data for the pie chart
dim data(-1) as double = array(21.0, 18, 15, 12, 8, 24)
// The labels for the pie chart
dim labels(-1) as string = array("Labor", "Licenses", "Taxes", "Legal", "Facilities", "Production")
// The colors to use for the sectors
dim colors(-1) as integer
colors.Append &h66aaee
colors.Append &heebb22
colors.Append &hbbbbbb
colors.Append &h8844ff
colors.Append &hdd2222
colors.Append &h009900
// Create a PieChart object of size 600 x 320 pixels. Use a vertical gradient
// color from light blue (99ccff) to white (ffffff) spanning the top 100 pixels
// as background. Set border to grey (888888). Use rounded corners. Enable soft
// drop shadow.
dim c as new CDPieChartMBS(width, height)
c.setBackground(c.linearGradientColor(0, 0, 0, 100, &h99ccff, &hffffff),&h888888)
c.setRoundedFrame(&hFFFFFF,10)
'c.setDropShadow
// Add a title using 18 pts Times New Roman Bold Italic font. Add 16 pixels top
// margin to the title.
c.addTitle("Pie Chart With Legend Demonstration", "timesbi.ttf", 18).setMargin(0, 0, 16, 0)
// Set the center of the pie at (160, 165) and the radius to 110 pixels
c.setPieSize(width/4, height/2, 110)
// Draw the pie in 3D with a pie thickness of 25 pixels
c.set3D(25)
// Set the pie data and the pie labels
c.setData(data, labels)
call c.setLabelStyle
// Set the sector colors
c.setColors(CDPieChartMBS.kDataColor, colors)
// Use local gradient shading for the sectors
'c.setSectorStyle(CDPieChartMBS.kLocalGradientShading)
// Use the side label layout method, with the labels positioned 16 pixels from
// the pie bounding box
c.setLabelLayout(CDPieChartMBS.kSideLayout, 16)
// Show only the sector number as the sector label
c.setLabelFormat("{={sector}+1}")
c.setJoinLine 0,1
// Set the sector label style to Arial Bold 10pt, with a dark grey (444444)
// border
dim t as CDTextBoxMBS=c.setLabelStyle("arialbd.ttf", 10)
t.setBackground(CDPieChartMBS.kTransparent, &h444444)
// Add a legend box, with the center of the left side anchored at (330, 175), and
// using 10 pts Arial Bold Italic font
dim b as CDLegendBoxMBS = c.addLegend(width/2, height/2, true, "arialbi.ttf", 10)
b.setAlignment(CDPieChartMBS.kLeft)
// Set the legend box border to dark grey (444444), and with rounded conerns
b.setBackground(CDPieChartMBS.kTransparent, &h444444)
b.setRoundedCorners(10)
// Set the legend box margin to 16 pixels, and the extra line spacing between the
// legend entries as 5 pixels
b.setMargin(16)
b.setKeySpacing(0, 5)
// Set the legend box icon to have no border (border color same as fill color)
b.setKeyBorder(CDPieChartMBS.kSameAsMainColor)
if HighLightSector >= 0 then
System.DebugLog "Explode sector: "+str(HighLightSector)
c.setExplode(HighLightSector)
end if
// Set the legend text to show the sector number, followed by a 120 pixels wide
// block showing the sector label, and a 40 pixels wide block showing the
// percentage
b.setText("<*block,valign=top*>{={sector}+1}.<*advanceTo="+_
Str(22)+"*><*block,width="+_
Str(120)+"*>{label}<*/*><*block,width="+Str(CDBaseChartMBS.ScaleFactor*40)+",halign=right*>{percent}<*/*>%")
// Output the chart
return c
End EventHandler
End Control
Control StaticText1 Inherits Label
ControlInstance StaticText1 Inherits Label
End Control
Control StaticText2 Inherits Label
ControlInstance StaticText2 Inherits Label
End Control
EventHandler Sub Open()
Dim x0 As Double = 70
Dim x1 As Double = 50
Dim x2 As Double = 30
Data0.Append(x0)
Data1.Append(x1)
Data2.Append(x2)
// Create randomized data points for line chart demo
For I As Integer = 1 To 100
x0 = x0 + Rnd() * 5.0 - 2.5
x1 = x1 + Rnd() * 5.0 - 2.5
x2 = x2 + Rnd() * 5.0 - 2.5
Data0.Append(x0)
Data1.Append(x1)
Data2.Append(x2)
Next
redim Data0clicked(UBound(data0))
redim Data1clicked(UBound(data1))
redim Data2clicked(UBound(data2))
End EventHandler
Function PrintA1() As Boolean
a1.Print
Return (True)
End Function
Function PrintA2() As Boolean
a2.Print
Return (True)
End Function
Function PrintA3() As Boolean
a3.Print
Return (True)
End Function
Function PrintA4() As Boolean
a4.Print
Return (True)
End Function
Function FilterValues(data() as Double, clicked() as Boolean, value as Boolean) As double()
dim r(-1) as Double
dim u as integer = UBound(data)
redim r(u)
for i as integer = 0 to u
if clicked(i) = value then
r(i) = data(i)
else
r(i) = CDBaseChartMBS.kNoValue
end if
next
Return r
End Function
Property Protected Data0() As Double
Property Data0clicked() As Boolean
Property Protected Data1() As Double
Property Data1clicked() As Boolean
Property Protected Data2() As Double
Property Data2clicked() As Boolean
Property Private HighLightSector As Integer
End Class
Class ChartDirectorControl Inherits Canvas
Const kModeDefault = 0
Const kModeMove = 1
Const kModeZoomIn = 2
Const kModeZoomOut = 3
Event ItemClicked(XLabel As String, X As Integer, DataSet As Integer, DataSetName As String, Value As Double, Title As String, Sector As Integer, Label As String)
End Event
Event ItemDoubleClicked(XLabel As String, X As Integer, DataSet As Integer, DataSetName As String, Value As Double, Title As String, Sector As Integer, Label As String)
End Event
Event Open()
End Event
Event Render(ViewPort As CDViewPortManagerMBS, Width As Integer, Height As Integer) As CDBaseChartMBS
End Event
EventHandler Sub DoubleClick(X As Integer, Y As Integer)
SendClickEvent true
End EventHandler
EventHandler Function MouseDown(X As Integer, Y As Integer) As Boolean
isMouseDown = true
If (AllowZoom = True) And (Keyboard.AsyncOptionKey = True) Then
// Zoom In
Mode = kModeZoomIn
StartX = X
StartY = Y
StartX2 = X
StartY2 = Y
ElseIf (AllowZoom = True) And (Keyboard.AsyncShiftKey = True) Then
// Zoom Out
Mode = kModeZoomOut
StartX = X
StartY = Y
StartX2 = X
StartY2 = Y
Else
Dim HotSpotID As Integer = -1
// Check if over a hotspot; Nil check prevents spurious NilObjectExceptions
If (ImageMapHandler <> Nil) Then
HotSpotID = ImageMapHandler.getHotSpot(X, Y)
end if
If (HotSpotID > 0) Then
// Click
Mode = kModeDefault
SendClickEvent false
ElseIf (AllowMove = True) Then
// Move
Mode = kModeMove
me.MouseCursor = System.Cursors.HandClosed
StartX = X
StartY = Y
ViewPort.startDrag // Call to initiate drag within the ViewPort
End If
End If
Return (True)
End EventHandler
EventHandler Sub MouseDrag(X As Integer, Y As Integer)
If (Mode = kModeMove) Then
// If the chart has been dragged/repositioned then complete the ViewPort drag and refresh the image
If (ViewPort.dragTo(CDBaseChartMBS.kDirectionHorizontalVertical, StartX - X, Y - StartY) = True) Then Redraw
me.Invalidate
ElseIf (Mode = kModeZoomIn) Or (Mode = kModeZoomOut) Then
// Calculate the coordinates for the scaling/drawing of the selection rectangle
Dim L, T, W, H As Integer
StartX2 = X
StartY2 = Y
L = Min(StartX, StartX2)
T = Min(StartY, StartY2)
W = StartX2 - StartX
H = StartY2 - StartY
If (W < 0) Then W = -W
If (H < 0) Then H = -H
ZoomPicture = new Picture(me.Width, me.Height)
ZoomPicture.Graphics.DrawPicture(RectanglePicture, L, T, W, H, 0, 0, RectanglePicture.Width, RectanglePicture.Height)
me.Invalidate
End If
End EventHandler
EventHandler Sub MouseExit()
// Finalize redraw
CleanDrawInfo
End EventHandler
EventHandler Sub MouseMove(X As Integer, Y As Integer)
If (AllowZoom = True) And (Keyboard.AsyncOptionKey = True) Then
// Zoom In
me.MouseCursor = System.Cursors.MagnifyLarger
CleanDrawInfo // Finalize redraw
ElseIf (AllowZoom = True) And (Keyboard.AsyncShiftKey = True) Then
// Zoom Out
me.MouseCursor = System.Cursors.MagnifySmaller
CleanDrawInfo // Finalize redraw
Else
Dim HotSpotID As Integer = -1
// Check if over a hotspot; Nil check prevents spurious NilObjectExceptions
If (ImageMapHandler <> Nil) Then
HotSpotID = ImageMapHandler.getHotSpot(X, Y)
'System.DebugLog "HotSpotID: "+str(HotSpotID)+" at " + str(x)+"/"+str(y)
end if
If (HotSpotID > 0) Then
me.MouseCursor = System.Cursors.StandardPointer
DrawInfoX = X
DrawInfoY = Y
UpdateDrawInfo
me.Invalidate
ElseIf (AllowMove = True) Then
me.MouseCursor = System.Cursors.HandOpen
CleanDrawInfo // Finalize redraw
Else
me.MouseCursor = System.Cursors.StandardPointer
CleanDrawInfo // Finalize redraw
End If
End If
End EventHandler
EventHandler Sub MouseUp(X As Integer, Y As Integer)
isMouseDown = false
ZoomPicture = nil
If (Mode = kModeMove) Then
// Move
me.MouseCursor = System.Cursors.HandOpen
ElseIf (Mode = kModeZoomIn) Then
// Zoom In (On Point)
If (Abs(X - StartX) < 3) And (Abs(Y - StartY) < 3) Then
// Zoom In (On Point)
If (ViewPort.canZoomIn(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then
If (ViewPort.zoomAt(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, ZoomInRatio) = True) then
Redraw
end if
Else
// Cannot zoom in...
Beep
End If
Else
// Zoom In (On Rectangle)
If (ViewPort.canZoomIn(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then
If (ViewPort.zoomTo(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, StartX, StartY) = True) Then
Redraw
end if
Else
// Cannot zoom in...
Beep
End If
End If
ElseIf (Mode = kModeZoomOut) Then
// Zoom Out (On Point)
If (ViewPort.canZoomOut(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then
If (ViewPort.zoomAt(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, ZoomOutRatio) = True) Then
Redraw
end if
else
// Cannot zoom out...
Beep
End If
End If
End EventHandler
EventHandler Sub Open()
// Initialize the ViewPort manager class
ViewPort = New CDViewPortManagerMBS
// Set ViewPort coordinant constraints
ViewPort.setViewPortHeight(1.0)
ViewPort.setViewPortTop(0.0)
ViewPort.setViewPortWidth(1.0)
ViewPort.setViewPortLeft(0.0)
// Set Zoom In and Zoom Out constraints (if desired; otherwise allow infinite zoom)
'ViewPort.setZoomInWidthLimit(0.01)
'ViewPort.setZoomInHeightLimit(0.01)
'ViewPort.setZoomOutWidthLimit(1.0)
'ViewPort.setZoomOutHeightLimit(1.0)
// Initialize scalable image for use in rendering the selection rectangle
RectanglePicture = New Picture(32, 32, 32)
RectanglePicture.Graphics.ForeColor = &cFF0000
RectanglePicture.Graphics.FillRect(0, 0, 32, 32)
RectanglePicture.Mask.Graphics.ForeColor = &cCCCCCC
RectanglePicture.Mask.Graphics.FillRect(0, 0, 32, 32)
// Set the initial cursor style
me.MouseCursor = System.Cursors.HandOpen
// Call overridden superclass method
Open
// fix some properties if set wrong in IDE
me.DoubleBuffer = false
me.EraseBackground = false
#if RBVersion >= 2013.0 then
me.Transparent = False
#endif
StartZoomTimer
End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect)
// Handle drawing the composited image to the drawing region
if LastPicture = nil or LastWidth <> g.Width or LastHeight <> g.Height then
Render g.Width, g.Height
end if
// remember width/Height so we can render again if resized
LastWidth = g.Width
LastHeight = g.Height
// Draw composited chart image
if LastPicture<>nil then
G.DrawPicture(LastPicture, 0, 0, g.Width, g.Height, 0, 0, LastPicture.Width, LastPicture.Height)
end if
if DrawInfoX >= 0 then
DrawItemInfo(g, DrawInfoX, DrawInfoY)
end if
if ZoomPicture <> nil then
g.DrawPicture ZoomPicture, 0, 0, g.Width, g.Height, 0, 0, ZoomPicture.Width, ZoomPicture.Height
end if
End EventHandler
Private Sub CleanDrawInfo()
// Finalize redraw
DrawInfoX = -1
DrawInfoY = -1
DrawInfoLabel = ""
DrawInfoXLabel = ""
DrawInfoXValue = ""
DrawInfoDataSet = ""
DrawInfoDataSetName = ""
DrawInfoValue = ""
DrawInfoTitle = ""
DrawInfoSector = ""
self.Invalidate
End Sub
Function CurrentPicture() As Picture
// Return the currently displayed chart image
Return (LastPicture)
End Function
Private Sub DrawItemInfo(destGraphics as graphics, X As Integer, Y As Integer)
Dim Value As String
// Compose label graphic
If (DrawInfoLabel <> "") Then
Value = DrawInfoLabel + ": " + DrawInfoValue
else
Value = DrawInfoValue
end if
const f = 2 // draw 2x
Dim B As Picture = new Picture(LastPicture.Width,LastPicture.Height, 32)
Dim BG As Graphics = B.Graphics
bg.TextSize = 12 * f
Dim W As Integer = 6 + BG.StringWidth(Value)
Dim H As Integer = 20
// Add some space between the cursor position and the initial label drawing position
X = X*f + 10
Y = Y*f + 10
// If we are too close to the right, we need to move our label drawing position left
If (X + W > me.Width) Then X = X - 20 - W
// If we are too close to the bottom, we need to move our label drawing position up
If (Y + H > me.Height) Then Y = Y - 20 - H
'System.DebugLog "rect: "+str(X)+" "+str(y)+" "+str(w)+" "+str(h)
// Draw in the current composited chart image
B. Graphics.DrawPicture(LastPicture, 0, 0, bg.Width, bg.Height, 0, 0, LastPicture.Width, LastPicture.Height)
B.Mask.Graphics.DrawPicture(LastPicture.Mask, 0, 0, bg.Width, bg.Height, 0, 0, LastPicture.Width, LastPicture.Height)
// Draw the label graphic
BG.ForeColor = &cCCCC00
BG.FillRect(X, Y, W, H)
BG.ForeColor = &c000000
BG.DrawString(Value, X + 3, Y + 15)
// Update the composited chart image
destGraphics.DrawPicture(B, 0, 0, destGraphics.Width, destGraphics.Height, 0, 0, LastPicture.Width, LastPicture.Height)
End Sub
Sub Print()
Dim PS As New PrinterSetup
// Set landscape orientation if appropriate
PS.Landscape = Width > Height
// Initialize graphics region for printing
Dim G As Graphics = OpenPrinterDialog(PS)
// Bail out...
If (G = Nil) Then Return
// Render printed chart (use page size)
// NOTE: This uses a factor of 4x to up the resolution of the printed chart image (approximately 300 dpi)
'Dim P As Picture = RenderPicture(4, G.Width, G.Height)
'G.DrawPicture(P, 0, 0, G.Width, G.Height, 0, 0, G.Width * 4, G.Height * 4)
// Render printed chart (use control size)
// NOTE: This uses a factor of 4x to up the resolution of the printed chart image (approximately 300 dpi)
Dim P As Picture = RenderPicture(2, Width, Height)
G.DrawPicture(P, 0, 0, Width, Height, 0, 0, p.Width, p.Height)
End Sub
Sub Redraw()
// Method to directly trigger internal redraw
// cause render to run again
LastPicture = nil
me.Invalidate
End Sub
Private Sub Render(gWidth as integer, gHeight as integer)
// Prepare ViewPort coordinates for chart rendering
ViewPort.validateViewPort
// Trigger chart rendering (actual chart configuration and data plotting)
Current = Render(ViewPort, gWidth, gHeight)
If (Current = Nil) Then
// failed?
Else
// 2x for higher DPI displays
Current.setOutputOptions("bmpscale=2")
if RenderWithTransparency then
// Get in-memory version of the chart image in PNG format (supports transparency)
Dim S As String
S = Current.makeChart(CDBaseChartMBS.kPNG)
// Error check
If (S = "") Then
Return
End If
// Generate RB Picture from chart image data
LastPicture = PNGStringToPictureMBS(S, 0)
else
// Get the chart image based on the returned chart object
LastPicture = Current.makeChartPicture
end if
// Support overlay picture drawing
If (OverlayPicture <> Nil) Then
dim ow as integer = OverlayPicture.Width * 2
dim oh as integer = OverlayPicture.Height * 2
LastPicture.Graphics.DrawPicture(OverlayPicture, (LastPicture.Width - ow) / 2, (LastPicture.Height - oh) / 2, ow, oh, 0, 0, OverlayPicture.Width, OverlayPicture.Height)
End If
// Support control border drawing
If (Border = True) Then
LastPicture.Graphics.PenHeight = 1
LastPicture.Graphics.PenWidth = 1
LastPicture.Graphics.ForeColor = &c000000
LastPicture.Graphics.DrawRect(0, 0, LastPicture.Width, LastPicture.Height)
LastPicture.Mask.Graphics.PenHeight = 1
LastPicture.Mask.Graphics.PenWidth = 1
LastPicture.Mask.Graphics.ForeColor = &c000000
LastPicture.Mask.Graphics.DrawRect(0, 0, LastPicture.Width, LastPicture.Height)
End If
// Update ViewPort to current chart coordinants
ViewPort.setChartMetrics(Current.getChartMetrics)
// Process ImageMap data to support data point rollovers, etc.
Dim ImageMapString As String
ImageMapString = Current.getHTMLImageMap("myurl" )
ImageMapHandler = New CDImageMapHandlerMBS(ImageMapString)
#if false // DebugBuild then
dim f as FolderItem = SpecialFolder.Desktop.Child("ImageMapString "+me.Name+".txt")
dim b as BinaryStream = BinaryStream.Create(f, true)
if b <> nil then
b.Write ImageMapString
end if
dim g as FolderItem = SpecialFolder.Desktop.Child("Image "+me.Name+".png")
LastPicture.Save(g, LastPicture.SaveAsPNG)
#endif
End If
End Sub
Function RenderPicture(Factor As Double, Width As Integer, Height As Integer) As Picture
// Prepare ViewPort coordinates for chart rendering
ViewPort.validateViewPort
// Handle scaled redraw for printing
Dim C As CDBaseChartMBS = Render(ViewPort, Width, Height)
'If (C <> Nil) Then Return (C.makeChartPicture)
// 2x for higher DPI displays
Dim option As String = "bmpscale=" + factor.ToString
c.setOutputOptions(option)
// Error check
If (C = Nil) Then Return (Nil)
// Get in-memory version of the chart image in PNG format (supports transparency)
Dim S As String
S = C.makeChart(C.kPNG)
// Error check
If (S = "") Then Return (Nil)
// Generate RB Picture from chart image data
Dim P As Picture
P = PNGStringToPictureMBS(S, 0)
Return (P)
End Function
Private Sub SendClickEvent(isDoubleClick as Boolean)
Dim Index as integer
Dim Key, KeyValue As String
Dim XLabel As String
Dim XValue As String
Dim DataSet As String
Dim DataSetName As String
Dim Value As String
Dim Title As String
Dim Sector As String
Dim Label As String
// Error check
If (ImageMapHandler = Nil) Then Return
// Do key/value pair priming calls
Index = 0
Key = ImageMapHandler.getKey(Index)
KeyValue = ImageMapHandler.getValue(Index)
Do
// NOTE: This is not the complete supported list of parameters; Refer to ChartDirector documentation on 'Parameter Substitution and Formatting' for additional parameters
Select Case Key
Case "xlabel"
XLabel = KeyValue
Case "x"
XValue = KeyValue
Case "dataset"
DataSet = KeyValue
Case "datasetname"
DataSetName = KeyValue
Case "value"
Value = KeyValue
Case "title"
Title = KeyValue
Case "sector"
Sector = KeyValue
Case "label"
Label = KeyValue
End Select
Index = Index + 1
// Get next key/value pair
Key = ImageMapHandler.getKey(Index)
KeyValue = ImageMapHandler.getValue(Index)
Loop Until Key.Len = 0
// Trigger click event
if isDoubleClick then
ItemDoubleClicked(XLabel, Val(XValue), Val(DataSet), DataSetName, Val(Value), Title, Val(Sector), Label)
else
ItemClicked(XLabel, Val(XValue), Val(DataSet), DataSetName, Val(Value), Title, Val(Sector), Label)
end if
End Sub
Private Sub StartZoomTimer()
// we only need zoom timer for zooming...
if not self.AllowZoom then return
if ZoomTimer = nil then
ZoomTimer = new timer
AddHandler ZoomTimer.Action, WeakAddressOf ZoomTimerAction
ZoomTimer.Mode = timer.ModeMultiple
ZoomTimer.Period = 200
end if
End Sub
Private Sub UpdateDrawInfo()
DrawInfoLabel = ""
DrawInfoXLabel = ""
DrawInfoXValue = ""
DrawInfoDataSet = ""
DrawInfoDataSetName = ""
DrawInfoValue = ""
DrawInfoTitle = ""
DrawInfoSector = ""
// Error check
If (ImageMapHandler = Nil) Then Return
Dim Index as integer
Dim Key, KeyValue As String
// Do key/value pair priming calls
Index = 0
Key = ImageMapHandler.getKey(Index)
KeyValue = ImageMapHandler.getValue(Index)
Do
// NOTE: This is not the complete supported list of parameters;
// Refer to ChartDirector documentation on 'Parameter Substitution and Formatting' for additional parameters
Select Case Key
Case "xlabel"
DrawInfoXLabel = KeyValue
Case "x"
DrawInfoXValue = KeyValue
Case "dataset"
DrawInfoDataSet = KeyValue
Case "datasetname"
DrawInfoDataSetName = KeyValue
Case "value"
DrawInfoValue = KeyValue
Case "title"
DrawInfoTitle = KeyValue
Case "sector"
DrawInfoSector = KeyValue
Case "label"
DrawInfoLabel = KeyValue
End Select
Index = Index + 1
// Get next key/value pair
Key = ImageMapHandler.getKey(Index)
KeyValue = ImageMapHandler.getValue(Index)
Loop Until Key.Len = 0
End Sub
Sub UpdateMouseCursor()
// call from a timer in your window to keep the mouse cursor change with pressing alt and shift keys.
// zoom allowed?
if not AllowZoom then Return
if isMouseDown then Return
dim x as integer = me.MouseX
dim y as integer = me.MouseY
// check if mouse is inside
if X < 0 then Return
if Y < 0 then Return
if X > me.Width then Return
if Y > me.Height then Return
// now update cursor
If Keyboard.AsyncOptionKey Then
// Zoom In
me.MouseCursor = System.Cursors.MagnifyLarger
ElseIf Keyboard.AsyncShiftKey Then
// Zoom Out
me.MouseCursor = System.Cursors.MagnifySmaller
Else
If DrawInfoX >= 0 Then
// showing info
me.MouseCursor = System.Cursors.StandardPointer
ElseIf (AllowMove = True) Then
// hand, so you can move
me.MouseCursor = System.Cursors.HandOpen
Else
// regular one
me.MouseCursor = System.Cursors.StandardPointer
End If
End If
End Sub
Private Sub ZoomTimerAction(t as timer)
UpdateMouseCursor
End Sub
Note "About Properties"
AllowMove and AllowZoom must be enabled if you want to zoom and move.
You need to use the viewport in your render code.
If Border is true, a black line is drawn around the chart.
If CleanGraphicsBeforeDrawing is true then the graphics object is cleared
before something is drawn
The OverlayPicture defines a picture which is drawn on top of the chart
as a watermark.
RenderWithTransparency will do redraws slower. The chart is rendered as
PNG with mask and for every redraw we refresh the whole control.
This may look bad on Windows.
ZoomInRatio and ZoomOutRatio are used for zooming.
This is how much we zoom on each click.
Note "Notes for Render Event"
Use the Width and Height you get and not the control's.
Use the Factor to multiply all values so you get it properly scaled for printing.
Use the ViewPort for zooming and moving.
Property AllowMove As Boolean
Property AllowZoom As Boolean
Property Border As Boolean
Property Private Current As CDBaseChartMBS
Property Private DrawInfoDataSet As String
Property Private DrawInfoDataSetName As String
Property Private DrawInfoLabel As string
Property Private DrawInfoSector As String
Property Private DrawInfoTitle As String
Property Private DrawInfoValue As String
Property Private DrawInfoX As Integer = -1
Property Private DrawInfoXLabel As string
Property Private DrawInfoXValue As String
Property Private DrawInfoY As integer
Property Private ImageMapHandler As CDImageMapHandlerMBS
Property Private LastHeight As Integer
Property Private LastPicture As Picture
Property Private LastWidth As Integer
Property Private MaxValue As Double
Property Private MinValue As Double
Property Private Mode As Integer
Property OverlayPicture As Picture
Property Private RectanglePicture As Picture
Property RenderWithTransparency As Boolean
Property Private StartX As Integer
Property Private StartX2 As Integer
Property Private StartY As Integer
Property Private StartY2 As Integer
Property Private ViewPort As CDViewPortManagerMBS
Property ZoomInRatio As Double = 1.25
Property ZoomOutRatio As Double = 0.75
Property ZoomPicture As Picture
Property Private ZoomTimer As timer
Property Private isMouseDown As Boolean
End Class
SetRetina
End SetRetina
End Project
See also:
The items on this page are in the following plugins: MBS ChartDirector Plugin.