Chart for .NET / User's Guide / Miscellaneous / Custom Painting

Custom Painting

All content panels in Nevron Chart for .NET have an associated PaintCallback property, which you can initialize with your custom implementation of the INPaintCallback interface and perform custom painting before or after the panel content is painted. For the purpose of this topic we'll examine how to paint custom markers on a bar chart which will also show how to perform coordinate transformation in order to obtain the position of a certain datapoint on the screen.

 Intercepting the panel AfterPaint event

To provide custom code to execute after the root panel is painted you should write code similar to:

C#
Copy Code
...
NChart chart = chartControl.Charts[0];
chart.PaintCallback = new CustomPaintCallback();

...

public class CustomPaintCallback : INPaintCallback
{
    ...

    public void OnAfterPaint(NPanel panel, NPanelPaintEventArgs eventArgs)
    {
        // do something in after paint 
    }
} 
Visual Basic
Copy Code
...
Dim chart As NChart = chartControl.Charts(0)
chart.PaintCallback = New CustomPaintCallback()

...

Public Class CustomPaintCallback 
    Implements INPaintCallback
    ...

    Public Sub OnAfterPaint(ByVal panel As NPanel, ByVal eventArgs As NPanelPaintEventArgs) Handles INPaintCallback.OnAfterPaint
        ' do something in after paint
    End Sub
End Sub
 Painting using GDI+ device

After the event is intercepted you may draw on the chart canvas by converting the EventArgs argument to NPanelPaintEventArgs which has an associated GDI+ Graphics object. The following example creates a simple bar chart and draws an upward arrow on the biggest bar.

C#
Copy Code
private void Form1_Load(object sender, System.EventArgs e)
{
    NChart chart = chartControl.Charts[0];

    chart.PaintCallback = new CustomPaintCallback();
    NBarSeries bar = chart.Series.Add(SeriesType.Bar) as NBarSeries;

    bar.Values.Add(10);
    bar.Values.Add(20);
    bar.Values.Add(30);
    bar.Values.Add(25);
    bar.DataLabelStyle.Visible = false;

    chartControl.Refresh();
}

public class CustomPaintCallback : INPaintCallback
{
    public void OnAfterPaint(NPanel panel, NPanelPaintEventArgs eventArgs)
    {
        // get chart and bar series from control
        NChart chart = chartControl.Charts[0]; 
        NBarSeries bar = chart.Series[0] as NBarSeries;

        // find the biggest value double maxValue = (double)bar.Values[0];
        int maxValueIndex = 0;

        for (int i = 1; i < bar.Values.Count; i++)
        {
            if (maxValue < (double)bar.Values[i])
            {
                maxValue = (double)bar.Values[i];
                maxValueIndex = i;
            }
        }

        // transform coordinates
        NScale2DToViewTransformation scale2DToViewTransformation = new NScale2DToViewTransformation(chartControl.View.Context, chart, (int)StandardAxis.PrimaryX, (int)StandardAxis.PrimaryY);
        NPointF viewPoint = new NPointF();
        scale2DToViewTransformation.Transform(new NVector2DD(maxValueIndex, (double)bar.Values[maxValueIndex]), ref viewPoint);
        // create a path for arrow RectangleF arrowBounds = new RectangleF(viewPoint.X - 25, viewPoint.Y, 50, 50);

        float arrowStubWidth = arrowBounds.Width * 30.0f / 100.0f;
        float arrowHeadHeight = arrowBounds.Height * 30.0f / 100.0f;

        PointF[] lines = new PointF[7];
        lines[0].X = arrowBounds.Left + arrowBounds.Width / 2;
        lines[0].Y = arrowBounds.Top;

        lines[1].X = arrowBounds.Right;
        lines[1].Y = arrowBounds.Top + arrowHeadHeight;

        lines[2].X = arrowBounds.Right - (arrowBounds.Width - arrowStubWidth) / 2;
        lines[2].Y = arrowBounds.Top + arrowHeadHeight;

        lines[3].X = lines[2].X;
        lines[3].Y = arrowBounds.Bottom;

        lines[4].X = arrowBounds.Left + (arrowBounds.Width - arrowStubWidth) / 2;
        lines[4].Y = arrowBounds.Bottom;

        lines[5].X = lines[4].X;
        lines[5].Y = arrowBounds.Top + arrowHeadHeight;

        lines[6].X = arrowBounds.Left;
        lines[6].Y = lines[1].Y;

        // create a graphics path GraphicsPath path = new GraphicsPath();
        path.AddLines(lines);
        path.CloseAllFigures();

        // obtain device Graphics graphics = eventArgs.Graphics.DeviceGraphics;
        // create a brush and pen SolidBrush brush = new SolidBrush(Color.Red);
        Pen pen = new Pen(Color.Black, 1);

        // paint the arrow graphics.FillPath(brush, path);
        graphics.DrawPath(pen, path);

        // dispose objects path.Dispose();
        brush.Dispose();
        pen.Dispose();
    }
}
Visual Basic
Copy Code
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim chart As NCartesianChart = chartControl.Charts(0)
    chart.PaintCallback = New CustomPaintCallback()

    Dim bar As NBarSeries = chart.Series.Add(SeriesType.Bar)

    bar.Values.Add(10)
    bar.Values.Add(20)
    bar.Values.Add(30)
    bar.Values.Add(25)
    bar.DataLabelStyle.Visible = False

    chartControl.Refresh()
End Sub

Public Class CustomPaintCallback
    Implements INPaintCallback   
    Public Sub OnAfterPaint(ByVal panel As NPanel, ByVal eventArgs As NPanelPaintEventArgs) Handles INPaintCallback.OnAfterPaint 
        ' get chart and bar series from control
        Dim chart As NCartesianChart = chartControl.Charts(0)
        Dim bar As NBarSeries = chart.Series(0)

        ' find the biggest value
        Dim maxValue As Double = CType(bar.Values(0), Double)
        Dim maxValueIndex As Integer = 0

        For i As Integer = 0 To bar.Values.Count - 1
            If (maxValue < CType(bar.Values(i), Double)) Then
                maxValue = CType(bar.Values(i), Double)
                maxValueIndex = i
            End If
        Next

        ' transform coordinates
        Dim scale2DToViewTransformation As New NScale2DToViewTransformation(chartControl.View.Context, chart, CType(StandardAxis.PrimaryX, Integer), CType(StandardAxis.PrimaryY, Integer))
        Dim viewPoint As New NPointF
        scale2DToViewTransformation.Transform(New NVector2DD(maxValueIndex, CType(bar.Values(maxValueIndex), Double)), viewPoint)

        ' create a path for arrow
        Dim arrowBounds As RectangleF = New RectangleF(viewPoint.X - 25, viewPoint.Y, 50, 50)

        Dim arrowStubWidth As Single = arrowBounds.Width * 30.0F / 100.0F
        Dim arrowHeadHeight As Single = arrowBounds.Height * 30.0F / 100.0F

        Dim lines(6) As PointF
        lines(0).X = arrowBounds.Left + arrowBounds.Width / 2
        lines(0).Y = arrowBounds.Top

        lines(1).X = arrowBounds.Right
        lines(1).Y = arrowBounds.Top + arrowHeadHeight

        lines(2).X = arrowBounds.Right - (arrowBounds.Width - arrowStubWidth) / 2
        lines(2).Y = arrowBounds.Top + arrowHeadHeight

        lines(3).X = lines(2).X
        lines(3).Y = arrowBounds.Bottom

        lines(4).X = arrowBounds.Left + (arrowBounds.Width - arrowStubWidth) / 2
        lines(4).Y = arrowBounds.Bottom

        lines(5).X = lines(4).X
        lines(5).Y = arrowBounds.Top + arrowHeadHeight

        lines(6).X = arrowBounds.Left
        lines(6).Y = lines(1).Y

        ' create a graphics path
        Dim path As GraphicsPath = New GraphicsPath
        path.AddLines(lines)
        path.CloseAllFigures()
    
        ' obtain device
        Dim graphics As Graphics = CType(e, NPanelPaintEventArgs).Graphics.DeviceGraphics

        ' create a brush and pen
        Dim brush As New SolidBrush(Color.Red)
        Dim pen As New Pen(Color.Black, 1)

        ' paint the arrow
        graphics.FillPath(brush, path)
        graphics.DrawPath(pen, path)

        ' dispose objects
        path.Dispose()
        brush.Dispose()
        pen.Dispose()
    End Sub
End Sub
 Painting Using Nevron Device

You can also paint directly to the Nevron Device containing the GDI+ graphics object and take full advantage of its advanced presentation features.

The following code will paint the arrow with applied gradient and lighting image filter:

C#
Copy Code
// obtain device
NGraphics graphics = ((NPanelPaintEventArgs)e).Graphics;
NGradientFillStyle gradient = new NGradientFillStyle(GradientStyle.Horizontal, GradientVariant.Variant1, Color.White, Color.Red);
gradient.ImageFiltersStyle.Filters.Add(new NLightingImageFilter());
NStrokeStyle stroke = new NStrokeStyle(1, Color.Black);

graphics.PaintPath(gradient, stroke, path);
Visual Basic
Copy Code
' obtain device
Dim graphics As NGraphics = CType(e, NPanelPaintEventArgs).Graphics
Dim gradient As NGradientFillStyle = New NGradientFillStyle(GradientStyle.Horizontal, GradientVariant.Variant1, Color.White, Color.Red)
gradient.ImageFiltersStyle.Filters.Add(New NLightingImageFilter)
Dim stroke As NStrokeStyle = New NStrokeStyle(1, Color.Black)

graphics.PaintPath(gradient, stroke, path)
 Related Examples

Windows Forms: Custom Painting\Custom Painting Using GDI+

Windows Forms: Custom Painting\Custom Painting Nevron Device

Web Forms: Custom Painting\Custom Painting Using GDI+

Web Forms: Custom Painting\Custom Painting Nevron Device