3

i've been trying to reach the following result in a winforms vb.net application

Desired Result

where each arc or circle in this image is clickable , clickable arc is colored in pink.

i managed to write the following code

Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

    'Create pen objects 
    Dim p As New Pen(Color.Green, 30)
    Dim p1 As New Pen(Color.Yellow, 30)
    Dim p2 As New Pen(Color.Red, 30)
    Dim p3 As New Pen(Color.Blue, 30)

    'Create rectangle objects 
    Dim rt As New Rectangle(160, 150, 80, 100)
    Dim rt1 As New Rectangle(100, 150, 80, 100)
    Dim rt2 As New Rectangle(130, 120, 80, 100)
    Dim rt3 As New Rectangle(130, 180, 80, 100)

    'Draw arcs 
    e.Graphics.DrawArc(p, rt, 45, -90)
    e.Graphics.DrawArc(p1, rt1, -135, -90)
    e.Graphics.DrawArc(p2, rt2, -45, -90)
    e.Graphics.DrawArc(p3, rt3, 135, -90)

End Sub

that resulted the following output .

Output

what i didn't figure out is :

1- how to make a border for each of the arcs.

2- how to handle clicks on each of the arcs.

is there any better way than the way i'am trying to pull this off.

any help would be appreciated .

Ahmad.Tr
  • 756
  • 7
  • 22
  • They arent objects so they wont have click events. You could hold onto the rectangles to see if the mouse is inside one or the other when clicked, but there will be issues with them as arcs – Ňɏssa Pøngjǣrdenlarp Nov 05 '16 at 21:08
  • @Plutonix , what to do you recommend to memic the required result ? – Ahmad.Tr Nov 05 '16 at 21:09
  • @RezaAghaei , in want way you mean i can use rectangle.contains ? can you provide a short code sample just to make things clear ? – Ahmad.Tr Nov 05 '16 at 21:11
  • 2
    No you can not use `Rectangle.Contains` for Arcs. You should use [`GraphicsPath.IsVisible`](https://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.graphicspath.isvisible(v=vs.110).aspx). Take a look at this post for example: [How can I treat the circle as a control after drawing it?](https://stackoverflow.com/questions/38345828). Also take a look at this post to draw those arcs. [How to draw a circular progressbar pie using GraphicsPath in WinForm?](https://stackoverflow.com/questions/36096759) – Reza Aghaei Nov 05 '16 at 21:12
  • Much better approach. – Ňɏssa Pøngjǣrdenlarp Nov 05 '16 at 21:20
  • 1
    You can simply create a Class containing the required information for drawing and hit-testing your object, a `Rectangle` property, and 2 integer properties for `StartAngle` and `EndAngle`. Then you can simply pperform hit-testing and drawing. For drawing, accept a `Graphics` object as parameter of `Draw` method and draw the path on it. – Reza Aghaei Nov 05 '16 at 21:20
  • @RezaAghaei thank you for sharing the posts , will give a try – Ahmad.Tr Nov 05 '16 at 21:37
  • @RezaAghaei everything worked great , i have more question , the paint event in the form is triggered like a hundred times , ia there a workaround ? Can – Ahmad.Tr Nov 06 '16 at 16:18
  • 1
    The paint event will raise only when needed. When the form needs to repaint, for example when you restore it from minimize, or when you move another window over it. You don't need to do anything about it. Just set `DoubleBuffered = true` to have a flicker-free painting. – Reza Aghaei Nov 06 '16 at 19:54
  • @RezaAghaei would you post an answer so i can upvote it and choose it as an answer , everg thing went great . – Ahmad.Tr Nov 06 '16 at 20:59
  • 1
    In fact you don't need to upvote this comment, go to the question page and click up arrow near the question and near the answer :) [this one](https://stackoverflow.com/questions/38345828) and [this one](https://stackoverflow.com/questions/36096759). – Reza Aghaei Nov 06 '16 at 21:04
  • Done , but this question will remain unanswered ? correct me if iam wrong ? – Ahmad.Tr Nov 06 '16 at 21:07
  • 1
    I can close it as duplicate of the first post. Also you can post your own answer based on the linked posts. What do you prefer? – Reza Aghaei Nov 06 '16 at 21:08
  • Ill add an answer based on what you helped me – Ahmad.Tr Nov 06 '16 at 21:09
  • Good job, I'll see your answer :) – Reza Aghaei Nov 06 '16 at 21:11
  • @RezaAghaei thank you , – Ahmad.Tr Nov 08 '16 at 13:11

1 Answers1

2

Special Thanks for Reza Aghaei for the help , i used in my solution the following code

EDIT: IMPROVED ANSWER

Disposing the graphics path , and making code neater

Imports System.Drawing.Drawing2D
Public Class SurfaceSelection

Private Sub SurfaceSelection_Click(sender As Object, e As MouseEventArgs) Handles Me.Click
    Dim hitSurface As String = String.Empty
    If GetPath(EnumsClass.SurfacesEnum.L).IsVisible(e.Location) Then
        hitSurface = "L"
    ElseIf GetPath(EnumsClass.SurfacesEnum.M).IsVisible(e.Location) Then
        hitSurface = "M"
    ElseIf GetPath(EnumsClass.SurfacesEnum.F).IsVisible(e.Location) Then
        hitSurface = "F"
    ElseIf GetPath(EnumsClass.SurfacesEnum.D).IsVisible(e.Location) Then
        hitSurface = "D"
    Else
        hitSurface = "Missed"
    End If

    MsgBox(hitSurface)

End Sub

Private Sub SurfaceSelection_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

    Me.DrawPath(EnumsClass.SurfacesEnum.L, e)
    Me.DrawPath(EnumsClass.SurfacesEnum.M, e)
    Me.DrawPath(EnumsClass.SurfacesEnum.F, e)
    Me.DrawPath(EnumsClass.SurfacesEnum.D, e)

End Sub

Private Sub DrawPath(ByVal v_bytSurface As EnumsClass.SurfacesEnum, ByVal e As System.Windows.Forms.PaintEventArgs)
    Using p As GraphicsPath = GetPath(v_bytSurface)
        e.Graphics.FillPath(Brushes.Green, p)
        e.Graphics.DrawPath(Pens.Black, p)
    End Using
End Sub

Private Function GetPath(ByVal v_bytSurface As EnumsClass.SurfacesEnum) As GraphicsPath
    Dim path As New GraphicsPath
    Dim center = New Point(100, 100)
    Dim innerR = 70
    Dim thickness = 20
    Dim startAngle = getGraphicsPathAngle(v_bytSurface)
    Dim arcLength = 70
    Dim outerR = innerR + thickness
    Dim outerRect = New Rectangle(center.X - outerR, center.Y - outerR, 2 * outerR, 2 * outerR)
    Dim innerRect = New Rectangle(center.X - innerR, center.Y - innerR, 2 * innerR, 2 * innerR)
    path.AddArc(outerRect, startAngle, arcLength)
    path.AddArc(innerRect, startAngle + arcLength, -arcLength)
    path.CloseFigure()
    Return path
End Function

Private Function getGraphicsPathAngle(ByVal v_bytSurface As EnumsClass.SurfacesEnum) As Integer
    Select Case v_bytSurface
        Case EnumsClass.SurfacesEnum.F
            Return 235
        Case EnumsClass.SurfacesEnum.O
            Return 0
        Case EnumsClass.SurfacesEnum.L
            Return 55
        Case EnumsClass.SurfacesEnum.M
            Return 145
        Case EnumsClass.SurfacesEnum.D
            Return 325
        Case EnumsClass.SurfacesEnum.Unspecified
            Return -1

    End Select

End Function

End Class

Public Class EnumsClass
    Public Enum SurfacesEnum As Byte
        Unspecified = 0
        F = 1
        O = 2
        L = 3
        M = 4
        D = 5
    End Enum
End Class

i used Reza's answer in the following stackoverflow question :

How to draw a circular progressbar pie using GraphicsPath in WinForm?

How can I treat the circle as a control after drawing it? - Moving and selecting shapes

How to drag and move shapes in C#

Community
  • 1
  • 1
Ahmad.Tr
  • 756
  • 7
  • 22
  • 1
    By the way, the answer has a memory leak You are creating some `GraphicsPath` in paint event over and over again. – Reza Aghaei Nov 08 '16 at 13:13
  • 1
    You should create them just once. If you create them over and over again without disposing them, you will receive some exceptions and even will face with some problems in windows handle creation. You should fix the problem! – Reza Aghaei Nov 08 '16 at 13:22
  • I declared the graphics path at class level to use it on the click event , can i clear it instead of disposing ? – Ahmad.Tr Nov 08 '16 at 18:43
  • 1
    Yes you can clear it, but GDI resource should be disposed, clearing is not same as disposing. For example you can instead of storing them at form level, just put theme in a using block in your method. – Reza Aghaei Nov 08 '16 at 18:49
  • 1
    Consider `Circle` class in [this example](http://stackoverflow.com/a/38749134/3110834) or `HitTest` method in [this example](https://stackoverflow.com/questions/38345828/how-can-i-treat-the-circle-as-a-control-after-drawing-it) – Reza Aghaei Nov 08 '16 at 18:52
  • 1
    Good job! It's now OK :) – Reza Aghaei Nov 08 '16 at 20:32
  • 1
    By the way, you don't need to keep the original answer. Just keep the correct one and remove previous. It may mislead someone. – Reza Aghaei Nov 08 '16 at 20:33