jonathana jonathana - 3 months ago 16
Vb.net Question

getting control object with Graphics from class instance

i dont have much experience with Graphics class in winforms.
i am only in the sketching stage of it (also the code i have added).
my problem is that i am trying to create a panel:

clockPanel
with some graphics on it, no exception is thrown but the panel (as i can see in the UI) have no graphics on it. tryed to look for examples but i cant find mistakes or something i missed in my code. probably its an easy one for those of you that experienced with graphics.
thank you for your time an consideration.

VB CODE:
adding 'clockpanel' panel to other pannel ('secondaryPannel') via instance to GoalsClock class

Public Class ManagersTab
...
Public Sub BuiledDashBoard()
...
Dim p As GoalsClock = New GoalsClock(100, 100, 0.8)
p.Create()
p.clockPanel.Location = New Point(200, 100)
secondaryPannel.Controls.Add(p.clockPanel)
...
End Sub
...
End Class


Create() method is the most relevant part:

Class GoalsClock

Private Gclock As Graphics
Private clockWidth As Int16
Private clockHeight As Int16
Private xPos As Int16
Private yPos As Int16

Public clockPanel As Panel
Private panelColor As Color

Private PercentTextColor As Color
' rectangles to store squares
Protected OuterRect As Rectangle
Protected InnerRect As Rectangle
Protected InnerStringBrush As Brush
Protected InnerStringColor As Color
Protected InnerStringFontSize As Byte

' inner square
Private InnerSquarePen As Pen
Private InnerSquarePen_Color As Color
Private InnerSquarePen_Width As Byte
' outer square
Private OuterSquarePen As Pen
Private OuterSquarePen_Color As Color
Private OuterSquarePen_Width As Byte

Private _PercentOfGoals As Single ' to calculate the goals deg arc
Public Property PercentOfGoals() As Single
Get
Return _PercentOfGoals * 100
End Get
Private Set(ByVal value As Single)
If value <= 1.0F Then
_PercentOfGoals = value
Else
value = 0
End If
End Set
End Property

Sub New(ByVal clockWidth As Int16, ByVal clockHeight As Int16, ByVal GoalsPercent As Single)
Me.clockWidth = clockWidth
Me.clockHeight = clockHeight
PercentOfGoals = GoalsPercent


' values for test
xPos = 0
yPos = 0
InnerStringFontSize = 12
OuterSquarePen = New Pen(Color.Gray)
InnerSquarePen = New Pen(Color.Cyan)
OuterSquarePen_Width = 23
InnerSquarePen_Width = 15
End Sub

''' <summary>
'''
''' create graphics of the goals clock on clockPanel
''' </summary>
''' <remarks></remarks>
Public Sub Create()
' panel
clockPanel = New Panel()
clockPanel.Size = New Size(clockWidth, clockHeight)
clockPanel.BackColor = Color.Beige
Gclock = clockPanel.CreateGraphics()
' create outer rectangle
OuterRect = New Rectangle(xPos, yPos, clockWidth, clockHeight)
' create inner rectangle
Dim w, h, x, y As Integer
getInnerRectSizeAndLocation(w, h, x, y)
InnerRect = New Rectangle(x, y, w, h)
' draw goals string inside inner rect
InnerStringBrush = Brushes.Cyan
Gclock.DrawString(getPercentString(), New Font("ARIAL", InnerStringFontSize, FontStyle.Bold), InnerStringBrush, InnerRect)

' create outer square
OuterSquarePen = New Pen(OuterSquarePen_Color, OuterSquarePen_Width)
Gclock.DrawArc(OuterSquarePen, OuterRect, 1.0F, 360.0F)

' create inner square
InnerSquarePen = New Pen(InnerSquarePen_Color, InnerSquarePen_Width)
Dim sweepAngle As Short = getSweepAngleFromGoalsPercent()
Gclock.DrawArc(InnerSquarePen, OuterRect, -90.0F, sweepAngle)

End Sub

Private Sub getInnerRectSizeAndLocation(ByRef w As Integer, ByRef h As Integer, ByRef x As Integer, ByRef y As Integer)
' values for test
w = 40
h = 40
x = 64
y = 65
End Sub

Private Function getPercentString() As String
Return PercentOfGoals.ToString() & "%"
End Function

Private Function getSweepAngleFromGoalsPercent() As Single
' value for test
Return 0.0F
End Function


End Class

Answer

You must subscribe to the panel's Paint event and perform all drawing there. The AddHandler statement is used to dynamically subscribe to events.

The Graphics class will not store any information about what you draw, so when your panel is redrawn everything you previously drew will be gone unless you draw it again. This is where the Paint event comes into play: it will be raised every time your panel is redrawn, passing an instance of a Graphics class in its PaintEventArgs so you can draw your stuff onto the panel again.

Public Sub Create()
    ' panel
    clockPanel = New Panel()
    clockPanel.Size = New Size(clockWidth, clockHeight)
    clockPanel.BackColor = Color.Beige

    ' subscribe to the panel's paint event
    AddHandler clockPanel.Paint, AddressOf clockPanel_Paint
End Sub

Private Sub clockPanel_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
    Dim Gclock As Graphics = e.Graphics 'Local variable only, as the Graphics object might change.
    ' create outer rectangle
    OuterRect = New Rectangle(xPos, yPos, clockWidth, clockHeight)
    ' create inner rectangle
    Dim w, h, x, y As Integer
    getInnerRectSizeAndLocation(w, h, x, y)
    InnerRect = New Rectangle(x, y, w, h)
    ' draw goals string inside inner rect
    InnerStringBrush = Brushes.Cyan
    Gclock.DrawString(getPercentString(), New Font("ARIAL", InnerStringFontSize, FontStyle.Bold), InnerStringBrush, InnerRect)

    ' create outer square
    OuterSquarePen = New Pen(OuterSquarePen_Color, OuterSquarePen_Width)
    Gclock.DrawArc(OuterSquarePen, OuterRect, 1.0F, 360.0F)

    ' create inner square
    InnerSquarePen = New Pen(InnerSquarePen_Color, InnerSquarePen_Width)
    Dim sweepAngle As Short = getSweepAngleFromGoalsPercent()
    Gclock.DrawArc(InnerSquarePen, OuterRect, -90.0F, sweepAngle)
End Sub

As you also might've seen I am constantly redeclaring a new Gclock variable in the Paint event. This is because the Graphics instance used to draw your panel with might change, so you shouldn't store it any longer than the time the Paint event lasts (so I highly recommend you remove the declaration in the top of your class).