Results 1 to 5 of 5

Thread: Plotting persistent graphics to an object then rendering to PictureBox

  1. #1

    Thread Starter
    Member
    Join Date
    Feb 2026
    Posts
    33

    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

  2. #2
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Posts
    12,370

    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.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | HtmlLessons | CssLessons | Code Tags | Sword of Fury - Jameram

  3. #3
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    26,413

    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.

  4. #4

    Thread Starter
    Member
    Join Date
    Feb 2026
    Posts
    33

    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.

  5. #5
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Posts
    12,370

    Re: Plotting persistent graphics to an object then rendering to PictureBox

    Quote Originally Posted by AntonyL View Post
    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.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | HtmlLessons | CssLessons | Code Tags | Sword of Fury - Jameram

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
  •  



Click Here to Expand Forum to Full Width