Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1529

VB6 Stock-Data-Rendering via RC6 cChart-Class

$
0
0
The Demo shows, how one can use the built-in cChart-HelperClass of the RC6, to "ease the pain" with:
- correct handling of ChartArea-Offsets
- rendering of x- and y-Axis-Ticks and -Texts at the correct (offset) positions
- and last but not least: "reverse Mouse-Interaction" (resolving to the right Data-Values from Mouse-Coords)

The Project needs only a reference to RC6 - and should only contain...
an empty Form with this code:
Code:

Option Explicit

Private SDD As cStockDataDays

Private Sub Form_Load()
  Caption = "Resize Me": AutoRedraw = True
  Set SDD = New cStockDataDays
  LoadNewDataInto SDD
End Sub

Sub LoadNewDataInto(SDD As cStockDataDays)  'simulate "recent week-data"... (7 added records)
  SDD.ClearData 'reset the SDD-internal Data-Buffers
 
  SDD.AddRecord Date - 6, 115, 125, 110, 135 'Params: xDay, yOpen, yClose, yLow, yHigh
  SDD.AddRecord Date - 5, 135, 155, 130, 160
  SDD.AddRecord Date - 4, 150, 165, 150, 175
  SDD.AddRecord Date - 3, 170, 175, 160, 185
 
  SDD.AddRecord Date - 2, 165, 155, 155, 175 'the last 3 records fullfill a "OpenPrice>ClosePrice"-condition
  SDD.AddRecord Date - 1, 150, 145, 140, 155
  SDD.AddRecord Date - 0, 135, 125, 120, 150 'the last added "Date - 0"-term resolves to "today"
End Sub

Private Sub Form_Resize()
  RefreshChartOn Me 're-render the whole chart in case of a change in Canvas-Size (works with Form or PictureBox)
End Sub

Sub RefreshChartOn(VBCanvas As Object)
  SDD.Chart.OffsL = 50 'change the Values of two ChartArea-Offset-Props from their defaults...
  SDD.Chart.OffsB = 55 '...OffsT and OffsR are available as well of course
 
  'if we have records, then refresh the underlying Chart-Surface (also updating our Form-Canvas)
  If SDD.RecordCount Then SDD.RefreshOn VBCanvas, "Some Chart-Title"
End Sub

'demonstrates reverse-transformation from device-space (xPxl/yPxl MouseCoords), to user-space (time/price Coords)
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
  If SDD.RecordCount = 0 Then Exit Sub  '<- do nothing, when the chart doesn't hold any data yet
 
  Dim xPxlChart, yPxlChart, xDataValue, yDataValue 'ByRef-Var definitions (filled in the next line)
  SDD.Chart.MouseCoordsToChartCoords x, y, xPxlChart, yPxlChart, xDataValue, yDataValue
  Caption = Format$(xDataValue + 0.5, "yyyy-mm-dd hh:nn") & "  " & Format$(yDataValue, "$0.00")
End Sub

and a single Drawing-Helper-Class, named cStockDataDays:
Code:

Option Explicit

Public WithEvents Chart As cChart

Private mData(), mRecordCount As Long, mYMin As Double, mYMax As Double

Private Sub Class_Initialize()
  Set Chart = New_c.Chart 'initialize the internal Public Chart-Variable to a new helper-instance
  ClearData 'init the internal Private-Vars to their default-state
End Sub

Public Sub ClearData() 'implemented as a Public Method, to allow a "reset for new Data" also from the outside
  ReDim mData(4, 0): mRecordCount = 0: mYMin = 1E+35: mYMax = -1E+35
End Sub

Public Sub AddRecord(ByVal xDay As Date, ByVal yOpen#, ByVal yClose#, ByVal yLow#, ByVal yHigh#)
  ReDim Preserve mData(0 To 4, mRecordCount) 'prolong our 2D-Array by a "new Row"...
        mData(0, mRecordCount) = xDay '... and copy the incoming params over
        mData(1, mRecordCount) = yOpen
        mData(2, mRecordCount) = yClose
        mData(3, mRecordCount) = yLow
        mData(4, mRecordCount) = yHigh

        Dim i As Long
        For i = 1 To 4 'determine the total y-Min/Max across all Y-Columns (which start at Index 1)
            If mYMin > mData(i, mRecordCount) Then mYMin = mData(i, mRecordCount)
            If mYMax < mData(i, mRecordCount) Then mYMax = mData(i, mRecordCount)
        Next
  mRecordCount = mRecordCount + 1 'increase this for the next round
End Sub

Public Property Get RecordCount() As Long
  RecordCount = mRecordCount
End Property

Public Function RefreshOn(VBCanvas As Object, Optional Title As String) As cCairoSurface 'return the used Surface as well
  VBCanvas.ScaleMode = vbPixels
  Set RefreshOn = Chart.Render(mData, VBCanvas.ScaleWidth, VBCanvas.ScaleHeight, Title) 'the Render-call returns a cCairoSurface
      RefreshOn.DrawToDC VBCanvas.hDC 'Blit the generated content of the returned Surface to the VBCanvas-Obj (a Form or PictureBox)
  If VBCanvas.AutoRedraw Then VBCanvas.Refresh 'call the Refresh-method on the Canvas, in case AutoRedraw is "On"
End Function

'------------------ Event-interface-implementation of cChart -----------------------------
Private Sub Chart_DrawChartBackGroundAndTitle(CC As RC6.cCairoContext, ByVal Title As String)
  CC.Paint 1, Cairo.CreateSolidPatternLng(&H888888)
  CC.SelectFont "Arial", 13, vbBlue, True, True
  CC.DrawText 0, 5, CC.Surface.Width, Chart.OffsT, Title, True, vbCenter, 0, 1
End Sub

Private Sub Chart_DrawChartAreaRect(CC As cCairoContext, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long)
  CC.Rectangle x, y, dx, dy: CC.Fill , Cairo.CreateSolidPatternLng(vbBlack)
End Sub

Private Sub Chart_OverrideAxisProps(Axis As cChartAxis, ByVal CurrentMin As Double, ByVal CurrentMax As Double, ByVal CurrentTickIntervals As Long)
  If mRecordCount = 0 Then Exit Sub 'nothing to do here
  Select Case Axis.Name
    Case "X" 're-adjust the Min/Max-DayValues of the X-Axis to "one day more"
        Axis.Min = mData(0, 0) - 0.5 '...by shifting our Min-DayValue "half a day to the left"
        Axis.Max = mData(0, mRecordCount - 1) + 0.5 '...and the Max-DayValue "half a day to the right"
        Axis.TickIntervals = mRecordCount

    Case "Y" 're-adjust the Y-Axis to the "total Min/Max" we found across all "Y-Fields"
        Axis.Min = mYMin
        Axis.Max = mYMax
  End Select
End Sub

Private Sub Chart_DrawSingleTickForAxis(Axis As cChartAxis, CC As cCairoContext, ByVal TickValue As Variant, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long)
  Select Case Axis.Name
    Case "X"
        If x > Chart.OffsL Then x = x - Axis.TickDistPxl / 2 Else Exit Sub
        CC.DrawLine x, y + dy, x, y + dy + 5, True, 1, vbMagenta
        Axis.DrawTickText CC, x, y + 10, Format$(TickValue, "mmm-dd")
        Axis.DrawTickText CC, x, y + 23, Format$(TickValue, "yyyy")

    Case "Y"
        CC.DrawLine x - 5, y, x, y, True, 1, vbYellow
        Axis.DrawTickText CC, x - 3, y, Format$(TickValue, "0.0")
  End Select
'  Debug.Print Axis.Name, x, y, TickValue, Axis.TickDist, Axis.TickDistPxl
End Sub

Private Sub Chart_DrawData(CC As cCairoContext, DataArr() As Variant, ByVal dx As Long, ByVal dy As Long)
  Dim xPxlSC As Double, yPxlSC As Double, dstPxl As Double, i As Long
      xPxlSC = Chart.AxisCol("X").PxlScaleFac 'get the Axis-ScaleFactors into local Vars, before entering the Loop at the end
      yPxlSC = Chart.AxisCol("Y").PxlScaleFac
      dstPxl = Chart.AxisCol("X").TickDistPxl / 5: If dstPxl > 15 Then dstPxl = 15

  For i = 0 To mRecordCount - 1
      RenderCandleOn CC, i, xPxlSC, yPxlSC, dstPxl, False 'delegate to a helper-routine
  Next
End Sub

Private Sub RenderCandleOn(CC As cCairoContext, ByVal RecIdx&, ByVal xPxlSC#, ByVal yPxlSC#, ByVal dstPxl#, Optional ByVal FillIt As Boolean)
  Dim xDay#:  xDay = mData(0, RecIdx) * xPxlSC
  Dim yOpen#:  yOpen = mData(1, RecIdx) * yPxlSC
  Dim yClose#: yClose = mData(2, RecIdx) * yPxlSC
  Dim yLow#:  yLow = mData(3, RecIdx) * yPxlSC
  Dim yHigh#:  yHigh = mData(4, RecIdx) * yPxlSC
  Dim Color&:  Color = IIf(yClose < yOpen, vbRed, vbGreen)

  If yClose < yOpen And yLow < yHigh Then '<- in this case...
    Dim yTmp#: yTmp = yLow: yLow = yHigh: yHigh = yTmp '...swap yLow and yHigh (for correct "Path-rendering-order")
  End If

  CC.Save 'buffer the prior TransForm-Matrix (the prior Coord-Sys)
    CC.TranslateDrawings xDay, 0 'to be able, to leave out the xDay(Offs) in all x-Params of the following block

    CC.MoveTo 0, yLow
    CC.LineTo 0, yOpen
    CC.LineTo -dstPxl, yOpen: CC.LineTo -dstPxl, yClose
    CC.LineTo 0, yClose
    CC.LineTo 0, yHigh
    CC.LineTo 0, yClose
    CC.LineTo dstPxl, yClose: CC.LineTo dstPxl, yOpen
    CC.LineTo 0, yOpen
  CC.Restore 'restore the prior TransForm-Matrix

  CC.SetSourceColor Color
  CC.SetLineWidth 1
  If FillIt Then CC.Fill True
  CC.Stroke
End Sub

These two code-modules will then produce:
Name:  CandleChart.png
Views: 33
Size:  34.3 KB

Have fun,

Olaf
Attached Images
 

Viewing all articles
Browse latest Browse all 1529

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>