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

Welcome to the SciChart Community Forums!

Please use the forums below to ask questions about SciChart. Take a moment to read our Question asking guidelines on how to ask a good question and our support policy

We also have a tag=SciChart on Stackoverflow.com where you can earn rep for your questions!

1
0

I have created a chart that displays real-time instrument data with a sample arriving every 10ms or so. I am displaying this with a CateoryDateTimeAxis:

                <!--  Create an X Axis  -->
                <s:SciChartSurface.XAxis>
                    <s:CategoryDateTimeAxis  MinHeight="50" AutoRange="Always" VisibleRange="{Binding XAxisVisibleRange, Mode=TwoWay}"
                            AxisTitle="Time" DrawMinorGridLines="False" DrawMinorTicks="False"  TextFormatting="hh:mm:ss.ss">
                    </s:CategoryDateTimeAxis>
                </s:SciChartSurface.XAxis>

My probem is that the TextFormatting is ignored and I get labels in the form HH:mm which is not much use. Is there a way round this?

Secondly if I try to use a DateTimeAxis to show the actual times (with any gaps) the automatic scaling resets the axis to cover about two days.

Any guidance would be appreciated.

Mike

Version
.Net 4.6.1
  • You must to post comments
0
0

Hi Mike,

CategoryDateTimeAxis and DateTimeAxis have two text formatting properties:

  • TextFormatting

  • SubDayTextFormatting (used when VisibleRange shows less than a day)

You can find out more, as well as how to dynamically change text formatting at the following links:

  1. Axis Labels – TextFormatting and CursorTextFormatting]
  2. LabelProvider API – Full Control over Axis Labels

Best regards,

Andrew

  • You must to post comments
0
0

Thanks Andrew. I tried usingthe SubDayTextFormatting parameter (both with and without a TextFormatting parameter) but it made no apparent difference.

Regards,
Mike

  • You must to post comments
0
0

Hi Mike,

My mistake. I looked in the code and because CategoryDateTimeAxis is a special case, we format labels according to the level of zoom.

All of this label formatting is handled by the TradeChartAxisLabelProvider class, which is attached by default to the CategoryDateTimeAxis.LabelProvider property.

The source for the TradeChartAxisLabelProvider is included below. I’m hoping you can take this and modify it to meet your needs.

/// <summary>
/// A LabelFormatter instance to use on stock charts. Designed to be used with the <see cref="CategoryDateTimeAxis"/> and applied by default on the <see cref="SciStockChart"/> control
/// </summary>
public class TradeChartAxisLabelProvider : LabelProviderBase
{
    private readonly string[] _cursorFormatStrings = new[]
        {
            "MMM {0}",
            "yyyy {0}",
            "{0} yyyy",
            "dd MMM {0}", 
        };

    private readonly string[] _majorFormatStrings = new[]
        {
            "yyyy",
            "MMM",
            "dd MMM",
            "HH:mm"
        };

    private int _formatIndex;

    /// <summary>
    /// Called when the label formatted is initialized, with the parent axis instance
    /// </summary>
    /// <param name="parentAxis">The parent <see cref="IAxis" /> instance</param>
    /// <exception cref="System.InvalidOperationException">The TradeChartAxisLabelFormatter is only valid on instances of CategoryDateTimeAxis</exception>
    public override void Init(IAxisCore parentAxis)
    {
        var catAxis = parentAxis as CategoryDateTimeAxis;
        if (catAxis == null)
        {
            throw new InvalidOperationException("The TradeChartAxisLabelFormatter is only valid on instances of CategoryDateTimeAxis");
        }

        base.Init(parentAxis);
    }

    /// <summary>
    /// Called at the start of an axis render pass, before any labels are formatted for the current draw operation
    /// </summary>
    public override void OnBeginAxisDraw()
    {
        var catAxis = (CategoryDateTimeAxis)ParentAxis;

        var barTimeFrame = catAxis.GetBarTimeFrame();
        var visibleRangeAsDateRange = catAxis.ToDateRange((IndexRange)catAxis.VisibleRange);

        _formatIndex = 3;

        if (visibleRangeAsDateRange != null && visibleRangeAsDateRange.IsDefined)
        {
            long ticksInViewport = visibleRangeAsDateRange.Diff.Ticks;

            if (ticksInViewport > TimeSpanExtensions.FromYears(2).Ticks)
            {
                _formatIndex = 0;
            }
            else if (ticksInViewport > TimeSpan.FromDays(14).Ticks || barTimeFrame >= TimeSpan.FromDays(1).Ticks)
            {
                _formatIndex = -1;
            }
        }

        base.OnBeginAxisDraw();
    }

    /// <summary>
    /// Formats a label for the cursor, from the specified data-value passed in
    /// </summary>
    /// <param name="dataValue">The data-value to format</param>
    /// <returns>
    /// The formatted cursor label string
    /// </returns>
    public override string FormatCursorLabel(IComparable dataValue)
    {
        string formattedText;

        var dateTime = dataValue.ToDateTime();
        if (ParentAxis.CursorTextFormatting.IsNullOrEmpty())
        {
            int index = GetFormattingIndex(dateTime, true);

            formattedText = string.Format(dateTime.ToString(_cursorFormatStrings[index]),
                                 dateTime.ToString(_majorFormatStrings[index]));
        }
        else
        {
            formattedText = dateTime.ToString(ParentAxis.CursorTextFormatting);
        }

        return formattedText;
    }

    /// <summary>
    /// Formats a label for the axis from the specified data-value passed in
    /// </summary>
    /// <param name="dataValue">The data-value to format</param>
    /// <returns>
    /// The formatted label string
    /// </returns>
    public override string FormatLabel(IComparable dataValue)
    {
        var dateTime = dataValue.ToDateTime();

        var index = GetFormattingIndex(dateTime);

        var formatString = _majorFormatStrings[index];
        var formattedValue = dateTime.ToString(formatString);

        return formattedValue;
    }

    private int GetFormattingIndex(DateTime dataValue, bool forCursor = false)
    {
        var index = _formatIndex;

        if (index < 0)
        {
            index = 2;

            if (dataValue.Day == 1 && !forCursor)
            {
                index = 1;
            }
        }

        return index;
    }
}

Alternatively, if you’re not interested in the gap-collapsing ability of CategoryDateTimeAxis, there is the DateTimeAxis which is far simpler and respects TextFormatting and SubDayTextFormatting.

Best regards,
Andrew

  • You must to post comments
0
0

Thanks Andrew. As I mentioned in my original post I started out by usingthe DateTimeAxis but I got some very odd behaviour with the automatic range setting – the X axis covered a peridod of 24-48 hours with all my data in a vertical line somewhere near the centre!

I’ll have anotheer try with it to see if I can get something sensible.

Reagrds,
MIke

  • Andrew
    Hi Mike, if all your data was in the centre, it suggests the X-values have the same value. This is what scichart does when it calculates VisibleRange Max==Min (from the data). It expands VisibleRange around the data. Please check it. Alternatively if your DateTimes are different but by only nanoseconds then it could be a rounding error internally to scichart. CategoryDateTimeAxis works differently: it ignores date value and uses X-Index to space the data. Finally, an alternative (if say you do have nanoseconds) is to use NumericAxis, treat 1.0 as 1.0ns and use LabelProvider or TextFormatting to format the labels.
  • You must to post comments
Showing 4 results
Your Answer

Please first to submit.