SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and now iOS Charting & Android Chart Components

Answered
1
0

Is there any possibility to draw Bands only for special days, like Saturday, Sunday, BankHolidays… ?

  • You must to post comments
Best Answer
2
0

Hi Jan,

Thanks for your enquiry! Yes as it happens there is a way to draw custom axis bands. I will give you two options below.

Option 1 – Use BoxAnnotation API

Using our Annotations API, you can create a BoxAnnotation for every weekend band. Use CoordinateMode.RelativeY to stretch the annotation to fit vertically on the viewport.

<s:BoxAnnotation X1="{Binding weekendStart}" X2="{Binding weekendEnd}" Y0="0" Y1="1" CoordinateMode="RelativeY"/>

This will give the effect of a vertical band spanning the weekend. You will be responsible for maintaining the collection of annotations and also note that annotations are WPF UIElements, which will slow down your chart if you have hundreds or thousands of them.

Option 2 – Hack our RenderContext API

I’ve longed to do a tutorial on the power of our RenderContext API for a while. This is a brief intro.

If you inherit DateTimeAxis and override DrawGridLines, you can draw anything to the bitmap layer

In this sample below we override DrawGridLines and draw vertical bands between weekdays, using the IRenderContext.Layers property to enqueue drawing before Gridlines are drawn.

public class DateTimeAxisEx : DateTimeAxis
{
    protected override void DrawGridLines(IRenderContext2D renderContext, TickCoordinates tickCoords)
    {
        // Draw the Axis gridlines
        base.DrawGridLines(renderContext, tickCoords);

        // Now, we're going to enqueue some more drawing in the AxisBands layer. This allows us to 
        // draw out of order but the composition is done in order. Axis Bands are drawn BELOW gridlines themselves
        renderContext.Layers[RenderLayer.AxisBands].Enqueue(() => DrawBandsOnWeekends(renderContext));

        // You could draw renderContext.FillPolygon right here but it will draw above the gridlines. 
    }

    private void DrawBandsOnWeekends(IRenderContext2D renderContext)
    {
        // Calculate weekends in the Axis VisibleRange
        var bandCoordinates = FindWeekendCoordinates();

        // Create a brush for drawing
        using (var bandBrush = renderContext.CreateBrush(AxisBandsFill))
        {
            for (int i = 1; i < bandCoordinates.Count; i += 2)
            {
                var coord0 = bandCoordinates[i - 1];
                var coord1 = bandCoordinates[i];

                DrawAxisBand(renderContext, bandBrush, coord0, coord1);
            }
        }
    }

    // Draws a single axis band (rectangle) to the Rendercontext
    private void DrawAxisBand(IRenderContext2D renderContext, IBrush2D bandBrush, float coord0, float coord1)
    {
        var renderSurface = RenderSurface;
        if (renderSurface == null) return;

        var topLeft = new Point(coord0, 0);
        var bottomRight = new Point(coord1, renderSurface.ActualHeight);

        // Draw the rectangle
        renderContext.FillRectangle(bandBrush, topLeft, bottomRight, 0d);
    }

    // Brute force method to find pixel coordinates that span start and end of weekend. 
    // This could be improved, or modified to suit other needs
    private List<float> FindWeekendCoordinates()
    {
        var coordCalculator = this.GetCurrentCoordinateCalculator();
        List<float> coordinates = new List<float>();
        var startDate = ((DateTime)this.VisibleRange.Min).Date;
        var endDate = ((DateTime)this.VisibleRange.Max).Date;

        TimeSpan diff = endDate - startDate;
        int days = diff.Days;
        for (var i = 0; i <= days; i++)
        {
            var testDate = startDate.AddDays(i);
            switch (testDate.DayOfWeek)
            {
                case DayOfWeek.Saturday:
                    coordinates.Add((float)coordCalculator.GetCoordinate(testDate));
                    break;
                case DayOfWeek.Sunday:
                    // First coordinate found is a Sunday? Add left-edge of viewport
                    if (coordinates.Count == 0)
                    {
                        coordinates.Add((float)coordCalculator.GetCoordinate(startDate));
                    }
                    coordinates.Add((float)coordCalculator.GetCoordinate(testDate));
                    break;
            }
        }            

        // Odd number of coordinates? Add the right-edge of viewport
        if (coordinates.Count % 2 != 0)
        {
            coordinates.Add((float)coordCalculator.GetCoordinate(endDate));
        }

        return coordinates;
    }
}

And the result is below.

Does this help? The Weekend calculator method is pretty brute force and could be optimised. Also drawing large blocks could be virtualised (e.g. don’t draw weekend bands if VisibleRange.Max – Min is greater than 1 year for instance).

Thanks and regards,

Andrew

Images
  • Jan Kaiser
    Thanks. I was thinking about use of the annotation API, but the possibility of hacking RenderContext API looks better (faster).
  • Andrew
    I just tried it. It is. When you blow up the chart to 4K screen size there is a noticeable slowdown in our default software renderer, but not in DirectX (as the limit becomes number of pixels to fill). The above DateTimeAxisEx works in v3.2 of Scichart, but should be the same for v3.1. Try it! Let me know.
  • You must to post comments
0
0

Hi Andrew,
I came from this thread.
Is it possible to make Option 2 work with CategoryDateTimeAxis?
Changing DateTimeAxis to CategoryDateTimeAxis will just get empty ChartSurface.

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.