locked
DataPoint Drawing, Position and Interactivity RRS feed

  • Question

  • Hi, Just out of curiosity, I wonder if it is possible to 'grab' a datapoint on mouse down and drag it around the screen, and on mouse up the point is dropped to the nearest xValue based on its position. I have read that it should be possible by tapping into the chart paint events?

    anyone know if it is possible? or is this too complex for mschart?

    Thursday, June 17, 2010 12:31 PM

Answers

  • Hello again, I modified your code to work like I presume you want it to. Basically I just modified the way the total is calculated.

    Imports System.Windows.Forms.DataVisualization.Charting
    
    Public Class ExampleForm3
    
    	Private _selectedPoint As DataPoint
    	Private _selectedPointIndex As Integer
    	Private _selectedSeries As Series
    
    	Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		Chart1.Series.Clear()
    
    		'Add a series to the chart
    		Dim series As New Series("SERIES")
    		series.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series)
    
    		'Add a series to the chart
    		Dim series2 As New Series("SERIES2")
    		series2.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series2)
    
    		'Add a series to the chart
    		Dim series3 As New Series("SERIES3")
    		series3.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series3)
    
    		'Add data to series
    		Dim xValues As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    		Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    		series.Points.DataBindXY(xValues, yValues)
    		series2.Points.DataBindXY(xValues, yValues)
    		series3.Points.DataBindXY(xValues, yValues)
    
    	End Sub
    
    	Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
    		Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    		If result.ChartElementType = ChartElementType.DataPoint Then
    			_selectedPointIndex = result.PointIndex
    			_selectedSeries = result.Series
    			_selectedPoint = result.Series.Points(_selectedPointIndex)
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
    		' Check if data point selected
    		If Not _selectedPoint Is Nothing Then
    
    			Dim total As Double = 0
    
    			'Calculate the total up to (not including) the selected point.
    			For Each s As Series In Chart1.Series
    				'Exit loop when reached the selected series
    				If s.Equals(_selectedSeries) Then Exit For
    
    				total = total + s.Points(_selectedPointIndex).YValues(0)
    			Next
    
    			'Mouse coordinates should not be outside of the chart 
    			Dim yValue As Double = Chart1.ChartAreas("ChartArea1").AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    
    			' Minus the total value found for correction.
    			yValue = yValue - total
    			yValue = Math.Min(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Maximum)
    			yValue = Math.Max(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Minimum)
    
    			' Update selected point Y value
    			_selectedPoint.YValues(0) = yValue
    
    			' Invalidate chart
    			Chart1.Invalidate()
    
    		Else
    
    			' Set different shape of cursor over the data points
    			Dim hitResult As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    			If hitResult.ChartElementType = ChartElementType.DataPoint Then
    				Chart1.Cursor = Cursors.Hand
    			Else
    				Chart1.Cursor = Cursors.Default
    			End If
    
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
    		_selectedPoint = Nothing
    
    	End Sub
    
    End Class
    
    • Marked as answer by enexooone Tuesday, June 29, 2010 7:47 AM
    Monday, June 28, 2010 2:57 PM
    Moderator
  • Instead of moving the data point with the x-value, you should change the y-value of the corresponding point in the same series.

    This example (as well as the previous ones) will probably only work if there are the same amount of points in each series and the x-values correspond to the points' x-indexes (1, 2, 3, 4, 5 ... )

    Also, note that if you disable the labels, you won't be able to modify the bottom most series' points if the y-value goes to zero, since it will be under the x-axis.

    Imports System.Windows.Forms.DataVisualization.Charting
    
    Public Class Form1
    
    	'The point that is dragged
    	Private _selectedPoint As DataPoint
    	Private _selectedPointIndex As Integer
    	Private _selectedSeries As Series
    	Private _selectedValue As Double
    
    	'The point the selected point is being dragged over
    	Private _currentPoint As DataPoint
    	Private _currentValue As Double
    
    	'The previous point the selected point was dragged over
    	'(To restore the value after moving over)
    	Private _previousPoint As DataPoint
    	Private _previousValue As Double
    
    	Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		Chart1.Series.Clear()
    
    		'Add a series to the chart
    		Dim series As New Series("SERIES")
    		series.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series)
    
    		'Add a series to the chart
    		Dim series2 As New Series("SERIES2")
    		series2.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series2)
    
    		'Add a series to the chart
    		Dim series3 As New Series("SERIES3")
    		series3.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series3)
    
    		'Add data to series
    		Dim xValues As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    		Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    		series.Points.DataBindXY(xValues, yValues)
    		series2.Points.DataBindXY(xValues, yValues)
    		series3.Points.DataBindXY(xValues, yValues)
    
    		For Each s In Chart1.Series
    			s.IsValueShownAsLabel = True
    		Next
    
    	End Sub
    
    	Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
    		Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    		'If the bottom data point value is 0, you can only drag from the label.
    		If result.ChartElementType = ChartElementType.DataPoint Or result.ChartElementType = ChartElementType.DataPointLabel Then
    			_selectedPointIndex = result.PointIndex
    			_selectedSeries = result.Series
    			_selectedPoint = result.Series.Points(_selectedPointIndex)
    			_selectedValue = _selectedPoint.YValues(0)
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
    		' Check if data point selected
    		If Not _selectedPoint Is Nothing Then
    
    			Dim area As ChartArea = Chart1.ChartAreas(0)
    
    			If e.Button = Windows.Forms.MouseButtons.Left Then
    
    				Dim total As Double = 0
    
    				'Calculate the total up to (not including) the selected point.
    				For Each s As Series In Chart1.Series
    					'Exit loop when reached the selected series
    					If s.Equals(_selectedSeries) Then Exit For
    
    					total = total + s.Points(_selectedPointIndex).YValues(0)
    				Next
    
    				'Mouse coordinates should not be outside of the chart 
    				Dim yValue As Double = area.AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    
    				' Minus the total value found for correction.
    				yValue = yValue - total
    				yValue = Math.Min(yValue, area.AxisY.Maximum)
    				yValue = Math.Max(yValue, area.AxisY.Minimum)
    
    				' Update selected point Y value
    				_selectedPoint.YValues(0) = Math.Round(yValue) 'Round the double value
    
    				'Invalidate chart
    				Chart1.Invalidate()
    
    			ElseIf e.Button = Windows.Forms.MouseButtons.Right Then
    
    				'Find the closest point index based on the x-value
    				Dim xValue As Double = Math.Round(area.AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0)))
    				xValue = Math.Max(Math.Min(xValue, _selectedSeries.Points.Count), 1)
    
    				_currentPoint = _selectedSeries.Points(CInt(xValue - 1))
    				_currentValue = _currentPoint.YValues(0)
    
    				If _previousPoint Is Nothing Then
    					_previousPoint = _currentPoint
    					_previousValue = _previousPoint.YValues(0)
    				End If
    
    				If Not _currentPoint.Equals(_previousPoint) Then
    					_previousPoint.YValues(0) = _previousValue - _selectedValue
    					_currentPoint.YValues(0) = _currentValue + _selectedValue
    					_previousPoint = _currentPoint
    					_previousValue = _previousPoint.YValues(0)
    				End If
    
    				'Invalidate chart
    				Chart1.Invalidate()
    
    			End If
    
    		Else
    
    			' Set different shape of cursor over the data points
    			Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    			If result.ChartElementType = ChartElementType.DataPoint Or result.ChartElementType = ChartElementType.DataPointLabel Then
    				Chart1.Cursor = Cursors.Hand
    			Else
    				Chart1.Cursor = Cursors.Default
    			End If
    
    		End If
    
    
    	End Sub
    
    	Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
    		_previousPoint = Nothing
    		_selectedPoint = Nothing
    
    	End Sub
    
    End Class
    • Marked as answer by enexooone Tuesday, June 29, 2010 1:45 PM
    Tuesday, June 29, 2010 1:00 PM
    Moderator
  • I mentioned this in one of my earlier posts. Try setting Series.IsXValueIndexed = True for each series in the chart. If you have missing data points, call the Chart.DataManipulator.InsertEmptyPoints method to align the series.

    'Add data to series
    Dim xValues As DateTime() = {#1/1/2011#, #1/2/2011#, #1/3/2011#, #1/4/2011#, #1/5/2011#, #1/6/2011#, #1/7/2011#, #1/8/2011#, #1/9/2011#, #1/10/2011#}
    Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    series.Points.DataBindXY(xValues, yValues)
    series2.Points.DataBindXY(xValues, yValues)
    series3.Points.DataBindXY(xValues, yValues)
    
    series.IsXValueIndexed = True
    series2.IsXValueIndexed = True
    series3.IsXValueIndexed = True
    
    'Change the interval to whatever your data has.
    Chart1.ChartAreas(0).AxisX.Interval = 1
    Chart1.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
    
    'Not necessary unless you have missing data points in some of the series.
    Chart1.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, "SERIES,SERIES2,SERIES3")
    
    • Marked as answer by enexooone Thursday, January 20, 2011 9:49 AM
    Wednesday, January 19, 2011 1:46 PM
    Moderator

All replies

  • In the Forms Charting it is fairly easy to do. There is a example of it in the Forms Samples Environment (at least in the .Net 3.5 samples). Content Tab > Chart Features > Interactive Charting > Selection > Changing values by dragging.

    Here is a piece of code to demonstrate. Mostly the same as the samples:

    Imports System.Windows.Forms.DataVisualization.Charting
    
    Public Class ExampleForm
    
    	Private _mouseDown As Boolean = False
    	Private _draggedPoint As DataPoint
    
    	Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		Chart1.Series.Clear()
    
    		'Set axisX limits and interval so that they wont change when moving data points
    		Chart1.ChartAreas(0).AxisX.Minimum = 0
    		Chart1.ChartAreas(0).AxisX.Maximum = 10
    		Chart1.ChartAreas(0).AxisX.Interval = 1
    
    		'Add a series to the chart
    		Dim series As New Series("SERIES")
    		series.ChartType = SeriesChartType.Point 'Will work with other types too.
    		series.MarkerSize = 10
    		Chart1.Series.Add(series)
    
    		'Add data to series
    		Dim xValues As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    		Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    		series.Points.DataBindXY(xValues, yValues)
    
    	End Sub
    
    	Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
    		Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    		If result.ChartElementType = ChartElementType.DataPoint Then
    			_draggedPoint = Chart1.Series(0).Points(result.PointIndex)
    			_mouseDown = True
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
    		If _mouseDown Then
    
    			Dim area As ChartArea = Chart1.ChartAreas(0)
    
    			'Mouse coordinates should not be outside of the chart 
    			Dim yValue As Double = area.AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    			yValue = Math.Min(yValue, area.AxisY.Maximum)
    			yValue = Math.Max(yValue, area.AxisY.Minimum)
    
    			Dim xValue As Double = area.AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0))
    			xValue = Math.Min(xValue, area.AxisX.Maximum)
    			xValue = Math.Max(xValue, area.AxisX.Minimum)
    
    			_draggedPoint.XValue = xValue
    			_draggedPoint.YValues(0) = yValue
    
    			Chart1.Invalidate()
    
    		Else
    			
    			'Set different shape of cursor over the data points
    			Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    			If result.ChartElementType = ChartElementType.DataPoint Then
    				Chart1.Cursor = Cursors.Hand
    			Else
    				Chart1.Cursor = Cursors.Default
    			End If
    
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
    		_mouseDown = False
    
    	End Sub
    
    End Class

    On the Web side I suspect this could be possible, but would be more complicated and involve javascript and ajax.

    Friday, June 18, 2010 10:18 AM
    Moderator
  • Hi Sipla, thanks for replying. try setting the series type to column and see what happens!, Ideally I was thinking when the mouse is released the point that is selected gets dropped to the nearest xvalue based on the pixel location. something like the Annotation Position changing event (in the samples) where it 'locks on' to the nearest point.

    thanks anyway

    regards,

    enex

    Friday, June 18, 2010 12:48 PM
  • It's very simple to get the data points to lock on to round values for example. Just set

    _draggedPoint.XValue = Math.Round(xValue)
    _draggedPoint.YValues(0) = Math.Round(yValue)

    in the Chart1.MouseMove event.

    You'll have to do some more coding if you want to get the points to lock on to any axis interval though.

    This will also "cure" the funky column width changes that you get in the original example, if you change the chart type to column. :)

    Friday, June 18, 2010 5:25 PM
    Moderator
  • Hi Sipla that does work really well but how do i work with multiple series? I plan on using the code for a stacked column chart with muliple series. I have looked over the mouse_down event and I can see it does refer to the series with an index of zero.

    Also you would probably know the answer to the other problem im having, basically I have adapted the code found in the samples to move the charts datapoints up and down to adjust the yValues. however because I am using a stacked column chart and I move a datapoint that is stacked on another, the point will jump up out of position. I think this is due to the method PixelPositionToValue. would you know how to correct this issue?

    im basically trying to loop through if the points are the selected stacks xvalue add them up and then I need to minus them to gain the correct position??  heres what i have tried:

      Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
        ' Check if data point selected
        If Not (selectedDataPoint Is Nothing) Then
          ' Mouse coordinates should not be outside of the chart 
          Dim coordinate As Integer = e.Y
          If coordinate < 0 Then
            coordinate = 0
          End If
          If coordinate > Chart1.Size.Height - 1 Then
            coordinate = Chart1.Size.Height - 1
          End If
    
     
          Dim total As Integer
    
          'Loop for each series
          For Each s In Chart1.Series
            'Go through the datapoints
            For Each p As DataPoint In s.Points
              'if the Datapoint found has the correct xValue
              If p.XValue = selectedDataPoint.XValue Then
                'Add to total
                total = total + p.YValues(0) 'Minus ANY previous series YValue here
              End If
            Next
          Next
    
          For Each s In Chart1.Series
            For Each p As DataPoint In s.Points
              If p.XValue = selectedDataPoint.XValue Then
    
              End If
            Next
          Next
    
          'This line is incorrect???
          total = total - selectedDataPoint.YValues(0)
    
    
          'Minus the total value found for correction.
          Dim yValue As Integer = Chart1.ChartAreas("ChartArea1").AxisY.PixelPositionToValue(coordinate) - total
    
          yValue = Math.Min(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Maximum)
          yValue = Math.Max(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Minimum)
    
          ' Update selected point Y value
          selectedDataPoint.YValues(0) = yValue
    
          ' Invalidate chart
          Chart1.Invalidate()
        Else
          ' Set different shape of cursor over the data points
          Dim hitResult As HitTestResult = Chart1.HitTest(e.X, e.Y)
          If hitResult.ChartElementType = ChartElementType.DataPoint Then
            Chart1.Cursor = Cursors.Hand
          Else
            Chart1.Cursor = Cursors.Default
          End If
        End If
      End Sub 'Chart1_MouseMove

     

    thanks in advance, the code you provided is very helpful

    Regards,

    enex

    Monday, June 21, 2010 1:26 PM
  • Hello again, I modified your code to work like I presume you want it to. Basically I just modified the way the total is calculated.

    Imports System.Windows.Forms.DataVisualization.Charting
    
    Public Class ExampleForm3
    
    	Private _selectedPoint As DataPoint
    	Private _selectedPointIndex As Integer
    	Private _selectedSeries As Series
    
    	Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		Chart1.Series.Clear()
    
    		'Add a series to the chart
    		Dim series As New Series("SERIES")
    		series.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series)
    
    		'Add a series to the chart
    		Dim series2 As New Series("SERIES2")
    		series2.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series2)
    
    		'Add a series to the chart
    		Dim series3 As New Series("SERIES3")
    		series3.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series3)
    
    		'Add data to series
    		Dim xValues As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    		Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    		series.Points.DataBindXY(xValues, yValues)
    		series2.Points.DataBindXY(xValues, yValues)
    		series3.Points.DataBindXY(xValues, yValues)
    
    	End Sub
    
    	Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
    		Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    		If result.ChartElementType = ChartElementType.DataPoint Then
    			_selectedPointIndex = result.PointIndex
    			_selectedSeries = result.Series
    			_selectedPoint = result.Series.Points(_selectedPointIndex)
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
    		' Check if data point selected
    		If Not _selectedPoint Is Nothing Then
    
    			Dim total As Double = 0
    
    			'Calculate the total up to (not including) the selected point.
    			For Each s As Series In Chart1.Series
    				'Exit loop when reached the selected series
    				If s.Equals(_selectedSeries) Then Exit For
    
    				total = total + s.Points(_selectedPointIndex).YValues(0)
    			Next
    
    			'Mouse coordinates should not be outside of the chart 
    			Dim yValue As Double = Chart1.ChartAreas("ChartArea1").AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    
    			' Minus the total value found for correction.
    			yValue = yValue - total
    			yValue = Math.Min(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Maximum)
    			yValue = Math.Max(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Minimum)
    
    			' Update selected point Y value
    			_selectedPoint.YValues(0) = yValue
    
    			' Invalidate chart
    			Chart1.Invalidate()
    
    		Else
    
    			' Set different shape of cursor over the data points
    			Dim hitResult As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    			If hitResult.ChartElementType = ChartElementType.DataPoint Then
    				Chart1.Cursor = Cursors.Hand
    			Else
    				Chart1.Cursor = Cursors.Default
    			End If
    
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
    		_selectedPoint = Nothing
    
    	End Sub
    
    End Class
    
    • Marked as answer by enexooone Tuesday, June 29, 2010 7:47 AM
    Monday, June 28, 2010 2:57 PM
    Moderator
  • Hi Sipla that code is highly helpful (ive been trying to solve that for weeks thanks alot!)

    I am able to drag the datapoints up and down how I want. You wrote some code that moves the datapoints and drops them where the user specifies, this does work well for the point style series. I would like to apply the idea to my stacked column chart. Heres what I'd like to do:

    instead of adjusting the Yvalue for the datapoint I would like to move it from side to side so I can drop it in another stack and the Yvalue will update accordingly depending on the stack selected all it will do is add the selectedpoint Yvalue to which ever series in the target stack matches the selected points series

    I can tear out a datapoint and move it side to side with the code you provided, but I would like to update the stack with the new yvalue heres the code I have so far again any help is highly helpful!

    edit: I was testing with the top series "series(2)" but I would like to apply this to any series selected.

    Imports System.Windows.Forms.DataVisualization.Charting
    
    
    Public Class Form1
    
     Private _selectedPoint As DataPoint
     Private _selectedPointIndex As Integer
     Private _selectedSeries As Series
    
     Private _mouseDown As Boolean = False
     Private _draggedPoint As DataPoint
    
     Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
      For Each s In Chart1.Series
       s.IsValueShownAsLabel = True
      Next
    
    
     End Sub
    
     Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
      Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
      If result.ChartElementType = ChartElementType.DataPoint Then
       _selectedPointIndex = result.PointIndex
       _selectedSeries = result.Series
       _selectedPoint = result.Series.Points(_selectedPointIndex)
    
       _draggedPoint = Chart1.Series(2).Points(result.PointIndex)
       _mouseDown = True
      End If
    
    
     End Sub
    
     Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
      If e.Button = Windows.Forms.MouseButtons.Left Then
       ' Check if data point selected
       If Not _selectedPoint Is Nothing Then
    
        Dim total As Integer = 0
    
        'Calculate the total up to (not including) the selected point.
        For Each s As Series In Chart1.Series
         'Exit loop when reached the selected series
         If s.Equals(_selectedSeries) Then Exit For
    
         total = total + s.Points(_selectedPointIndex).YValues(0)
        Next
    
        'Mouse coordinates should not be outside of the chart 
        Dim yValue As Integer = Chart1.ChartAreas("ChartArea1").AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    
        ' Minus the total value found for correction.
        yValue = yValue - total
        yValue = Math.Min(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Maximum)
        yValue = Math.Max(yValue, Chart1.ChartAreas("ChartArea1").AxisY.Minimum)
    
        ' Update selected point Y value
        _selectedPoint.YValues(0) = yValue
    
        ' Invalidate chart
        Chart1.Invalidate()
    
       Else
    
        ' Set different shape of cursor over the data points
        Dim hitResult As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
        If hitResult.ChartElementType = ChartElementType.DataPoint Then
         Chart1.Cursor = Cursors.Hand
        Else
         Chart1.Cursor = Cursors.Default
        End If
    
       End If
      End If
    
      If e.Button = Windows.Forms.MouseButtons.Right Then
    
       If _mouseDown Then
    
        Dim area As ChartArea = Chart1.ChartAreas(0)
    
        Dim xValue As Double = area.AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0))
        xValue = Math.Min(xValue, area.AxisX.Maximum)
        xValue = Math.Max(xValue, area.AxisX.Minimum)
    
        _draggedPoint.XValue = Math.Round(xValue)
    
        Chart1.Invalidate()
    
       Else
    
        'Set different shape of cursor over the data points
        Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
        If result.ChartElementType = ChartElementType.DataPoint Then
         Chart1.Cursor = Cursors.Hand
        Else
         Chart1.Cursor = Cursors.Default
        End If
    
       End If
      End If
    
     End Sub
    
     Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
      _selectedPoint = Nothing
      _mouseDown = False
     End Sub
    
    End Class
    
    Tuesday, June 29, 2010 10:42 AM
  • Instead of moving the data point with the x-value, you should change the y-value of the corresponding point in the same series.

    This example (as well as the previous ones) will probably only work if there are the same amount of points in each series and the x-values correspond to the points' x-indexes (1, 2, 3, 4, 5 ... )

    Also, note that if you disable the labels, you won't be able to modify the bottom most series' points if the y-value goes to zero, since it will be under the x-axis.

    Imports System.Windows.Forms.DataVisualization.Charting
    
    Public Class Form1
    
    	'The point that is dragged
    	Private _selectedPoint As DataPoint
    	Private _selectedPointIndex As Integer
    	Private _selectedSeries As Series
    	Private _selectedValue As Double
    
    	'The point the selected point is being dragged over
    	Private _currentPoint As DataPoint
    	Private _currentValue As Double
    
    	'The previous point the selected point was dragged over
    	'(To restore the value after moving over)
    	Private _previousPoint As DataPoint
    	Private _previousValue As Double
    
    	Private Sub ExampleForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		Chart1.Series.Clear()
    
    		'Add a series to the chart
    		Dim series As New Series("SERIES")
    		series.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series)
    
    		'Add a series to the chart
    		Dim series2 As New Series("SERIES2")
    		series2.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series2)
    
    		'Add a series to the chart
    		Dim series3 As New Series("SERIES3")
    		series3.ChartType = SeriesChartType.StackedColumn
    		Chart1.Series.Add(series3)
    
    		'Add data to series
    		Dim xValues As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    		Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    		series.Points.DataBindXY(xValues, yValues)
    		series2.Points.DataBindXY(xValues, yValues)
    		series3.Points.DataBindXY(xValues, yValues)
    
    		For Each s In Chart1.Series
    			s.IsValueShownAsLabel = True
    		Next
    
    	End Sub
    
    	Private Sub Chart1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseDown
    
    		Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    		'If the bottom data point value is 0, you can only drag from the label.
    		If result.ChartElementType = ChartElementType.DataPoint Or result.ChartElementType = ChartElementType.DataPointLabel Then
    			_selectedPointIndex = result.PointIndex
    			_selectedSeries = result.Series
    			_selectedPoint = result.Series.Points(_selectedPointIndex)
    			_selectedValue = _selectedPoint.YValues(0)
    		End If
    
    	End Sub
    
    	Private Sub Chart1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseMove
    
    		' Check if data point selected
    		If Not _selectedPoint Is Nothing Then
    
    			Dim area As ChartArea = Chart1.ChartAreas(0)
    
    			If e.Button = Windows.Forms.MouseButtons.Left Then
    
    				Dim total As Double = 0
    
    				'Calculate the total up to (not including) the selected point.
    				For Each s As Series In Chart1.Series
    					'Exit loop when reached the selected series
    					If s.Equals(_selectedSeries) Then Exit For
    
    					total = total + s.Points(_selectedPointIndex).YValues(0)
    				Next
    
    				'Mouse coordinates should not be outside of the chart 
    				Dim yValue As Double = area.AxisY.PixelPositionToValue(Math.Max(Math.Min(e.Y, Chart1.Size.Height - 1), 0))
    
    				' Minus the total value found for correction.
    				yValue = yValue - total
    				yValue = Math.Min(yValue, area.AxisY.Maximum)
    				yValue = Math.Max(yValue, area.AxisY.Minimum)
    
    				' Update selected point Y value
    				_selectedPoint.YValues(0) = Math.Round(yValue) 'Round the double value
    
    				'Invalidate chart
    				Chart1.Invalidate()
    
    			ElseIf e.Button = Windows.Forms.MouseButtons.Right Then
    
    				'Find the closest point index based on the x-value
    				Dim xValue As Double = Math.Round(area.AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0)))
    				xValue = Math.Max(Math.Min(xValue, _selectedSeries.Points.Count), 1)
    
    				_currentPoint = _selectedSeries.Points(CInt(xValue - 1))
    				_currentValue = _currentPoint.YValues(0)
    
    				If _previousPoint Is Nothing Then
    					_previousPoint = _currentPoint
    					_previousValue = _previousPoint.YValues(0)
    				End If
    
    				If Not _currentPoint.Equals(_previousPoint) Then
    					_previousPoint.YValues(0) = _previousValue - _selectedValue
    					_currentPoint.YValues(0) = _currentValue + _selectedValue
    					_previousPoint = _currentPoint
    					_previousValue = _previousPoint.YValues(0)
    				End If
    
    				'Invalidate chart
    				Chart1.Invalidate()
    
    			End If
    
    		Else
    
    			' Set different shape of cursor over the data points
    			Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
    
    			If result.ChartElementType = ChartElementType.DataPoint Or result.ChartElementType = ChartElementType.DataPointLabel Then
    				Chart1.Cursor = Cursors.Hand
    			Else
    				Chart1.Cursor = Cursors.Default
    			End If
    
    		End If
    
    
    	End Sub
    
    	Private Sub Chart1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Chart1.MouseUp
    
    		_previousPoint = Nothing
    		_selectedPoint = Nothing
    
    	End Sub
    
    End Class
    • Marked as answer by enexooone Tuesday, June 29, 2010 1:45 PM
    Tuesday, June 29, 2010 1:00 PM
    Moderator
  • Hi Sipla, thanks again for the code you have provided, it does exactly what I want it to do.

    I have been testing the Moving from stack to stack, you were saying something about a minus value?

    maybe the dragging up and down inteferes with moving from stack to stack?

    heres a screenshot, I moved the 7 from the stack with Xvalue of 10 to the xvalue of 8 it does move accross, but the value dosent get taken from x10

    I also get some sort of glitch where when i move the cursor over x9 to get to x8 it shows me a minus value in the label? any ideas?

     

    Wednesday, June 30, 2010 7:47 AM
  • It's because you have a 3D chart. The way the code selects the selected data point is different from the way it determines on top of which point the cursor is. (One is from HitTestResult.PointIndex the other is calculated from the position of the cursor.)

    If change the code in the MouseDown event from

    _selectedPointIndex = result.PointIndex
    _selectedSeries = result.Series

    to

    _selectedSeries = result.Series
    'Find the closest point index based on the x-value
    Dim xValue As Double = Math.Round(Chart1.ChartAreas(0).AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0)))
    xValue = Math.Max(Math.Min(xValue, _selectedSeries.Points.Count), 1)
    _selectedPointIndex = Cint(xValue) - 1
    

    it should work in 3D. Though the data point dragging won't be as accurate as it grabs a data point even if it is behind a column. (in 2D it will work fine like this too.)

    Wednesday, June 30, 2010 12:22 PM
    Moderator
  • Hi sipla I will look at applying that to the code, but I will stick with a 2D Chart for now as it works perfectly!

    many thanks for all your help

    regards

    enex

    Wednesday, June 30, 2010 1:41 PM
  • Another quick question,

    on a 2D Chart, When I drag the datapoints up and down and I grip the datapoint that is at the very top of the stack, Gripping right at the top (where the line is) will allow the user to adjust series data, but I want it so you can only adjust the visible series data any ideas of how i prevent this?

    heres a screenshot of what i mean:

     

    Friday, July 2, 2010 11:43 AM
  • Well you could just do something like this in the MouseDown event:

    If _selectedValue = 0 Then
    	_selectedPoint = Nothing
    End If

    Tough this way, if you drag a point to another column or set a value to zero, you have one (non-zero) value less in the chart and there's no way to get it back.

     

    Friday, July 2, 2010 3:00 PM
    Moderator
  • hello sipla & enexooone

    i want to try the code made by sipla

    but when i pasted the code in the code behind (blabla.aspx.vb), i've got 1 warning said:

    "namespace or type specified in the Imports 'System.Windows.Forms.DataVisualization.Charting' doesn't contain any public member or cannot be found. make sure the namespace pr the type is defined and contains at least one public member. make sure the imported element name doesn't use any aliases"

    it's just the only warning,,

    i'm creating a web-based application with charts right now..

    please help me.

    Thanks before =].

    Wednesday, August 25, 2010 6:49 AM
  • Hello,

    This kind of interactivity only works on a Windows Forms Application, not on the web version of the chart control. There is a Silverlight chart control from Microsoft in the Silverlight Toolkit, which I assume could be used to make a web chart more interactive but I haven't tried it.

    To get these examples to work, create a Windows Forms Application (make sure it has a reference to System.Windows.Forms.DataVisualization in the project properties), add a chart (with the name Chart1) to a form and paste the code to the codebehind.

    Wednesday, August 25, 2010 10:24 AM
    Moderator
  • Hello. O, Ya. I thought so too yesterday. And about the Silverlight Toolkit, I'll try it. Thanks.
    Thursday, August 26, 2010 2:36 AM
  • Hi Sipla,

    Another quick question

    I have been binding to my stacked chart with an xValue type of string, but recently changed the xValue type to be Date.

    All works well when moving the points up and down, but it wont work from side to side!, I think its a problem with the mouse move event

    this line:

     

    Dim xValue As Double = Math.Round(area.AxisX.PixelPositionToValue(Math.Max(Math.Min(e.X, Chart1.Size.Width - 1), 0)))

    for some reason it returns the date (OADate) instead of the pixel position?

    you can see what I mean if you change the xValue type to be date

    thanks in advance

    enex

    Tuesday, January 18, 2011 1:53 PM
  • I mentioned this in one of my earlier posts. Try setting Series.IsXValueIndexed = True for each series in the chart. If you have missing data points, call the Chart.DataManipulator.InsertEmptyPoints method to align the series.

    'Add data to series
    Dim xValues As DateTime() = {#1/1/2011#, #1/2/2011#, #1/3/2011#, #1/4/2011#, #1/5/2011#, #1/6/2011#, #1/7/2011#, #1/8/2011#, #1/9/2011#, #1/10/2011#}
    Dim yValues As Integer() = {2, 4, 6, 1, 5, 4, 8, 9, 4, 3}
    
    series.Points.DataBindXY(xValues, yValues)
    series2.Points.DataBindXY(xValues, yValues)
    series3.Points.DataBindXY(xValues, yValues)
    
    series.IsXValueIndexed = True
    series2.IsXValueIndexed = True
    series3.IsXValueIndexed = True
    
    'Change the interval to whatever your data has.
    Chart1.ChartAreas(0).AxisX.Interval = 1
    Chart1.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
    
    'Not necessary unless you have missing data points in some of the series.
    Chart1.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, "SERIES,SERIES2,SERIES3")
    
    • Marked as answer by enexooone Thursday, January 20, 2011 9:49 AM
    Wednesday, January 19, 2011 1:46 PM
    Moderator
  • IsXValueIndexed = True works perfectly thanks again!

    Thursday, January 20, 2011 9:47 AM