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

In This Topic
    Custom Painting
    In This Topic

    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