Drawing. Basics

It is possible to redefine the OnRender method in the indicator and realize your own logic of data rendering.

It is necessary to set the EnableCustomDrawing flag ‘true’ for this method to start being called.

It is also necessary to set a list of layers where rendering will be carried out.

The list of layers is set through the SubscribeToDrawingEvents method, to which the DrawingLayouts flags are passed over.

The DrawingLayouts flags could be:

  • None - nothing will be rendered in this case
  • Historical - in this case, rendering will be called at every new candle, when contracted and when the chart is moved
  • LatestBar - rendering is called when the most recent bar is changed. As a rule, it takes place at every new tick
  • Final - the final layer, which is rendered at every chart rendering. For example, when the mouse is moved.

If the SubscribeToDrawingEvents is not called, the indicator will be rendered only when the most recent bar changes.

Example of calling the SubscribeToDrawingEvents, after which the OnRender method will be called at every new tick and after the final rendering:

SubscribeToDrawingEvents(DrawingLayouts.Final | DrawingLayouts.LatestBar);

The following objects are passed to the OnRender method:

  • RenderContext context - the context, in which rendering would take place
  • DrawingLayouts layout - the layout, which is rendered at the current moment

RenderContext.

Rendering by GDI+ like principles is actually carried out with the help of this context.

Several examples:

protected virtual void OnRender(RenderContext context, DrawingLayouts layout)
        {
            //Draw Rectangle(width=100;height=200) from point(x=5;y=10)
            context.DrawRectangle(RenderPens.Blue,new Rectangle(5,10,100,200));

            //Fill Rectangle(width=100;height=100) from point(x=0;y=0)
            context.FillRectangle(Color.DarkSalmon, new Rectangle(0, 0, 100, 100));

            //Draw line from point(x=10;y=20) to point(x=50;y=60)
            context.DrawLine(RenderPens.AliceBlue, 10,20,50,60);

            //Draw string at point(x=50;y=60)
            context.DrawString("Sample string", new RenderFont("Arial",15),Color.Black, 50, 60);

            //Draw ellipse inside rectangle
            context.DrawEllipse(new RenderPen(Color.Bisque),new Rectangle(10,10,100,100));
        }

Coordinates system

The origin or coordinates of the chart is in the upper left corner.

The coordinates system is shown in the picture below

Every indicator has a container (Container property), which contains information about the rendering area (Region property).

All main properties of the chart, mouse and keyboard could be obtained through the ChartInfo property (see The Main Indicator Properties section).

The ChartArea property, which returns ChartInfo.ChartContainer.Region, was added for convenience of access to the chart area.

The MouseLocationInfo property, which returns ChartInfo.MouseLocationInfo, was added for convenience of access to the mouse data

Also, there are ChartInfo extensions, which allow working with coordinates:

  • GetXByBar(int bar, bool isStartOfBar) - the method returns the X coordinate for the passed bar number. If isStartOfBar=true, the bar beginning coordinate is returned, otherwise the bar middle coordinate is returned
  • GetYByPrice(decimal price, bool isStartOnPriceLevel) - the method returns the Y coordinate for the passed price. If isStartOnPriceLevel=true, the price level beginning coordinate is returned, otherwise the price level middle coordinate is returned

Example of an indicator, which draws the intersection and shows the volume of the bar, over which the mouse pointer is moved.

public class SampleRendering : Indicator
    {
        public SampleRendering()
        {
            EnableCustomDrawing = true;

            //Subscribing only to drawing on final layout
            SubscribeToDrawingEvents(DrawingLayouts.Final);
        }

        protected override void OnRender(RenderContext context,  DrawingLayouts layout)
        {
            // creating pen, width 4px
            var pen = new RenderPen(Color.BlueViolet, 4);

            //drawing horizontal line
            context.DrawLine(pen, 0, MouseLocationInfo.LastPosition.Y, ChartArea.Width, MouseLocationInfo.LastPosition.Y);

            //drawing vertical line
            context.DrawLine(pen, MouseLocationInfo.LastPosition.X, 0, MouseLocationInfo.LastPosition.X, ChartArea.Height);

            var candle = GetCandle(MouseLocationInfo.BarBelowMouse);

            if (candle != null)
            {
                var font = new RenderFont("Arial", 14);
                var text = $"Total candle volume={candle.Volume}";
                var textSize = context.MeasureString(text, font);
                var textRectangle = new Rectangle(MouseLocationInfo.LastPosition.X + 10, MouseLocationInfo.LastPosition.Y + 10, (int)textSize.Width, (int)textSize.Height);

                context.FillRectangle(Color.CornflowerBlue, textRectangle);
                context.DrawString(text, font, Color.AliceBlue, textRectangle);
            }
        }

        protected override void OnCalculate(int bar, decimal value)
        {
        }
    }

Is this article helpful for you?