|
-
May 26th, 2026, 10:20 PM
#1
Thread Starter
Member
Plotting persistent graphics to an object then rendering to PictureBox
Hi Guys,
I am having a little challenge drawing and rendering persistent graphics.
I am attempting to plot an XY graph using dynamic data where I receive a time value (X) and an increment value (Y)
I have created a picturebox object to plot the values to and programmatically define my centreline zero
I then take the incoming time event in seconds and the incoming incremental value which can range from -6 through to 6 inclusively.
Then I add that value to the incremental total and if that value is positive, I plot a Blue vertical line from the zero point to the value on the Y axis, where the X axis is automatically incremented by one (time in seconds) and if it is a negative value then I plot it in Red with a descending line from the zero point.
The above is both a description of what I am trying to do with the incoming data.
Where I am having difficulty is drawing the graphics in a persistent manner so that as the data arrives, the previous plot is not erased by the new incoming plot.
If I simulate the information and draw it all in one routine, then I get a nice graphic but when I attempt to plot it as the data arrives, I either get a single line that plots each time or I get nothing (most frustrating)
Here is my simulated data code which should plot some simulated red and blue solid arcs. (not actual arcs as the data is in column form)
I initially tried plotting direct to the form which worked if I did it all in one go, but of course I couldn't add any new data as calling the routine refreshed the form and I lost the previous plots.
Then I created a picturebox object and defined a rectangle area, I also defined a bufferedgraphicscontext object so that I could draw the column to the object and then render the object to the rectangle on the picturebox so that I could cumulatively add columns to the buffered object and then render the object, thus preventing the loss of previous plots as the whole plot graph would be rendered each time. This is where I am at and other than getting the render to flash on and instantly disappear, I have been unable to figure out how to keep the rendered image persistent.
I am hoping that you might be able to point out where I am going wrong?
Code:
Public Class Form1
Public MyImage As New BufferedGraphicsContext
Public rectangle = New Rectangle(x:=0, y:=0, width:=755, height:=201)
Public MyWorm As System.Drawing.BufferedGraphics
Public pen1 As Pen = New Pen(Color.White, 2)
Public pen2 As Pen = New Pen(Color.DarkRed, 1)
Public pen3 As Pen = New Pen(Color.Navy, 1)
Dim x As Integer
Private Sub Form_Load(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
Me.Width = 820
Me.Height = 253
Me.BackColor = Color.DarkSlateGray
DoubleBuffered = True
MyWorm = MyImage.Allocate(PictureBox1.CreateGraphics, rectangle)
With MyWorm.Graphics
.CompositingMode = Drawing2D.CompositingMode.SourceOver
.CompositingQuality = Drawing2D.CompositingQuality.AssumeLinear
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
.InterpolationMode = CType(Drawing2D.QualityMode.High, Drawing2D.InterpolationMode)
.PixelOffsetMode = Drawing2D.PixelOffsetMode.None
End With
PlotWorm(Nothing, Nothing)
End Sub
Public Sub PlotWorm(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
Do While x < 121
For y = 0 To 60 Step 6
x += 1
MyWorm.Graphics.DrawLine(pen3, 34 + x, 106 - y, 34 + x, 2 + y)
MyWorm.Render()
Next y
For y = 60 To 0 Step -6
x += 1
MyWorm.Graphics.DrawLine(pen3, 34 + x, 106 - y, 34 + x, 2 + y)
MyWorm.Render()
Next y
Loop
Do While x < 241
For y = 0 To -60 Step -5
x += 1
MyWorm.Graphics.DrawLine(pen2, 42 + x, 108 - y, 42 + x, 2 + y)
MyWorm.Render()
Next y
For y = -60 To 0 Step 5
x += 1
MyWorm.Graphics.DrawLine(pen2, 42 + x, 108 - y, 42 + x, 2 + y)
MyWorm.Render()
Next y
Loop
Do While x < 361
For y = 0 To 60 Step 4
x += 1
MyWorm.Graphics.DrawLine(pen3, 54 + x, 106 - y, 54 + x, 2 + y)
MyWorm.Render()
Next y
For y = 60 To 0 Step -4
x += 1
MyWorm.Graphics.DrawLine(pen3, 54 + x, 106 - y, 54 + x, 2 + y)
MyWorm.Render()
Next y
Loop
Do While x < 481
For y = 0 To -60 Step -3
x += 1
MyWorm.Graphics.DrawLine(pen2, 66 + x, 108 - y, 66 + x, 2 + y)
MyWorm.Render()
Next y
For y = -60 To 0 Step 3
x += 1
MyWorm.Graphics.DrawLine(pen2, 66 + x, 108 - y, 66 + x, 2 + y)
MyWorm.Render()
Next y
Loop
End Sub
End Class
I am looking forward to your advice and guidance.
Regards,
Antony
-
May 27th, 2026, 08:50 AM
#2
Re: Plotting persistent graphics to an object then rendering to PictureBox
It looks like you've put in a lot of effort at this point, but why aren't you using the built-in Chart control (documentation)?
A little help with ChatGPT came up with this:
Code:
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Private Shared areaName As String = "MainArea"
Private Shared seriesName As String = "Increment"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
SetupChart()
End Sub
' chart setup
Private Sub SetupChart()
ClearChart()
Chart1.ChartAreas.Add(CreateChartArea())
Chart1.Series.Add(CreateChartSeries())
End Sub
Private Sub ClearChart()
Chart1.Series.Clear()
Chart1.ChartAreas.Clear()
Chart1.Legends.Clear()
End Sub
Private Shared Function CreateChartArea() As ChartArea
Dim area As New ChartArea(areaName)
With area.AxisX
.Title = "Time"
.Interval = 1
.MajorGrid.Enabled = True
End With
With area.AxisY
.Title = "Total"
.Minimum = -6
.Maximum = 6
.Interval = 1
.Crossing = 0
.MajorGrid.Enabled = True
.StripLines.Add(New StripLine With {
.IntervalOffset = 0,
.StripWidth = 0,
.BorderColor = Color.Black,
.BorderWidth = 2,
.BorderDashStyle = ChartDashStyle.Solid
})
End With
Return area
End Function
Private Shared Function CreateChartSeries() As Series
Dim chartSeries As New Series(seriesName) With {
.ChartArea = areaName,
.ChartType = SeriesChartType.Column,
.IsVisibleInLegend = False
}
Return chartSeries
End Function
' chart changing
Private currentTime As Integer = 0
Private incrementalTotal As Double = 0
Private Sub AddIncomingValue(incrementValue As Double)
If (incrementValue < -6 OrElse incrementValue > 6) Then
Throw New ArgumentOutOfRangeException(NameOf(incrementValue), "Incoming increment must be between -6 and 6.")
End If
incrementalTotal += incrementValue
currentTime += 1
Dim pointIndex As Integer = Chart1.Series(seriesName).Points.AddXY(currentTime, incrementalTotal)
Dim point As DataPoint = Chart1.Series(seriesName).Points(pointIndex)
point.Color = If(incrementalTotal >= 0, Color.Blue, Color.Red)
Dim area As ChartArea = Chart1.ChartAreas(areaName)
ScrollXAxisIfNeeded(area, currentTime)
AdjustYAxisIfNeeded(area, incrementalTotal)
End Sub
Private Shared Sub ScrollXAxisIfNeeded(area As ChartArea, x As Double)
Dim visibleSeconds As Integer = 30
If (x > visibleSeconds) Then
area.AxisX.Minimum = x - visibleSeconds
area.AxisX.Maximum = x
Else
area.AxisX.Minimum = 0
area.AxisX.Maximum = visibleSeconds
End If
End Sub
Private Shared Sub AdjustYAxisIfNeeded(area As ChartArea, y As Double)
Dim absoluteMinimum As Double = Math.Abs(area.AxisY.Minimum)
Dim absoluteMaximum As Double = Math.Abs(area.AxisY.Maximum)
Dim maximumAbsoluteValue As Double = Math.Max(absoluteMinimum, absoluteMaximum)
Dim absoluteIncrementalTotal As Double = Math.Abs(y)
If (absoluteIncrementalTotal > maximumAbsoluteValue) Then
maximumAbsoluteValue = Math.Ceiling(Math.Abs(y))
area.AxisY.Minimum = -maximumAbsoluteValue
area.AxisY.Maximum = maximumAbsoluteValue
End If
End Sub
End Class
I tested it by adding a timer and adding a random value between -6 and 6 and it seemed to work well. It would adjust the "center line" after each value was added that exceeded the visible bounds.
-
May 27th, 2026, 09:37 AM
#3
Re: Plotting persistent graphics to an object then rendering to PictureBox
You need to incrementally keep track of all of your points. I’d suggest a form-level list(of point). Then, handle your PictureBox_Paint event and draw your graph there. As you have all of the information in a list, and handling the Paint event, the complete graph will be drawn each time the picturebox refreshes, thereby you’ll have persistent graphics…
That’s just a couple of steps beyond your current code. No need for a complete AI rewrite of your entire code. Where’s the fun in having AI write your app???
Last edited by .paul.; May 27th, 2026 at 09:40 AM.
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
May 28th, 2026, 08:07 PM
#4
Thread Starter
Member
Re: Plotting persistent graphics to an object then rendering to PictureBox
Hi dday, paul,
I like your suggestion of using a list(of Point) Paul, and can see how that would allow me to resume without loss of history if I write the list to a file as the data arrives. I was originally thinking of simply saving the bufferedgraphicscontext as an image to a file and reload it if there was an interruption, but saving the list is better. I'll do your suggestion as well and give that a go. There are a max total of 9600 datapoints(often less) that arrive over a 100 minute period and there can be up to 3 interruptions that occur before the full dataset arrives (data collection time is paused during an interruption).
Thanks for the suggestion to use the chart object dday.
I had actually been wrestling with chart for a couple of weeks and kept hitting roadblocks.
When I saw your code, I thought I would give it one more try as perhaps I had missed something.
So this is a result of my new attempt
Code:
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Private Shared areaName As String = "MainArea"
Private Shared seriesName As String = "Increment"
Private Sub _Load(sender As Object, e As EventArgs) Handles MyBase.Paint, Me.Load
With Me
.DoubleBuffered = True
.AllowTransparency = True
.Width = 820
.Height = 253
.BackColor = Color.DarkSlateGray
End With
Chart1.Width = 820
Chart1.Height = 253
'Chart1.BackColor = Color.Transparent
SetupChart()
End Sub
Private Sub SetupChart()
ClearChart()
Chart1.ChartAreas.Add(CreateChartArea())
Chart1.Series.Add(CreateChartSeries())
Chart1.Series(seriesName)("PointWidth") = "0.08"
End Sub
Private Sub ClearChart()
With Chart1
With .Series
.Clear()
End With
.ChartAreas.Clear()
.Legends.Clear()
'.BackColor = Color.Transparent
End With
End Sub
Private Shared Function CreateChartArea() As ChartArea
Dim area As New ChartArea(areaName)
area.BackColor = Color.Transparent
area.Position.X = 4.1
area.Position.Y = 12
area.Position.Height = 80
area.Position.Width = 92
With area.AxisX
.Minimum = 0
.Maximum = 9600
.Interval = 0.1
.MajorGrid.Enabled = False
.MinorGrid.Enabled = False
.LabelStyle.IsEndLabelVisible = False
.MinorTickMark.Enabled = False
.LabelStyle.Enabled = False
.IsMarginVisible = False
End With
With area.AxisY
.Minimum = -6
.Maximum = 6
.Interval = 1
.Crossing = 0
.MajorGrid.Enabled = False
.MinorGrid.Enabled = False
.LabelStyle.IsEndLabelVisible = False
.MinorTickMark.Enabled = False
.LabelStyle.Enabled = False
.IsMarginVisible = False
.StripLines.Add(New StripLine With {
.IntervalOffset = 0,
.StripWidth = 0,
.BorderColor = Color.Black,
.BorderWidth = 2,
.BorderDashStyle = ChartDashStyle.Solid
})
End With
Return area
End Function
Private Shared Function CreateChartSeries() As Series
Dim chartSeries As New Series(seriesName) With {
.ChartArea = areaName,
.ChartType = SeriesChartType.Column,
.IsVisibleInLegend = False,
.BorderColor = Color.Transparent
}
Return chartSeries
End Function
Private currentTime As Integer = 0
Private incrementalTotal As Double = 0
Private Sub AddIncomingValue(incrementValue As Double)
If (incrementValue < -6 OrElse incrementValue > 6) Then
Throw New ArgumentOutOfRangeException(NameOf(incrementValue), "Incoming increment must be between -6 and 6.")
End If
incrementalTotal += incrementValue
currentTime += 1
Dim pointIndex As Integer = Chart1.Series(seriesName).Points.AddXY(currentTime, incrementalTotal)
Dim point As DataPoint = Chart1.Series(seriesName).Points(pointIndex)
point.Color = If(incrementalTotal >= 0, Color.Blue, Color.Red)
Dim area As ChartArea = Chart1.ChartAreas(areaName)
ScrollXAxisIfNeeded(area, currentTime)
AdjustYAxisIfNeeded(area, incrementalTotal)
End Sub
Private Shared Sub ScrollXAxisIfNeeded(area As ChartArea, x As Double)
Dim visibleSeconds As Integer = 30
If (x > visibleSeconds) Then
area.AxisX.Minimum = x - visibleSeconds
area.AxisX.Maximum = x
Else
area.AxisX.Minimum = 0
area.AxisX.Maximum = visibleSeconds
End If
End Sub
Private Shared Sub AdjustYAxisIfNeeded(area As ChartArea, y As Double)
Dim absoluteMinimum As Double = Math.Abs(area.AxisY.Minimum)
Dim absoluteMaximum As Double = Math.Abs(area.AxisY.Maximum)
Dim maximumAbsoluteValue As Double = Math.Max(absoluteMinimum, absoluteMaximum)
Dim absoluteIncrementalTotal As Double = Math.Abs(y)
If (absoluteIncrementalTotal > maximumAbsoluteValue) Then
maximumAbsoluteValue = Math.Ceiling(Math.Abs(y))
area.AxisY.Minimum = -maximumAbsoluteValue
area.AxisY.Maximum = maximumAbsoluteValue
End If
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim upperbound = 6, lowerbound = -6
Dim rndvalue As Integer = Int((upperbound - lowerbound + 1) * Rnd() + lowerbound)
AddIncomingValue(rndvalue)
End Sub
End Class
While it does do something akin to what I was trying to accomplish, I still hit the same roadblocks. These are;
- When using background transparency, the entire chart disappears (I can get around this by creating a background image but I would have rathered have the chart object backgrounds set as transparent, so that only the Axis and Plots are visible
- When setting the point width to be a single thin line, the columns have gaps between them, and I have been unable to figure out how to remove the spaces between the columns.
I have tried adjusting the interval and the pointwidth as per MS but it either leaves spaces between the columns or the columns are to fat for the number to be plotted and end up causing unnecessary and unwanted chart area scrolling (looks nice though)
These are the two reasons I shied away from using the chart object but I am always willing to give anything a go. If you know how to remove the spaces between the columns then I am ready to give a chart object version a go and see how it works out with some live data.
-
May 29th, 2026, 08:40 AM
#5
Re: Plotting persistent graphics to an object then rendering to PictureBox
 Originally Posted by AntonyL
These are the two reasons I shied away from using the chart object but I am always willing to give anything a go. If you know how to remove the spaces between the columns then I am ready to give a chart object version a go and see how it works out with some live data.
Completely valid reasons, with that being said I'd go with .paul.'s recommendation.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|