Click here to Skip to main content
12,552,615 members (35,029 online)
Click here to Skip to main content
Add your own
alternative version


11 bookmarked

Oscillograph, Bargraph and Linegraph in .NET

, 31 Jul 2016 CPOL
Rate this:
Please Sign up or sign in to vote.
Oscillograph, Bargraph and Linegraph made in Visual Studio with VB.NET



Occasions for the usage of this library are, when there is a need to illustrate any digital performance, such as the CPU performance, a time lapse, a volume meter etc.


I created the library by making use of a Superclass-Childclass hierarchy, by using Graph as the abstract Class. It itself also inherits a superior Class, the .NET UserControl. The base Class Graph is inherited by Oscillograph, Bargraph and Linegraph. It is set up the same way as when we add a new Form to our project, call it Form1 which inherits the Windows.Forms.Form. The abstract Class is Windows.Forms.Form and the inheritor or clone is Form1() in that case. Put together, it acts like a child that inherits parental attributes, but can further develop todate unknown features. For more information how abstract classes work, view the documentation at MSDN;

- Namespace Statistics

  • Public MustInherit Class Graph : Inherits UserControl
  • Public Class OscilloGraph : Inherits Graph
  • Public Class BarGraph: Inherits Graph
  • Public Class LineGraph: Inherits Graph

Using the code

The reason why we use a Superclass-Childclass hierarchy is because all our three Classes are meant to use identical features, thus saving us time and effort without having to write much code. I will explain the Graph in detail and later how BarGraph inherits it.

The Element Structure is meant to resemble a graph value. It contains two attributtes, the Value and Brush. The passed Value must range between 0 and 100.

Option Explicit On
Option Strict On

Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

<EditorBrowsable(EditorBrowsableState.Advanced), ComVisible(False)>
Public MustInherit Class Graph : Inherits UserControl
    'This Event is fired just before the animation is appended
    'one can draw stuff, such as indicating lines to make the statistic more attractive
    Public Event PreAnimationPaint(ByVal sender As Object, ByRef e As PaintEventArgs)

    Structure Element
        Sub New(ByVal value As Integer, ByVal brush As Brush)
            Me.Value = value
            Me.Brush = brush
        End Sub
        Public Value As Integer
        Public Brush As Brush
    End Structure

#Region "Fields"
    Private _ItemWidth As Integer = 2
    Private _Space As Integer = 1
    Private _Flow As Flows = Flows.Right
    Private Elements As New List(Of Element)
    Private _VisibleItems As Integer
#End Region

#Region "Enumeration"
    Enum Flows
        Top = 2
        Right = 1
        Bottom = 3
        Left = 0
    End Enum
#End Region

The Space property is literally the distance between each graph representing it's value. A value of 0 should clamp the graphs otherwise spread them respectively out. The overridable Flow property is overriden on the cloned Class, when for example no Flow.Top and Flow.Bottom attributes are selectable.

The VisibleItems property returns the count of elements that fit in the DisplayRectangle. If we declare the Control with a width of 130, and the element attributes with ElementWidth 10 and Space 5, the VisibleItems property will return 9 {130 / (10+5) = 8  (+ 1addition = 9)}. Other items that would not fit in the DisplayRectangle are not processed.

The Count Property returns the number of Elements present in the list.

#Region "Properties"
    ReadOnly Property VisibleItems As Integer
            Return Me._VisibleItems
        End Get
    End Property
    Property ElementWidth As Integer
            Return Me._ItemWidth
        End Get
        Set(value As Integer)
            Me._ItemWidth = value
        End Set
    End Property
    Property Space As Integer
            Return Me._Space
        End Get
        Set(ByVal value As Integer)
            Me._Space = value
        End Set
    End Property
    Overridable Property Flow As Flows
            Return Me._Flow
        End Get
        Set(ByVal value As Flows)
            Me._Flow = value
        End Set
    End Property
    ReadOnly Property Count As Integer
            Return Me.Elements.Count
        End Get
    End Property
#End Region
#Region "Methods"
    Sub New()
        Me.Size = New Size(110, 40)
        Me.MinimumSize = New Size(10, 10)
        Me.BackColor = Color.FromKnownColor(KnownColor.Control)
        Me.DoubleBuffered = True
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint, True)
    End Sub

The Install procedure receives a List(Of Element) as an argument. You declare such a list on your project then call this method with the list as reference. Once installed every time you insert/add an item to that list, the Graph Subclasses will already have the reference.

    Sub Install(ByRef LIST_OF_ELEMENTS As List(Of Element))
        Me.Elements = LIST_OF_ELEMENTS
    End Sub

The RefreshGraph method refreshes the Animation. You can call it from a Timer when a live statistic is performed, but consider not to call it on a Loop, such as For-Next, Do-While.

Public Sub RefreshGraph()
End Sub

The CalculateVisibleItems procedure  is called when the values of ElementWidth, Space and Flow are changed. It updates the VisibleItems property.

Private Sub CalculateVisibleItems()
    Dim result As Integer

    If (ElementWidth = 0) Then
        result = 0
        Select Case _Flow
            Case Flows.Right, Flows.Left
                If (Me.ClientSize.Width >= ElementWidth) Then
                    result = (Me.ClientSize.Width \ (ElementWidth + _Space)) + 1
                End If

            Case Flows.Top, Flows.Bottom
                If (Me.ClientSize.Height >= ElementWidth) Then
                    result = (Me.ClientSize.Height \ (ElementWidth + _Space)) + 1
                End If
        End Select
    End If
    Me._VisibleItems = result
End Sub
Private Sub Me_SizeChanged(ByVal sender As Object, ByVal e As EventArgs) Handles Me.SizeChanged
End Sub

Here we declare a global Event that is raised every single second. It serves the purpose to publish the graphical frame rates of the animation.

    Dim FPS As Integer = 0
    Public Event FramesPerSecondChanged(ByVal sender As Object, ByVal fps As Integer)
    Private WithEvents FPS_TIMER As New Windows.Forms.Timer() With {.Interval = 1000, .Enabled = True}
    Private Sub FPS_TIMER_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles FPS_TIMER.Tick
        RaiseEvent FramesPerSecondChanged(Me, FPS)
        FPS = 0
    End Sub


The overridable AnimationPaint procedure, is overriden on the cloned Classes, and there we write the code for the animation.

    Protected Overridable Sub AnimationPaint(ByRef e As PaintEventArgs, ByVal elements() As Element)
    End Sub
    Protected Overrides Sub OnPaint(e As PaintEventArgs)
        Using e
            e.Graphics.CompositingQuality = CompositingQuality.HighQuality
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
            e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality
            e.Graphics.InterpolationMode = InterpolationMode.High

            RaiseEvent PreAnimationPaint(Me, e)
            AnimationPaint(e, Elements.ToArray)

        End Using
        FPS = (FPS + 1)

    End Sub

That was all about the abstract class Graph, below i explain how it can be used to create all possible graphs. I will take the BarGraph as example:

<Browsable(True), EditorBrowsable(EditorBrowsableState.Always), ComVisible(True), ToolboxBitmap(GetType(BarGraph), "bargraph.png"), Description("Bargraph")>
Public Class BarGraph : Inherits Graph

We override the Flow property, as we will not use some of it's features. The constructor MyBase refers to the base (abstract) class Graph. The attributtes Top and Bottom are not needed for the BarGraph as it makes no sense. We replace any of the two with the Left attribute, thus we will use only the Left and Right, Flow features.

Public Overrides Property Flow As Flows
         Return MyBase.Flow
     End Get
     Set(value As Flows)
         If (value = Flows.Top) Or (value = Flows.Bottom) Then
             value = Flows.Left
         End If
         MyBase.Flow = value
     End Set
 End Property

Here we override the AnimationPaint method located on the base Graph. Do not dispose of the e variable referencing the PaintEventArgs as the base class will take care of.

Protected Overrides Sub DrawAnimation(ByRef e As PaintEventArgs, elements() As Element)
    'Note, the first item in the array is the newest added, the last one the oldest
    'the elements range is 1-based, while the index is 0-based
    If (Not Count = 0) Then
        For i = 1 To Count
            If (i > VisibleItems) Then Exit For

            Dim _element As Element = elements(i - 1)
            Dim rect As Rectangle = Me.BlockRectCollapsed(i, _element.Value)
            'draw the element into the control,
            e.Graphics.FillRectangle(_element.Brush, rect)
            'dispose the variables  that are not needed anymore
            _element = Nothing
            rect = Nothing
        elements = Nothing
    End If
End Sub

Private Function BlockRectCollapsed(ByVal index As Integer, ByVal value As Integer) As Rectangle
    Dim x, y, block_width, block_height As Integer

    Select Case Flow
        Case Flows.Left
            block_width = ElementWidth
            x = Space + CInt((index - 1) * (Space + block_width))
            block_height = CInt((Me.ClientSize.Height / 100) * (value))
            y = CInt((Me.ClientSize.Height - block_height))

        Case Flows.Right
            block_width = ElementWidth
            x = CInt(Me.ClientSize.Width - ((index) * (Space + block_width)))
            block_height = CInt((Me.ClientSize.Height / 100) * (value))
            y = CInt((Me.ClientSize.Height - block_height))

    End Select

    Return New Rectangle(x, y, block_width, block_height)
End Function

How can the graphs be used in our application?

This example produces a statistic with 5 values and also shows the implementation of the PreAnimationPaint Event handling; New items are to be inserted always at the begining of the array.

 Public Class Form1 
    Friend WithEvents BarGraph1 As New BarGraph() 
    Dim ITEMS As New List(Of Graph.Element) 
    Private Sub Me_Load() Handles Me.Load 
           BarGraph1.Size = New Size(220,80)
           BarGraph1.Location = New Point(50,50) 
           Me.Controls.Add( BarGraph1 )
           BarGraph1.Install( ITEMS )                      
           ITEMS.Insert(0, New Graph.Element(10, Brushes.BLUE))
           ITEMS.Insert(0, New Graph.Element(20, Brushes.LIMEGREEN)) 
           ITEMS.Insert(0, New Graph.Element(30, Brushes.Green)) 
           ITEMS.Insert(0, New Graph.Element(50, Brushes.YELLOW)) 
           ITEMS.Insert(0, New Graph.Element(100, Brushes.RED)) 
    End Sub
    Private Sub BarGraph1_PreAnimationPaint(ByVal sender As Object, ByRef e As PaintEventArgs) Handles BarGraph1.PreAnimationPaint
        'Draw Lines
        For i = 1 To 4
            e.Graphics.DrawLine(Pens.Blue, New Point(0, i * BarGraph1.Height \ 5), New Point(BarGraph1.Width, i * BarGraph1.Height \ 5))
    End Sub
End Class 



  • 6.July.2016 - The list of items is now maintained externaly. Added functionality to represent each item with unique brush.
  • 31.July.2016 - Added PreAnimationPaint Eventhandler and updated the library.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Albania Albania
Hobby Programmer

"The men who marched to Babylon, Persia and India were the ancestors of the Albanians..." Wadham Peacock (1913)

You may also be interested in...

Comments and Discussions

GeneralMy vote of 1 Pin
Alexey KK8-Aug-16 21:13
professionalAlexey KK8-Aug-16 21:13 
GeneralRe: My vote of 1 Pin
Gegniani8-Aug-16 23:39
memberGegniani8-Aug-16 23:39 
GeneralRe: My vote of 1 Pin
Alexey KK8-Aug-16 23:46
professionalAlexey KK8-Aug-16 23:46 
GeneralRe: My vote of 1 Pin
Gegniani9-Aug-16 0:04
memberGegniani9-Aug-16 0:04 
GeneralRe: My vote of 1 Pin
Alexey KK9-Aug-16 0:13
professionalAlexey KK9-Aug-16 0:13 
GeneralMy vote of 4 Pin
Muhammad Shahid Farooq30-Jul-16 18:58
professionalMuhammad Shahid Farooq30-Jul-16 18:58 
GeneralRe: My vote of 4 Pin
Gegniani31-Jul-16 0:45
memberGegniani31-Jul-16 0:45 
Questionthanks for reply Pin
avisal11-Jul-16 8:19
memberavisal11-Jul-16 8:19 
AnswerRe: thanks for reply Pin
Gegnjani11-Jul-16 22:17
memberGegnjani11-Jul-16 22:17 
QuestionDrawing Pin
avisal7-Jul-16 6:39
memberavisal7-Jul-16 6:39 
AnswerRe: Drawing Pin
Gegnjani11-Jul-16 8:02
memberGegnjani11-Jul-16 8:02 
QuestionMessage Removed Pin
Gegnjani4-Jul-16 13:12
memberGegnjani4-Jul-16 13:12 
GeneralMy vote of 5 Pin
raiserle4-Jul-16 7:35
memberraiserle4-Jul-16 7:35 
GeneralRe: My vote of 5 Pin
Gegnjani4-Jul-16 13:07
memberGegnjani4-Jul-16 13:07 
QuestionHard but helpful ! Pin
XavierRossignol3-Jul-16 4:22
memberXavierRossignol3-Jul-16 4:22 
AnswerRe: Hard but helpful ! Pin
Gegnjani4-Jul-16 13:08
memberGegnjani4-Jul-16 13:08 
AnswerRe: Hard but helpful ! Pin
Alexey KK8-Aug-16 21:26
professionalAlexey KK8-Aug-16 21:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161021.1 | Last Updated 31 Jul 2016
Article Copyright 2016 by Gegniani
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid