Create a user editable line graph in vb.net

Hi

I hope this is not too complicated for a single question.

I want to create a graph with specific points that the user can drag up or down, with the mouse, within a rectangle. Just to set the context, the graph will determine the number of units available over 12 periods. Each moveable point is a circle shape. I have worked out how to get the shape to move up and down but I need help with:

How do I ensure that it only moves within the top and bottom boundaries of the rectangle?

Is it possible to have the line graph adjust in real time so that each line begins and ends in the centre of a circle?

I went over to the dark side for a while and managed to do this with 'Livecode', but I'm struggling with how to do it in VB.net.

The attached picture shows what the Livecode version of the graph looks like. Basically, the user can drag the circles and the lines of the graph move simultaneously.

Regards

Terry
Interactive-Graph.jpg
TerrygordonAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Robert SchuttSoftware EngineerCommented:
Try this:
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        AddHandler Chart1.MouseDown, AddressOf Chart_MouseDown
        AddHandler Chart1.MouseMove, AddressOf Chart_MouseMove
    End Sub

    Sub SetY(ByVal ch As Chart, ByVal e_X As Integer, ByVal e_Y As Integer)
        Try
            If e_X >= 0 And e_X < Chart1.Width And e_Y > 0 And e_Y < Chart1.Height Then
                Dim currentx As Integer = ch.ChartAreas(0).AxisX.PixelPositionToValue(e_X)
                If currentx < ch.ChartAreas(0).AxisX.Minimum Or currentx > ch.ChartAreas(0).AxisX.Maximum Then
                    currentx = -1
                End If
                Dim currenty As Integer = ch.ChartAreas(0).AxisY.PixelPositionToValue(e_Y)
                If currenty < ch.ChartAreas(0).AxisY.Minimum Or currenty > ch.ChartAreas(0).AxisY.Maximum Then
                    currenty = -1
                End If
                If currentx > -1 And currenty > -1 Then
                    ch.Series(0).Points.FindByValue(currentx, "X").SetValueY(currenty)
                    ch.Invalidate()
                End If
            End If
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

    Private Sub Chart_MouseDown(sender As Object, e As MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Left Then
            SetY(CType(sender, Chart), e.X, e.Y)
        End If
    End Sub

    Private Sub Chart_MouseMove(sender As Object, e As MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Left Then
            SetY(CType(sender, Chart), e.X, e.Y)
        End If
    End Sub

Open in new window

0
TerrygordonAuthor Commented:
Hi Robert

I couldn't get this to work. Presumably I need to create chart1?

Regards

Terry
0
Robert SchuttSoftware EngineerCommented:
Yes, I'm not sure what your form looks like but I tested it by just putting a chart on a form, that gets named Chart1 by default.
0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

TerrygordonAuthor Commented:
Hi Robert

I'm getting an error "Type chart is not defined".

Regards

Terry
0
Robert SchuttSoftware EngineerCommented:
Right, you probably need to add (at the very top of your file):
Imports System.Windows.Forms.DataVisualization.Charting

Open in new window

0
Robert SchuttSoftware EngineerCommented:
Here's some more code I have to make it look like your example a bit more.
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Dim dt As New DataTable
        dt.Columns.AddRange({New DataColumn("X", GetType(Integer)), New DataColumn("Y", GetType(Integer))})
        Dim r As New Random
        For i As Integer = 1 To 12
            dt.Rows.Add({i, r.Next(1000, 49000)})
        Next
        Chart1.DataSource = dt
        Chart1.BackColor = Color.FromArgb(58, 80, 184)
        Chart1.BackSecondaryColor = Color.FromArgb(111, 128, 206)
        Chart1.BackGradientStyle = GradientStyle.TopBottom
        Chart1.ChartAreas(0).AxisX.Minimum = 1
        Chart1.ChartAreas(0).AxisX.Maximum = 12
        Chart1.ChartAreas(0).AxisX.MajorGrid.LineColor = Color.Silver
        Chart1.ChartAreas(0).AxisX.LabelStyle.ForeColor = Color.White
        Chart1.ChartAreas(0).AxisY.Maximum = 50000
        Chart1.ChartAreas(0).AxisY.MajorGrid.LineColor = Color.Silver
        Chart1.ChartAreas(0).AxisY.MajorGrid.Interval = 5000
        Chart1.ChartAreas(0).AxisY.LabelStyle.ForeColor = Color.White
        Chart1.ChartAreas(0).BackGradientStyle = GradientStyle.TopBottom
        Chart1.ChartAreas(0).BackColor = Color.FromArgb(150, 165, 221)
        Chart1.ChartAreas(0).BackSecondaryColor = Color.FromArgb(69, 93, 194)
        Chart1.Series(0).XValueMember = "X"
        Chart1.Series(0).YValueMembers = "Y"
        Chart1.Series(0).ChartType = SeriesChartType.Line
        Chart1.Series(0).Color = Color.White
        Chart1.Series(0).BorderWidth = 2
        Chart1.Series(0).MarkerImage = "marker.png"
        Chart1.Series(0).MarkerImageTransparentColor = Color.FromArgb(1, 0, 1)
        Chart1.Legends(0).Enabled = False
        AddHandler Chart1.MouseDown, AddressOf Chart_MouseDown
        AddHandler Chart1.MouseMove, AddressOf Chart_MouseMove
    End Sub

Open in new window

0
TerrygordonAuthor Commented:
Hi Robert

Still getting lots of errors with this (unhandled exception of type system.ArgumentOutOfRangeException).  Additional information says - Must be non-negative and less than the size of the collection. It is highlighting the line:

Chart1.Legends(0).Enabled = False

Then, when I break or continue, all instances of Chart1 are highlighted.

I have also tried again with your original code and am getting an error in line 21:

 ch.Series(0).Points.FindByValue(currentx, "X").SetValueY(currenty)

The error is: Object reference not set to an instance of an object.


Regards

Terry
0
Robert SchuttSoftware EngineerCommented:
I'm not sure from those messages what's missing. Can you try this project?
EE-Q-28493650.ZIP
0
TerrygordonAuthor Commented:
Hi Robert

Works and looks great on your version. I will have a play around with it tomorrow and award the points once I've figured it out. :-)

Regards

Terry
0
Robert SchuttSoftware EngineerCommented:
Ok, Terry that's good news. Any questions, don't hesitate.
0
TerrygordonAuthor Commented:
Hi Robert

I have recreated form1 and Chart1in my own project and it is now working great.

I was getting an error "Cannot load image from this location" when it tried to load marker.png, but this seems to have been resolved by copying the marker.png file into the bin/debug folder.

In the next stage I am adding multiple charts to the form and have started by just adding a new datatable, random numbers, properties, etc. for Chart2, in the form1 load sub.

This displays perfectly and allows me to drag the points on both charts independently, even though I haven't amended any of the other subs.

Do I need to make any changes to the other subs? For example, the SetY sub refers to Chart1, but none of the other subs (MouseDown, MouseMove, Customize) do.

I am more than happy with your solution so far so, if you prefer, I can post this as a separate question.

Regards

Terry
0
Robert SchuttSoftware EngineerCommented:
resolved by copying the marker.png file into the bin/debug folder
Normally this should be done as a build action, the file marker.png should be added to the project, then in the solution explorer, select it and right click to go to properties, then choose the appropriate copy action
captureThe reference to Chart1 in SetY was a later addition and I did that too fast... it should refer to "ch" (the first argument to the sub), and then everything should be independent of the actual chart the mousexxx functions are handling. So the correct code would be:
    Sub SetY(ByVal ch As Chart, ByVal e_X As Integer, ByVal e_Y As Integer)
        Try
            If e_X >= 0 And e_X < ch.Width And e_Y > 0 And e_Y < ch.Height Then
...

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
TerrygordonAuthor Commented:
Thanks Robert. Not only a brilliant solution, but saved me a load of time by not having to redesign the charts.

Regards

Terry
0
TerrygordonAuthor Commented:
An excellent solution with some design thrown in and very fast response times.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Programming Languages-Other

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.