Public Class OBXGraph
Inherits PictureBox
Private pts As New List(Of Point)
Private count, m As Integer
Public Sub Draw(b64 As String)
Dim idx = b64.IndexOf("Octer-stream^Base64^")
If idx = -1 Then idx = 0 Else idx += "Octer-stream^Base64^".Length
Dim bytes = Convert.FromBase64String(b64.Substring(idx))
pts.Clear()
count = 0 : m = 0
For Each b In bytes
If b > m Then m = b
pts.Add(New Point(count, b))
count += 1
Next
Me.Invalidate()
End Sub
Protected Overrides Sub OnPaint(pe As PaintEventArgs)
MyBase.OnPaint(pe)
If pts.Count = 0 Then Return
Dim dx = (Me.Width - 20) / count, dy = (20 - Me.Height) / m
With pe.Graphics
.ScaleTransform(dx, dy)
.TranslateTransform(10 / dx, (-50 - Me.Height))
.DrawCurve(Pens.Black, pts.ToArray)
End With
End Sub
End Class
Add this class to your project, build solution, add OBXGraph control on form and call:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s = "OBX|32|ED|15000^WBC Histogram. Binary^99MRC||^Application^Octer-stream^Base64^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAgIDAwUGCg4TGiIuOklYaX2OoLHD1OLu+P3//vny593Sw7Snmo1/dWthWFBJQjw2Mi8sKSclIyIgHx4cHBoaGBcWFBQTExISERAQEA8PDw4MDAsKCgoJCAgIBwcGBwcGBgYGBQQEBAQDBAMDAwMDAgICAgICAgEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
ObxGraph1.Draw(s)
End Sub
Imports Excel = Microsoft.Office.Interop.Excel
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s = "OBX|32|ED|15000^WBC Histogram. Binary^99MRC||^Application^Octer-stream^Base64^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAgIDAwUGCg4TGiIuOklYaX2OoLHD1OLu+P3//vny593Sw7Snmo1/dWthWFBJQjw2Mi8sKSclIyIgHx4cHBoaGBcWFBQTExISERAQEA8PDw4MDAsKCgoJCAgIBwcGBwcGBgYGBQQEBAQDBAMDAwMDAgICAgICAgEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
GraphToExcel(s)
End Sub
Private Sub GraphToExcel(b64 As String)
Dim idx = b64.IndexOf("Octer-stream^Base64^")
If idx = -1 Then idx = 0 Else idx += "Octer-stream^Base64^".Length
Dim bytes = Convert.FromBase64String(b64.Substring(idx))
Dim app = New Excel.Application
app.Visible = True
Dim wb = app.Workbooks.Add
Dim sheet As Excel.Worksheet = wb.Sheets(1)
For i = 1 To bytes.Count
sheet.Range("A" & i).Value2 = bytes(i - 1)
Next
Dim charts As Excel.ChartObjects = sheet.ChartObjects()
Dim chartObject = charts.Add(60, 10, 600, 300)
Dim chart = chartObject.Chart
Dim rng = sheet.Range("A1:A" & bytes.Count.ToString)
chart.SetSourceData(rng)
chart.ChartType = Excel.XlChartType.xlLine
chart.ChartWizard(Source:=rng)
End Sub
End Class
Imports System.ComponentModel
Public Class LIS_Graph
Inherits PictureBox
Public Class MsgInfo
Public Property MessageType As String
Public Property SequenceId As String
Public Property DataType As String
Public Property ObservationID As String
Public Property ObservationName As String
Public Property EncodeSys As String
Public Property Value As String
Public Property Units As String
Public Property RefRange As String
Public Sub Clear()
MessageType = "" : SequenceId = "" : DataType = "" : ObservationID = "" : ObservationName = "" : EncodeSys = "" : Value = "" : Units = "" : RefRange = ""
End Sub
Public Overrides Function ToString() As String
Dim sb As New Text.StringBuilder
For Each p In [GetType].GetProperties(Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance)
Dim s As String = p.GetValue(Me)
sb.AppendLine(String.Format("{0}: {1}", p.Name, s))
Next
Return sb.ToString
End Function
End Class
Private _graphBytes As Byte()
Private _lblInfo As New Label With {.AutoSize = False, .Width = 240, .Height = 140, .BorderStyle = BorderStyle.FixedSingle, .Padding = New Padding(5)}
Public Sub New()
Info = New MsgInfo
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Controls.Add(_lblInfo)
End Sub
Public Sub New(msg As String)
Info = New MsgInfo
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Message = msg
End Sub
<TypeConverter(GetType(PointFConverter)), DefaultValue(GetType(PointF), "1.4, 1.0")> Public Property UnitsScale As PointF = New PointF(1.4F, 1.0F)
<DefaultValue(GetType(Point), "50, 50")> Public Property LinesStep As Point = New Point(50, 50)
<DefaultValue(GetType(Point), "20, 20")> Public Property Offset As Point = New Point(20, 20)
<DefaultValue(True)> Public Property CursorLines As Boolean = True
<DefaultValue(True)> Public Property ShowInfo As Boolean = True
<Browsable(False)> Public ReadOnly Property Info As MsgInfo
Public WriteOnly Property Message As String
Set(value As String)
Parse(value)
End Set
End Property
Public Sub RefreshGraph()
Image = CreateGraph()
_lblInfo.Visible = ShowInfo
End Sub
Private ReadOnly Property GraphSize As Size
Get
Return New Size(Width - Offset.X * 2, Height - Offset.Y * 2)
End Get
End Property
Private ReadOnly Property GraphScale As PointF
Get
If _graphBytes Is Nothing OrElse _graphBytes.Length = 0 Then Return New PointF(1.0, 1.0)
Dim sz = GraphSize
Return New PointF(sz.Width / _graphBytes.Count, sz.Height / _graphBytes.Max)
End Get
End Property
Private Sub Parse(lisString As String)
Clear()
Dim parts = lisString.Split("|"c)
Info.MessageType = parts(0)
If parts.Length > 1 Then Info.SequenceId = parts(1)
If parts.Length > 2 Then Info.DataType = parts(2)
If parts.Length > 3 Then
Dim observation = parts(3)
If observation <> "" Then
Dim oParts = observation.Split("^"c)
Info.ObservationID = oParts(0)
Info.ObservationName = oParts(1).Replace(". ", ".")
Info.EncodeSys = oParts(2)
End If
End If
If parts.Length > 5 Then Info.Value = parts(5)
If Info.Value.StartsWith("^Application^Octer-stream^Base64^") Then
_graphBytes = Convert.FromBase64String(Info.Value.Replace("^Application^Octer-stream^Base64^", ""))
Info.Value = "Base64 encoded graph data"
Image = CreateGraph()
End If
If parts.Length > 6 Then Info.Units = parts(6)
If parts.Length > 7 Then Info.RefRange = parts(7)
_lblInfo.Visible = ShowInfo
_lblInfo.Text = Info.ToString
End Sub
Private Sub Clear()
_graphBytes = Nothing
Info.Clear()
Image = Nothing
End Sub
Private Function CreateGraph() As Bitmap
If _graphBytes Is Nothing OrElse _graphBytes.Length = 0 Then
Return Nothing
End If
Dim graph As New Bitmap(ClientSize.Width, ClientSize.Height)
Dim pts As New List(Of Point)
Dim count = 0, sz = GraphSize, scale = GraphScale
For Each b In _graphBytes
pts.Add(New Point(count * scale.X + Offset.X, b * scale.Y + Offset.Y))
count += 1
Next
Dim w = sz.Width + Offset.X, h = sz.Height + Offset.Y
Using g = Graphics.FromImage(graph)
g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
Using f As New Font("Arial", 8, FontStyle.Regular, GraphicsUnit.Point)
Using linePen = New Pen(Color.Black) With {.DashStyle = Drawing2D.DashStyle.Dash}
Dim sf As New StringFormat With {.Alignment = StringAlignment.Far, .LineAlignment = StringAlignment.Center}
For i = 0 To _graphBytes.Max * UnitsScale.Y Step LinesStep.Y
Dim y = h - i * scale.Y / UnitsScale.Y
Dim p = If(i = 0, Pens.Black, linePen)
g.DrawLine(p, Offset.X, y, w, y)
g.DrawString(i, f, Brushes.Black, Offset.X, y, sf)
Next
sf = New StringFormat With {.Alignment = StringAlignment.Center, .LineAlignment = StringAlignment.Near}
For i = 0 To count * UnitsScale.X Step LinesStep.X
Dim x = Offset.X + i * scale.X / UnitsScale.X
Dim p = If(i = 0, Pens.Black, linePen)
g.DrawLine(p, x, 0, x, h)
g.DrawString(i, f, Brushes.Black, x, h, sf)
Next
End Using
End Using
g.ScaleTransform(1.0F, -1.0F)
g.TranslateTransform(0.0F, -h - Offset.Y)
g.DrawCurve(Pens.Black, pts.ToArray)
g.ResetTransform()
'If ShowInfo Then
' Dim rc = New Rectangle(w - 240, Offset.Y, 240, 130)
' g.FillRectangle(Brushes.White, rc)
' g.DrawRectangle(Pens.Black, rc)
' rc.Inflate(-5, -5)
' g.DrawString(Info.ToString, Font, Brushes.Black, rc)
'End If
End Using
Return graph
End Function
Private Sub LIS_Graph_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If Not CursorLines OrElse _graphBytes Is Nothing OrElse _graphBytes.Length = 0 Then Return
Refresh()
Dim scale = GraphScale, sz = GraphSize
Dim w = sz.Width + Offset.X, h = sz.Height + Offset.Y
If e.X < Offset.X OrElse e.X > w OrElse e.Y < Offset.Y OrElse e.Y > h Then Return
Using p = New Pen(Brushes.Red) With {.DashStyle = Drawing2D.DashStyle.Dash}
Using g = Graphics.FromHwnd(Handle)
g.DrawLine(p, 0, e.Y, Width, e.Y)
g.DrawLine(p, e.X, 0, e.X, Height)
Dim sf As New StringFormat With {.LineAlignment = StringAlignment.Far}
Dim dX = If(e.X > GraphSize.Width / 2 + Offset.X, -80, 3)
Dim pt = New Point(e.X + dX, e.Y - 3)
Dim x As Integer = (e.X - Offset.X) * UnitsScale.X / scale.X,
y As Integer = (h - e.Y) * UnitsScale.Y / scale.Y
g.DrawString(String.Format("x = {0}; y = {1}", x, y), Font, Brushes.Red, pt, sf)
End Using
End Using
End Sub
Private Sub LIS_Graph_MouseLeave(sender As Object, e As EventArgs) Handles Me.MouseLeave
Refresh()
End Sub
Private Sub LIS_Graph_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
Image = CreateGraph()
_lblInfo.Location = New Point(GraphSize.Width + Offset.X - _lblInfo.Width, Offset.Y)
End Sub
End Class
Public Class PointFConverter
Inherits ExpandableObjectConverter
Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
If sourceType = GetType(String) Then
Return True
Else
Return MyBase.CanConvertFrom(context, sourceType)
End If
End Function
Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object) As Object
If TypeOf value Is String Then
Try
Dim s As String = DirectCast(value, String)
Dim converterParts As String() = s.Split(";"c)
Dim x As Single = 0.0F, y As Single = 0.0F
If converterParts.Length > 1 Then
x = Single.Parse(converterParts(0).Trim())
y = Single.Parse(converterParts(1).Trim())
ElseIf converterParts.Length = 1 Then
x = Single.Parse(converterParts(0).Trim())
End If
Return New PointF(x, y)
Catch
Throw New ArgumentException("Cannot convert [" + value.ToString() + "] to pointF")
End Try
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function
Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object, destinationType As Type) As Object
If destinationType = GetType(String) AndAlso TypeOf value Is PointF Then
Dim pt As PointF = value
Return String.Format("{0}; {1}", pt.X, pt.Y)
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
Using:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim graph = New LIS_Graph With {.Dock = DockStyle.Fill}
Controls.Add(graph)
graph.Message = "OBX|32|ED|15000^WBC Histogram. Binary^99MRC||^Application^Octer-stream^Base64^AAAAAAAAAAAAAAABAgQHDRcmO1Z3nMPg9P/98+HKsZl8aFdGMyUfHBwcHR8iJy02QUpLTU5PT09PTUxKSEZEQkBAPz4+PT09Pj49PDo5ODg3Nzg6PD5AQ0ZISUpMTU9RU1VWWFlZWlpaWlpaWlpbW1tbW1tcXV5fYGBhYWBgXl1bWVdVU1JQT01MSkhGREJAPj07OTg2NDMxLy0sKignJSQi"
End Sub
Open in new window
Just as a side note, where are these base64 strings coming from?