Click here to Skip to main content
15,892,575 members
Articles / Desktop Programming / Windows Forms

Customizable Tracker Control

Rate me:
Please Sign up or sign in to vote.
4.64/5 (14 votes)
6 Jun 2010CPOL3 min read 30.2K   1K   40  
Windows Forms Control to visualize performance counters
Option Strict On

Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Windows.Forms.Control
Imports System.Threading

<Description("TrackerControl 2.0")> _
Public Class TrackerControl

  Inherits System.Windows.Forms.Control

#Region "Private and public Declarations"

  Private GraphThreadDelegate As New ThreadStart(AddressOf InternalThread)
  Private GraphThread As New Thread(GraphThreadDelegate)
  Private mover As Integer = 0
  Private StopFlag As Boolean = False
  Private _Avg As Integer = 0
  Public Enum eTrackerRefresh
    Enabled = 0
    Disabled = 1
  End Enum
  Private _TrackerRefreshInDesignMode As eTrackerRefresh = eTrackerRefresh.Disabled
  Private _TrackerRefreshInRuntimeMode As eTrackerRefresh = eTrackerRefresh.Enabled
  Private _TrackerRefreshStartOnFirstValue As Boolean = False
  Private _Value As Integer = 0
  Private _Values As New List(Of Integer)
  Private _ResetValueBuffer As Boolean = False
  Private _Minimum As Integer = 0
  Private _Maximum As Integer = 100
  Private _InitialMaximum As Integer = 100
  Private _StartValue As Integer = 0
  Private _HighlightedRangeLowerBound As Integer = 25
  Private _HighlightedRangeUpperBound As Integer = 75
  Public Enum eRefreshingTime
    ValueTriggered = -1
    Custom = 0
    Running = 20
    Fast = 100
    Medium = 500
    Slow = 1000
  End Enum
  Private _RefreshInterval As eRefreshingTime = eRefreshingTime.Slow
  Private _CustomRefreshInterval As Integer = eRefreshingTime.Slow
  Private Const intDivision As Integer = 2
  Private _ShowGridLines As eShowGridLines = eShowGridLines.Both
  Public Enum eShowGridLines
    Both = 0
    Horizontal = 1
    Vertical = 2
    None = 3
  End Enum
  Private _GridSize As Integer = 12
  Private _GridSizeForeColor As Color = Color.DarkGreen
  Private _GridSizeBackColor As Color = Color.Black
  Public Enum eGridMovement
    MoveWithGraph = 0
    Fixed = 1
  End Enum
  Private _GridMovement As eGridMovement = eGridMovement.MoveWithGraph
  Private _ShowHighlightedRange As Boolean = False
  Private _MinimumColor As Color = Color.Yellow
  Private _MaximumColor As Color = Color.Red
  Private _FillGraph As Boolean = False
  Public Enum eGraphSizeMode
    Fixed = 1
    Zoom = 2
    AutoAdjustMaximum = 3
  End Enum
  Private _GraphSizeMode As eGraphSizeMode = eGraphSizeMode.Fixed
  Private _AvgBarShow As Boolean = False
  Private _AvgBarMaximumColor As Color = Color.Yellow
  Private _AvgBarMininumColor As Color = Color.Lime
  Private _objLockValues As New Object

#End Region

#Region "Private Procedures"

  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    ' TrackerControl
    Me.BackColor = System.Drawing.SystemColors.WindowText
    Me.Text = "TrackerControl 2.0"

  End Sub

  ''' <summary>
  ''' Internal thread for handling values and calculations
  ''' </summary>
  ''' <remarks></remarks>
  Private Sub InternalThread()

    ' Loop until Stop is requested
    Do While StopFlag = False

      ' Do nothing but also do not end thread, if refreshing is set to disabled.
      If Me.DesignMode And Me._TrackerRefreshInDesignMode = eTrackerRefresh.Disabled Then
        Thread.Sleep(_RefreshInterval)
        Continue Do
      ElseIf Not Me.DesignMode And Me._TrackerRefreshInRuntimeMode = eTrackerRefresh.Disabled Then
        If Me._Values.Count > 0 And Not Me._TrackerRefreshStartOnFirstValue Then
          Thread.Sleep(_RefreshInterval)
          Continue Do
        End If
      End If

      ' Flush internal value buffer, if requested
      If Me._ResetValueBuffer Then
        SyncLock Me._objLockValues
          Me._Values.Clear()
          ' Note that the job is done
          Me._ResetValueBuffer = False
        End SyncLock
        ' Skip processing this time
        Continue Do
      End If

      If mover >= Me._GridSize - intDivision Then
        mover = 0
      Else
        mover += intDivision
      End If

      If Me._GraphSizeMode = eGraphSizeMode.AutoAdjustMaximum Or Me._GraphSizeMode = eGraphSizeMode.Zoom Then

        ' AutoAdjustMaximum: Set Maximum to the highest available value.
        ' But make sure, that Maximum never falls below the initially set Maximum value (kind of lower bound for Maximum)

        Dim CurMax As Integer = 0
        For Each TmpValue As Integer In Me._Values
          If TmpValue > CurMax Then
            CurMax = TmpValue
          End If
        Next

        If Me._GraphSizeMode = eGraphSizeMode.AutoAdjustMaximum Then

          If CurMax > Me._InitialMaximum Then
            Me._Maximum = CurMax
          Else
            Me._Maximum = Me._InitialMaximum
          End If

          If Me._Value > Me._Maximum Then
            Me._Maximum = Me._Value
          End If

        ElseIf Me._GraphSizeMode = eGraphSizeMode.Zoom Then

          ' Zoom: Adjust Maximum, but never above the user set Maximum.

          If CurMax > Me._Minimum + 1 Then
            If CurMax < Me._InitialMaximum Then
              Me._Maximum = CurMax
            Else
              Me._Maximum = Me._InitialMaximum
            End If
          Else
            CurMax = Me._Minimum + 1
          End If

        End If

      End If

      SyncLock Me._objLockValues
        If Me._Values.Count = 0 Then
          Me._Values.Add(Me._StartValue)
        ElseIf Me._Values.Count < (Me.Width) / intDivision Then
          Me._Values.Add(_Value)
        Else
          Me._Values.RemoveAt(0)
          Me._Values.Add(_Value)
        End If
      End SyncLock

      ' Calculate Average
      If Me._AvgBarShow Then
        Dim valuesum As Integer = 0
        Dim NumberOfValuesForAvg As Integer
        For i = 1 To Me._Values.Count - 1
          valuesum += Me._Values(i)
        Next
        'NumberOfValuesForAvg = Convert.ToInt32(Me.Width / intDivision)
        NumberOfValuesForAvg = Me._Values.Count
        Me._Avg = Convert.ToInt32(valuesum / NumberOfValuesForAvg)
      End If

      ' Provoke redrawing of control
      If Not Me.StopFlag Then Me.Invalidate()

      ' Stick to the RefreshInterval
      If Me._RefreshInterval = eRefreshingTime.Custom Then
        Thread.Sleep(Me._CustomRefreshInterval)
      Else
        Thread.Sleep(_RefreshInterval)
      End If

    Loop

    ' Internal Thread is about to end -> flush value list
    SyncLock Me._objLockValues
      Me._Values.Clear()
    End SyncLock

  End Sub

  ''' <summary>
  ''' Recalculate a value using a given iFactor
  ''' </summary>
  ''' <param name="iFactor"></param>
  ''' <param name="Value"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Private Function CnvValue(ByVal iFactor As Double, ByVal Value As Integer) As Integer

    Dim Result As Integer

    Result = Convert.ToInt32(Math.Round(iFactor * Convert.ToDouble(Value), 0, MidpointRounding.AwayFromZero))

    Return Result

  End Function

  ''' <summary>
  ''' Ascertain the gradient color for a given y-coordinate
  ''' </summary>
  ''' <param name="StartColor"></param>
  ''' <param name="EndColor"></param>
  ''' <param name="YCoord"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Private Function GetGradientColorAccordingToYCoord(ByVal StartColor As Color, ByVal EndColor As Color, ByVal YCoord As Integer) As Color

    Dim RGBResult As sRGBColorStructure

    Dim StartRGB As sRGBColorStructure = GetRGBColorStructure(StartColor)
    Dim EndRGB As sRGBColorStructure = GetRGBColorStructure(EndColor)
    Dim TransitionSteps As Integer = Me.Height
    Dim NormalizedValue As Integer = TransitionSteps - (YCoord)

    If Me._GraphSizeMode = eGraphSizeMode.Zoom Then
      If Me._Maximum < Me._InitialMaximum Then
        NormalizedValue = Convert.ToInt32((Me._Maximum / Me._InitialMaximum) * NormalizedValue)
      End If
    End If

    With RGBResult
      .Red = StartRGB.Red + Convert.ToInt32(Math.Round((NormalizedValue * (EndRGB.Red - StartRGB.Red) / TransitionSteps), 0, MidpointRounding.ToEven))
      .Green = StartRGB.Green + Convert.ToInt32(Math.Round((NormalizedValue * (EndRGB.Green - StartRGB.Green) / TransitionSteps), 0, MidpointRounding.ToEven))
      .Blue = StartRGB.Blue + Convert.ToInt32(Math.Round((NormalizedValue * (EndRGB.Blue - StartRGB.Blue) / TransitionSteps), 0, MidpointRounding.ToEven))
    End With

    Return Color.FromArgb(RGBResult.Red, RGBResult.Green, RGBResult.Blue)

  End Function

  ''' <summary>
  ''' Structure representing a RGB value triple
  ''' </summary>
  ''' <remarks></remarks>
  Private Structure sRGBColorStructure
    Dim Red As Integer
    Dim Green As Integer
    Dim Blue As Integer
  End Structure

  ''' <summary>
  ''' Dissociation of a color into RGB values
  ''' </summary>
  ''' <param name="SystemColor"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Private Function GetRGBColorStructure(ByVal SystemColor As Color) As sRGBColorStructure

    Dim Result As sRGBColorStructure

    Dim hexColor As String = SystemColor.ToArgb().ToString("X8").Substring(2, 6)

    'Remove # if present
    If hexColor.IndexOf("#") <> -1 Then
      hexColor = hexColor.Replace("#", "")
    End If

    Dim red As Integer = 0
    Dim green As Integer = 0
    Dim blue As Integer = 0

    If hexColor.Length = 6 Then
      '#RRGGBB
      red = Integer.Parse(hexColor.Substring(0, 2), Globalization.NumberStyles.AllowHexSpecifier)
      green = Integer.Parse(hexColor.Substring(2, 2), Globalization.NumberStyles.AllowHexSpecifier)
      blue = Integer.Parse(hexColor.Substring(4, 2), Globalization.NumberStyles.AllowHexSpecifier)
    ElseIf hexColor.Length = 3 Then
      '#RGB
      red = Integer.Parse(hexColor(0).ToString() + hexColor(0).ToString(), Globalization.NumberStyles.AllowHexSpecifier)
      green = Integer.Parse(hexColor(1).ToString() + hexColor(1).ToString(), Globalization.NumberStyles.AllowHexSpecifier)
      blue = Integer.Parse(hexColor(2).ToString() + hexColor(2).ToString(), Globalization.NumberStyles.AllowHexSpecifier)
    End If

    With Result
      .Red = red
      .Green = green
      .Blue = blue
    End With

    Return Result

  End Function

#End Region

#Region "Public Properties"

  Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
    Get
      Dim mSize As System.Drawing.Size = New System.Drawing.Size(200, 100)
      Return mSize
    End Get
  End Property

  <Category("Behavior"), _
  DefaultValue(eRefreshingTime.Slow), _
  Description("Sets the refreshing time of the TrackerControl.")> _
  Public Property RefreshingTime() As eRefreshingTime
    Get
      Return Me._RefreshInterval
    End Get
    Set(ByVal Value As eRefreshingTime)
      Me._RefreshInterval = Value
    End Set
  End Property

  <Category("Behavior"), _
DefaultValue(eRefreshingTime.Slow), _
Description("Sets a custom refresh interval in milliseconds for the TrackerControl. RefreshingTime needs to be set to Custom to control this setting.")> _
    Public Property CustomRefreshInterval() As Integer
    Get
      Return Me._CustomRefreshInterval
    End Get
    Set(ByVal Value As Integer)
      If Value < eRefreshingTime.Slow Then
        Me._CustomRefreshInterval = eRefreshingTime.Slow
      Else
        Me._CustomRefreshInterval = Value
      End If
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(eTrackerRefresh.Disabled), _
  Description("Enable or disable refreshing of the TrackerControl in design mode.")> _
  Public Property TrackerRefreshInDesignMode() As eTrackerRefresh
    Get
      Return Me._TrackerRefreshInDesignMode
    End Get
    Set(ByVal value As eTrackerRefresh)
      Me._TrackerRefreshInDesignMode = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(eTrackerRefresh.Enabled), _
  Description("Enable or disable refreshing of the TrackerControl in runtime mode.")> _
  Public Property TrackerRefreshInRuntimeMode() As eTrackerRefresh
    Get
      Return Me._TrackerRefreshInRuntimeMode
    End Get
    Set(ByVal value As eTrackerRefresh)
      Me._TrackerRefreshInRuntimeMode = value
    End Set
  End Property

  <Category("Behavior"), _
DefaultValue(False), _
Description("Enable refreshing of the TrackerControl on first value, even if TrackerRefreshInRuntimeMode is set to Disabled.")> _
Public Property TrackerRefreshStartOnFirstValue() As Boolean
    Get
      Return Me._TrackerRefreshStartOnFirstValue
    End Get
    Set(ByVal value As Boolean)
      Me._TrackerRefreshStartOnFirstValue = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(eShowGridLines.Both), _
  Description("Show grid lines in the background of the TrackerControl.")> _
  Public Property ShowGridLines() As eShowGridLines
    Get
      Return Me._ShowGridLines
    End Get
    Set(ByVal value As eShowGridLines)
      Me._ShowGridLines = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(12), _
  Description("The grid size (mesh) in pixels of the TrackerControl.")> _
      Public Property GridSize() As Integer
    Get
      Return Me._GridSize
    End Get
    Set(ByVal Value As Integer)
      If Value > 0 Then Me._GridSize = Value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.DarkGreen"), _
  Description("Color of the grid lines of the TrackerControl.")> _
  Public Property GridForeColor() As Color
    Get
      Return Me._GridSizeForeColor
    End Get
    Set(ByVal value As Color)
      Me._GridSizeForeColor = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.Black"), _
  Description("Background color of the TrackerControl.")> _
  Public Property GridBackColor() As Color
    Get
      Return Me._GridSizeBackColor
    End Get
    Set(ByVal value As Color)
      Me._GridSizeBackColor = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(eGridMovement.MoveWithGraph), _
  Description("Move the grid with the graph when refreshing or fix it to background of the TrackerControl.")> _
  Public Property GridMovement() As eGridMovement
    Get
      Return Me._GridMovement
    End Get
    Set(ByVal value As eGridMovement)
      Me._GridMovement = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(50), _
  Description("The current value of the TrackerControl. If GraphSizeMode is set to AutoAdjustMaximum any value >= Minimum is accepted. Otherwise only values in the range specified by the Minimum and Maximum settings are allowed.")> _
  Public Property Value() As Integer
    Get
      Return Me._Value
    End Get
    Set(ByVal Value As Integer)

      If Me._GraphSizeMode = eGraphSizeMode.AutoAdjustMaximum Then

        If Me._Value >= Me._Minimum Then
          Me._Value = Value
        Else
          Me._Value = Me._Minimum
        End If

      Else

        If Value > Me._Maximum Then
          '_Value = me._Maximum
          Me._Maximum = Value
          Me._Value = Value
        ElseIf Value < Me._Minimum Then
          Me._Value = Me._Minimum
        Else
          Me._Value = Value
        End If

      End If

    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(0), _
  Description("The lower bound of the range the TrackerControl is working with.")> _
  Public Property Minimum() As Integer
    Get
      Return Me._Minimum
    End Get
    Set(ByVal value As Integer)
      If value > Me._HighlightedRangeLowerBound Then
        Me._Minimum = Me._HighlightedRangeLowerBound
      Else
        Me._Minimum = value
      End If
      Me.Invalidate()
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(100), _
  Description("The upper bound of the range this Tracker is working with. When GraphSizeMode is set to AutoAdjustMaximum this value is adjusted dynamically.")> _
  Public Property Maximum() As Integer
    Get
      Return Me._Maximum
    End Get
    Set(ByVal value As Integer)
      If value < Me._HighlightedRangeUpperBound And Me._ShowHighlightedRange Then
        Me._Maximum = Me._HighlightedRangeUpperBound
      Else
        Me._Maximum = value
      End If
      Me._InitialMaximum = Me._Maximum
      Me.Invalidate()
    End Set
  End Property

  <Category("Behavior"), _
DefaultValue(0), _
Description("Sets the start value of the TrackerControl, within the range specified by the Minimum and Maximum settings.")> _
Public Property StartValue() As Integer
    Get
      Return Me._StartValue
    End Get
    Set(ByVal Value As Integer)

      If Me._GraphSizeMode = eGraphSizeMode.AutoAdjustMaximum Then

        Me._StartValue = Value

      Else

        If Value > Me._Maximum Then
          '_Value = me._Maximum
          Me._Maximum = Value
          Me._StartValue = Value
        ElseIf Value < Me._Minimum Then
          Me._StartValue = Me._Minimum
        Else
          Me._StartValue = Value
        End If

      End If

    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(75), _
  Description("The upper bound of the highlighted range.")> _
  Public Property HighlightedRangeUpperBound() As Integer
    Get
      Return Me._HighlightedRangeUpperBound
    End Get
    Set(ByVal value As Integer)
      If value > Me._Maximum Then
        Me._HighlightedRangeUpperBound = Me._Maximum
      ElseIf value < Me._HighlightedRangeLowerBound Then
        Me._HighlightedRangeUpperBound = Me._HighlightedRangeLowerBound
      Else
        Me._HighlightedRangeUpperBound = value
      End If
      Me.Invalidate()
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(25), _
  Description("The lower bound of the highlighted range.")> _
  Public Property HighlightedRangeLowerBound() As Integer
    Get
      Return Me._HighlightedRangeLowerBound
    End Get
    Set(ByVal value As Integer)
      If value > Me._HighlightedRangeUpperBound Then
        Me._HighlightedRangeLowerBound = Me._HighlightedRangeUpperBound
      ElseIf value < Me._Minimum Then
        Me._HighlightedRangeLowerBound = Me._Minimum
      Else
        Me._HighlightedRangeLowerBound = value
      End If
      Me.Invalidate()
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(False), _
  Description("Highlight the area between HighlightedRangeLowerBound and HighlightedRangeUpperBound. Only applies when GraphSizeMode is set to Fixed.")> _
  Public Property ShowHighlightedRange() As Boolean
    Get
      Return Me._ShowHighlightedRange
    End Get
    Set(ByVal value As Boolean)
      Me._ShowHighlightedRange = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.Yellow"), _
  Description("The lower gradient color (minimum color).")> _
  Public Property MinimumColor() As Color
    Get
      Return Me._MinimumColor
    End Get
    Set(ByVal value As Color)
      Me._MinimumColor = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.Red"), _
  Description("The upper gradient color (maximum color).")> _
  Public Property MaximumColor() As Color
    Get
      Return Me._MaximumColor
    End Get
    Set(ByVal value As Color)
      Me._MaximumColor = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(False), _
  Description("Colorize area underneath garph using the appropriate gradient.")> _
  Public Property FillGraph() As Boolean
    Get
      Return Me._FillGraph
    End Get
    Set(ByVal value As Boolean)
      Me._FillGraph = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(eGraphSizeMode.Fixed), _
  Description("Sizing of drawn graph. If set to Zoom and vertical grid lines are shown, number of and distance between the vertical grid lines are adjusted dynamically.")> _
  Public Property GraphSizeMode() As eGraphSizeMode
    Get
      Return Me._GraphSizeMode
    End Get
    Set(ByVal value As eGraphSizeMode)
      Me._GraphSizeMode = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(False), _
  Description("Show calculated average of displayed values as a horizontal bar.")> _
  Public Property AvgBarShow() As Boolean
    Get
      Return Me._AvgBarShow
    End Get
    Set(ByVal value As Boolean)
      Me._AvgBarShow = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.Lime"), _
  Description("The avg bar minimum gradient color.")> _
  Public Property AvgBarMinimumColor() As Color
    Get
      Return Me._AvgBarMininumColor
    End Get
    Set(ByVal value As Color)
      Me._AvgBarMininumColor = value
    End Set
  End Property

  <Category("Behavior"), _
  DefaultValue(GetType(Color), "Color.Yellow"), _
  Description("The avg bar maximum gradient color.")> _
  Public Property AvgBarMaximumColor() As Color
    Get
      Return Me._AvgBarMaximumColor
    End Get
    Set(ByVal value As Color)
      Me._AvgBarMaximumColor = value
    End Set
  End Property

#End Region

#Region "Public Procedures"

  Public Sub New()
    MyBase.New()

    InitializeComponent()

    SetStyle(ControlStyles.ResizeRedraw, True)
    SetStyle(ControlStyles.UserPaint, True)
    SetStyle(ControlStyles.AllPaintingInWmPaint, True)
    SetStyle(ControlStyles.DoubleBuffer, True)
    GraphThread.Start()

  End Sub

  ''' <summary>
  ''' Stop internal thread
  ''' </summary>
  ''' <param name="disposing"></param>
  ''' <remarks></remarks>
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    ' Set stop flag for internal thread
    StopFlag = True
    MyBase.Dispose(disposing)
  End Sub

  ''' <summary>
  ''' Primary graph drawing procedure
  ''' </summary>
  ''' <param name="e"></param>
  ''' <remarks></remarks>
  Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

    MyBase.OnPaint(e)

    Dim i As Integer

    ' Draw grid lines according to control settings
    If Me._ShowGridLines = eShowGridLines.Both Or Me._ShowGridLines = eShowGridLines.Horizontal Then
      If Me._GraphSizeMode = eGraphSizeMode.Zoom Then
        For i = 1 To CInt(Me.Height / (Me._GridSize * (1 / (Me._Maximum / Me._InitialMaximum))))
          e.Graphics.DrawLine(New Pen(Me._GridSizeForeColor), 0, Convert.ToInt32(i * (Me._GridSize * (1 / (Me._Maximum / Me._InitialMaximum)))), Me.Width, Convert.ToInt32(i * (Me._GridSize * (1 / (Me._Maximum / Me._InitialMaximum)))))
        Next i
      Else
        For i = 1 To CInt(Me.Height / Me._GridSize)
          e.Graphics.DrawLine(New Pen(Me._GridSizeForeColor), 0, i * Me._GridSize, Me.Width, i * Me._GridSize)
        Next i
      End If
    End If
    If Me._ShowGridLines = eShowGridLines.Both Or Me._ShowGridLines = eShowGridLines.Vertical Then
      For i = 1 To CInt(Me.Width / Me._GridSize)
        If Me._GridMovement = eGridMovement.MoveWithGraph Then
          e.Graphics.DrawLine(New Pen(Me._GridSizeForeColor), i * Me._GridSize - mover, 0, i * Me._GridSize - mover, Me.Height)
        Else
          e.Graphics.DrawLine(New Pen(Me._GridSizeForeColor), i * Me._GridSize, 0, i * Me._GridSize, Me.Height)
        End If
      Next i
    End If

    Dim iMaximum As Integer
    Dim iMinimum As Integer
    Dim iFactor As Double

    ' Recalculate Minimum und Maximum to iMinimum and iMaximum: Normalize to pixel height of control
    If Me._Maximum < Me.Height Then
      iMaximum = Me.Height
      iFactor = Convert.ToDouble(Me.Height) / Convert.ToDouble(_Maximum)
    Else
      iMaximum = Me._Maximum
      iFactor = 1
    End If

    If Me._Minimum > 0 Then
      iMinimum = Convert.ToInt32(Math.Round(iFactor * Convert.ToDouble(_Minimum), 0, MidpointRounding.AwayFromZero))
    Else
      iMinimum = 0
    End If

    ' Define brush and pen for later use
    Dim localBrush As Drawing2D.LinearGradientBrush = Nothing
    Dim localPen As Pen = Nothing

    ' Draw graph (coherent lines) and fill area underneath if applicable
    For i = 1 To Me._Values.Count - 1

      ' Normalize value tuple for current line
      Dim StartValue As Integer = CnvValue(iFactor, Convert.ToInt32(_Values(i)))
      Dim EndValue As Integer = CnvValue(iFactor, Convert.ToInt32(_Values(i - 1)))

      ' Translate value tuple to actual coordinates within control
      Dim StartPoint As New Point(Me.Width - intDivision * (_Values.Count - i), CInt(Me.Height * (iMaximum - CnvValue(iFactor, Convert.ToInt32(_Values(i)))) / (iMaximum - iMinimum)))
      Dim EndPoint As New Point(Me.Width - intDivision * (_Values.Count - i + 1), CInt(Me.Height * (iMaximum - CnvValue(iFactor, Convert.ToInt32(_Values(i - 1)))) / (iMaximum - iMinimum)))

      ' Determine start and end color of current line
      Dim StartColor As Color = GetGradientColorAccordingToYCoord(Me._MinimumColor, Me._MaximumColor, StartPoint.Y)
      Dim EndColor As Color = GetGradientColorAccordingToYCoord(Me._MinimumColor, Me._MaximumColor, EndPoint.Y)

      If Not localBrush Is Nothing Then localBrush.Dispose()
      localBrush = New Drawing2D.LinearGradientBrush(StartPoint, EndPoint, StartColor, EndColor)
      localBrush.WrapMode = Drawing2D.WrapMode.TileFlipX
      localBrush.GammaCorrection = True

      If localPen Is Nothing Then
        localPen = New Pen(localBrush, 1)
      Else
        localPen.Brush = localBrush
        localPen.Width = 1
      End If
      localPen.StartCap = Drawing2D.LineCap.Round
      localPen.EndCap = Drawing2D.LineCap.Round

      ' Draw the line
      e.Graphics.DrawLine(localPen, StartPoint, EndPoint)

      ' If applicable, fill the area underneath the line
      If Me._FillGraph Then
        Do Until StartValue = iMinimum And EndValue = iMinimum

          StartValue -= 1
          EndValue -= 1

          If StartValue < iMinimum Then StartValue = iMinimum
          If EndValue < iMinimum Then EndValue = iMinimum

          StartPoint = New Point(Me.Width - intDivision * (_Values.Count - i), CInt(Me.Height * (iMaximum - StartValue) / (iMaximum - iMinimum)))
          EndPoint = New Point(Me.Width - intDivision * (_Values.Count - i + 1), CInt(Me.Height * (iMaximum - EndValue) / (iMaximum - iMinimum)))

          StartColor = GetGradientColorAccordingToYCoord(Me._MinimumColor, Me._MaximumColor, StartPoint.Y)
          EndColor = GetGradientColorAccordingToYCoord(Me._MinimumColor, Me._MaximumColor, EndPoint.Y)

          localBrush.Dispose()
          localBrush = New Drawing2D.LinearGradientBrush(StartPoint, EndPoint, StartColor, EndColor)
          localBrush.WrapMode = Drawing2D.WrapMode.TileFlipX
          localBrush.GammaCorrection = True

          localPen.Brush = localBrush
          localPen.Width = 1

          e.Graphics.DrawLine(localPen, StartPoint, EndPoint)

        Loop
      End If

    Next i

    ' Show average value as horizontal bar
    If Me._AvgBarShow Then

      Dim x1 As Integer = 0
      Dim y1 As Integer = Convert.ToInt32(Me.Height * (iMaximum - CnvValue(iFactor, Convert.ToInt32(Me._Avg))) / (iMaximum - iMinimum))
      Dim x2 As Integer = Me.Width
      Dim y2 As Integer = Convert.ToInt32(Me.Height * (iMaximum - CnvValue(iFactor, Convert.ToInt32(Me._Avg))) / (iMaximum - iMinimum))
      Dim BarColor As Color = GetGradientColorAccordingToYCoord(Me._AvgBarMininumColor, Me._AvgBarMaximumColor, y1)
      e.Graphics.DrawLine(New Pen(BarColor), x1, y1, x2, y2)

    End If

    ControlPaint.DrawBorder3D(e.Graphics, 0, 0, Width, Height, Border3DStyle.Sunken)

  End Sub

  ''' <summary>
  ''' Setting the background color and the highlighted range, if applicable
  ''' </summary>
  ''' <param name="pevent"></param>
  ''' <remarks></remarks>
  Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs)
    MyBase.OnPaintBackground(pevent)

    Me.BackColor = Me._GridSizeBackColor

    If Me._ShowHighlightedRange And Not Me._GraphSizeMode = eGraphSizeMode.Zoom And Not Me._GraphSizeMode = eGraphSizeMode.AutoAdjustMaximum Then
      ' Benannten Bereich hervorheben
      Dim myColor As Color
      myColor = Color.FromArgb(64, Color.White)
      Dim myBrush As New SolidBrush(myColor)
      Dim range As New Rectangle(0, CInt((Me.Maximum - Me._HighlightedRangeUpperBound) / (Me.Maximum - Me.Minimum) * Me.Height), Me.Width, CInt((Me._HighlightedRangeUpperBound - Me._HighlightedRangeLowerBound) / (Me.Maximum - Me.Minimum) * Me.Height))
      pevent.Graphics.FillRectangle(myBrush, range)
    End If

  End Sub

  ''' <summary>
  ''' Adjust internal value buffer on resize
  ''' </summary>
  ''' <param name="e"></param>
  ''' <remarks></remarks>
  Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
    MyBase.OnResize(e)

    ' If size decreases during runtime, downsize the internal value buffer accordingly
    If Me._Values.Count > (Me.Width) / intDivision Then
      SyncLock Me._objLockValues
        Me._Values.RemoveRange(0, Me._Values.Count - CInt((Me.Width) / intDivision))
      End SyncLock
    End If
  End Sub

  ''' <summary>
  ''' Flush all values and clear graph
  ''' </summary>
  ''' <param name="WaitForCompletion">If set to True, the call waits until the job is done.</param>
  ''' <remarks></remarks>
  Public Sub ResetValueBuffer(Optional ByVal WaitForCompletion As Boolean = False)

    SyncLock Me._objLockValues
      Me._ResetValueBuffer = True
    End SyncLock

    If WaitForCompletion Then
      Do Until Me._ResetValueBuffer = False
        Thread.Sleep(10)
      Loop
    End If

  End Sub

#End Region

End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions