Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

Scatter plot: gradient color

Solved!
Go to solution

Hi,

I can't seem to find an example where I can create a custom plot class. I need to be able to color the line of the plot as a function of it's Y value, and make it look like a smooth gradient. Ideally I'd like to change the point size of the line as well. This would allow me to "plot" 3 variables using a 2D graph. I've started (VB.Net):

 

Imports NationalInstruments.UI

Public Class clCustomPlot

Inherits ScatterPlot

Public Sub New()

MyBase.New()

End Sub

 Protected Overrides Sub OnBeforeDraw(ByVal sender As Object, ByVal e As NationalInstruments.UI.BeforeDrawXYPlotEventArgs)

 

End Sub

End Class

 

But VS tells me that:

Error MeasStudioGraphLineColour sub 'OnBeforeDraw' cannot be declared 'Overrides' because it does not override a sub in a base class.
Thanks for your help,

Nicolas

0 Kudos
Message 1 of 7
(5,688 Views)

Hello Nicolas,

 

You will actually want to create your own custom LineStyle class.  There is an excellent example of how to do that in the Measurement Studio help topic entitled "Creating a Custom Line Style for the Measurement Studio Web Forms Graph .NET Controls" Also, you may want to check out a forum I recently answered using a similar method - found here.  Let me know if you have any questions.

 

NickB

National Instruments 

Message 2 of 7
(5,687 Views)
Ok, had a first go at it. Am getting an error which I've seen before when trying to do this but couldn't solve. Could be "VB" related? I attached my code and here is the error:
 
 
 System.ArgumentException was unhandled
  Message="Parameter is not valid."
  Source="System.Drawing"
  StackTrace:
       at System.Drawing.Pen.get_Color()
       at NationalInstruments.UI.Internal.a5.a(Graphics A_0, Rectangle A_1, di A_2)
       at NationalInstruments.UI.Internal.a7.a(ComponentDrawArgsInternal A_0)
       at NationalInstruments.Restricted.ControlElement.a(ComponentDrawArgsInternal A_0, Rectangle A_1, Boolean A_2)
       at NationalInstruments.Restricted.ControlElement.DrawChildren(ComponentDrawArgsInternal args, Rectangle clipRectangle)
       at NationalInstruments.Restricted.ControlElement.a(ComponentDrawArgsInternal A_0, Rectangle A_1, Boolean A_2)
       at NationalInstruments.Restricted.ControlElement.DrawChildren(ComponentDrawArgsInternal args, Rectangle clipRectangle)
       at NationalInstruments.Restricted.ControlElement.a(ComponentDrawArgsInternal A_0, Rectangle A_1, Boolean A_2)
       at NationalInstruments.Restricted.ControlElement.DrawChildren(ComponentDrawArgsInternal args, Rectangle clipRectangle)
       at NationalInstruments.Restricted.ControlElement.a(ComponentDrawArgsInternal A_0, Rectangle A_1, Boolean A_2)
       at NationalInstruments.Restricted.ControlElement.a(ComponentDrawArgsInternal A_0, Rectangle A_1)
       at NationalInstruments.Restricted.ControlElement.Paint(PaintEventArgs e)
       at NationalInstruments.UI.WindowsForms.ControlBase.OnPaint(PaintEventArgs e)
       at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
       at System.Windows.Forms.Control.WmPaint(Message& m)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at MeasStudioGraphLineColour.frmMain.Main() in D:\Source\MiscVB.Net\MeasStudioGraphLineColour\frmMain.vb:line 1
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

 Thanks for your help Nick! 
0 Kudos
Message 3 of 7
(5,684 Views)

The forum wouldn't let me attach a zip with my code so here it is (gr is a ScatterGraph on the form which initially contains no plot): 

 
   Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
    Dim pl As New NationalInstruments.UI.ScatterPlot
    Me.gr.Plots.AddRange(New NationalInstruments.UI.ScatterPlot() {pl})
    pl.XAxis = Me.XAxis1
    pl.YAxis = Me.YAxis1

    Dim X(1000) As Double
    Dim Y(1000) As Double

    For i As Integer = 0 To 1000
      X(i) = CDbl(i)
      Y(i) = 5 + (5 * Math.Cos(i * Math.PI / 180)) * ((i + 1) / 1000)
    Next

    pl.LineStyle = New clGradientLineStyle
    pl.PlotXY(X, Y)
  End Sub
 
 Public Class clGradientLineStyle
  Inherits LineStyle

  Private _startColor As Color
  Private _endColor As Color

  Public Sub New()
    MyClass.New(Color.Red, Color.Blue)
  End Sub

  Public Sub New(ByVal startColor As Color, ByVal endColor As Color)
    _startColor = startColor
    _endColor = endColor
  End Sub

  Public Property StartColor() As Color
    Get
      Return _startColor
    End Get
    Set(ByVal value As Color)
      _startColor = value
    End Set
  End Property


  Public Property EndColor() As Color
    Get
      Return _endColor
    End Get
    Set(ByVal value As Color)
      _endColor = value
    End Set
  End Property

  Public Overrides ReadOnly Property IsContextDependent() As Boolean
    Get
      ' Return true to indicate that CreatePen
      ' should be called for any change in context
      Return True
    End Get
  End Property

  Public Overrides Function CreatePen(ByVal context As Object, ByVal args As LineStyleDrawArgs) As Pen
    Dim bounds As Rectangle = args.ContextBounds

    ' Add 1 to width and height of context bounds to include
    ' the right and bottom edge of the rectangle in the linear
    ' gradient brush
    bounds.Width += 1
    bounds.Height += 1

    Dim brush As Brush
    Try
      brush = New LinearGradientBrush(bounds, StartColor, EndColor, LinearGradientMode.Vertical)
      Return New Pen(brush, args.Width)
    Finally
      If Not (brush Is Nothing) Then
        brush.Dispose()
      End If
    End Try
  End Function
End Class

 

0 Kudos
Message 4 of 7
(5,682 Views)

 I've now tried implementing the example

 

 Creating a Custom Line Style for the Measurement Studio Windows Forms Graph .NET Controls

and get the same error. I'm using VS2008. Can you replicate this on your side?

0 Kudos
Message 5 of 7
(5,677 Views)

I've now tride implementing it in C#. I get the exact same error so I must be doing something wrong! Here's the code:

 

public partial class Form1 : Form

  {

    public Form1()

    {

      InitializeComponent();

    }


    private void Form1_Load(object sender, EventArgs e)

    {

      double[] x = new double[1000];

      double[] y = new double[1000];

      for (int i = 0; i < 1000; i++)

    {

      x[i]=i;

      y[i] = 5 + (5 * Math.Cos(i * Math.PI / 180)) * ((i + 1) / 1000);

  }

      GradientLineStyle GL;

      GL=new GradientLineStyle();

      scatterPlot1.LineStyle = GL;


      scatterPlot1.PlotXY(x, y);


    }

  }



  public class GradientLineStyle : NationalInstruments.UI.LineStyle

  {

    private Color _startColor;

    private Color _endColor;


    public GradientLineStyle()

      : this(Color.Red, Color.Blue)

    {

    }


    public GradientLineStyle(Color startColor, Color endColor)

    {

      _startColor = startColor;

      _endColor = endColor;

    }


    public Color StartColor

    {

      get

      {

        return _startColor;

      }

      set

      {

        _startColor = value;

      }

    }


    public Color EndColor

    {

      get

      {

        return _endColor;

      }

      set

      {

        _endColor = value;

      }

    }


    public override bool IsContextDependent

    {

      get

      {

        // Return true to indicate that CreatePen

        // should be called for any change in context

        return true;

      }

    }


    public override Pen CreatePen(object context, NationalInstruments.UI.LineStyleDrawArgs args)

    {

      Rectangle bounds = args.ContextBounds;


      // Add 1 to width and height of context bounds to include

      // the right and bottom edge of the rectangle in the linear

      // gradient brush

      bounds.Width += 1;

      bounds.Height += 1;


      using (Brush brush = new LinearGradientBrush(bounds, StartColor, EndColor, LinearGradientMode.Vertical))

      {

        return new Pen(brush, args.Width);

      }

    }

  } 

0 Kudos
Message 6 of 7
(5,674 Views)
Solution
Accepted by topic author FFCyrille

I got it working! For all those interested, here is a VB.Net custom ScatterPlot which will show you how to control/color every step of the line.

 

Imports NationalInstruments.UI

Imports System.Drawing

Imports System.Drawing.Drawing2D

Public Class clCustomScatterPlot

  Inherits ScatterPlot

  Private dblMin As Double

  Private dblMax As Double

  Private colMinColour As System.Drawing.Color

  Private colMaxColour As System.Drawing.Color

  Public Sub New(ByVal MinValue As Double, ByVal MaxValue As Double, _

                 ByVal MinValueColour As System.Drawing.Color, ByVal MaxValueColour As System.Drawing.Color)

    MyBase.New()

    ' no checking is done for the fact that MinValue<MaxValue

    dblMin = MinValue

    dblMax = MaxValue

    colMinColour = MinValueColour

    colMaxColour = MaxValueColour

  End Sub


  Protected Overrides Sub OnBeforeDraw(ByVal e As BeforeDrawXYPlotEventArgs)

    ' Clip data, iterate through clipped data, map, and draw.

    Dim plot As XYPlot = e.Plot

    Dim clippedXData() As Double = Nothing

    Dim clippedYData() As Double = Nothing

    plot.ClipDataPoints(clippedXData, clippedYData)

    For i As Integer = 0 To clippedXData.Length - 2

      Dim x1 As Double = clippedXData(i)

      Dim x2 As Double = clippedXData(i + 1)

      Dim y1 As Double = clippedYData(i)

      Dim y2 As Double = clippedYData(i + 1)

      Dim point1 As PointF = plot.MapDataPoint(e.Bounds, x1, y1)

      Dim point2 As PointF = plot.MapDataPoint(e.Bounds, x2, y2)


      Dim pen As Pen = New Pen(ValueToColour(y2))


      Dim g As Graphics = e.Graphics

      g.DrawLines(pen, New PointF() {point1, point2})

    Next

    e.Cancel = True

  End Sub

  Private Function ValueToColour(ByVal value As Double) As Color

    If value <= dblMin Then Return colMinColour

    If value >= dblMax Then Return colMaxColour


    Dim pctg As Double = (value - dblMin) / (dblMax - dblMin)

    Dim a, r, g, b As Integer


    a = CInt(CInt(colMinColour.A) + pctg * (CInt(colMaxColour.A) - CInt(colMinColour.A)))

    r = CInt(CInt(colMinColour.R) + pctg * (CInt(colMaxColour.R) - CInt(colMinColour.R)))

    g = CInt(CInt(colMinColour.G) + pctg * (CInt(colMaxColour.G) - CInt(colMinColour.G)))

    b = CInt(CInt(colMinColour.B) + pctg * (CInt(colMaxColour.B) - CInt(colMinColour.B)))


    r = Math.Max(0, r) : r = Math.Min(255, r)

    g = Math.Max(0, g) : g = Math.Min(255, g)

    b = Math.Max(0, b) : b = Math.Min(255, b)

    Return System.Drawing.Color.FromArgb(a, r, g, b)

    Dim col As Color = System.Drawing.Color.FromArgb(a, r, g, b)

    Return col

  End Function

End Class

 

Message 7 of 7
(5,666 Views)