This is a migrated thread and some comments may be shown as answers.

TickPoint Label string is not always being displayed.

4 Answers 59 Views
Chart
This is a migrated thread and some comments may be shown as answers.
Francois
Top achievements
Rank 2
Francois asked on 19 Feb 2013, 12:45 PM
Hi

Very odd situation. I have a simple line chart (see code below) which has e.g. 100 data points. I'm using XValue on the X axis and doing ranging myself (AutoRange=false). The X values are integers but I manually override the string values for the TickPoints' Label with the actual strings I need to display for each (in general the strings cannot be determined using a format pattern). In the chart's SizeChanged event I recalculate the Step/LabelStep/MinorTickPointMultiplier etc. values each time so that things are spaced nicely (only the Step part of the calculation is shown below).

Now, if there aren't too many data points then it works perfectly and the Label string values are always shown. 

But once I exceed say 70 data points, then as I resize the form slowly (to check that it's Step calculations look OK all the way) sometimes the Label values are shown (attached image 1) and sometimes the underlying TickPoint.Value number is shown instead (image 2). It seems at some specific points it flips from the one to the other (repeatably). I checked via a breakpoint and the Labels are always getting set. It is, as if the Tick Points are being reset again afterwards.

I just cannot figure out what else is happening behind the scenes....

Here's a small example that demonstrates (just create a WPF form with a chart and attach chart's SizeChanged event)

        private void SetupChart()
        {
            // some data to visualise...
            // NB: need a reasonable number of points, doesn't exhibit at low numbers
            var data = new DataTable();
            data.Columns.Add("x", typeof(int));
            data.Columns.Add("y", typeof(double));
            var date = new DateTime(2012, 1, 1);
            var rnd = new Random();
            for (var i = 0; i < 100; i++)
            {
                var row = data.NewRow();
                row["x"] = date.Year * 12 + date.Month - 1;     // integer value calc'd so that 1st of month is evenly spaced (see UpdateLabels() below)
                row["y"] = 50 + rnd.NextDouble() * 10;
                data.Rows.Add(row);
                date = date.AddMonths(1);
            }
 
            // just set up a basic line chart for testing purposes...          
            chart.DefaultView.ChartLegend.Visibility = System.Windows.Visibility.Collapsed;
            chart.DefaultView.ChartArea.EnableAnimations = false;
            chart.DefaultView.ChartArea.AxisY.StripLinesVisibility = System.Windows.Visibility.Collapsed;
            chart.DefaultView.ChartArea.AxisX = new AxisX();
            chart.DefaultView.ChartArea.AxisX.LayoutMode = AxisLayoutMode.Auto;
            chart.DefaultView.ChartArea.AxisX.StripLinesVisibility = System.Windows.Visibility.Collapsed;
            chart.DefaultView.ChartArea.AxisX.AutoRange = false;
            chart.DefaultView.ChartArea.AxisX.MinValue = data.Rows.Cast<DataRow>().Min(r => (int)r["x"]);
            chart.DefaultView.ChartArea.AxisX.MaxValue = data.Rows.Cast<DataRow>().Max(r => (int)r["x"]);
            var mapping = new SeriesMapping();
            mapping.ItemsSource = data;
            mapping.ItemMappings.Add(new ItemMapping("x", DataPointMember.XValue) { FieldType = data.Columns["x"].DataType });
            mapping.ItemMappings.Add(new ItemMapping("y", DataPointMember.YValue) { FieldType = data.Columns["y"].DataType });
            mapping.SeriesDefinition = new LineSeriesDefinition();
            chart.SeriesMappings.Add(mapping);
 
            // to force initial ranging calc to occur...
            chart_SizeChanged(null, null);
        }
 
 
 
        /// <summary>
        /// Event handler
        /// </summary>
        private void chart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e == null || e.WidthChanged)
            {
                RecalcRange();
                UpdateLabels();
            }
        }
 
 
 
        /// <summary>
        /// Event handler
        /// </summary>
        private void updateLabelsButton_Click(object sender, RoutedEventArgs e)
        {
            // manual force recalculate of the labels
            UpdateLabels();
        }
 
 
 
 
        private void RecalcRange()
        {
#if true
            // pick a reasonable step value...
            var width = chart.DefaultView.ChartArea.ActualWidth;
            var range = chart.DefaultView.ChartArea.AxisX.MaxValue - chart.DefaultView.ChartArea.AxisX.MinValue;
            var step = Math.Max(1, (int)Math.Ceiling(range * chart.DefaultView.ChartArea.AxisX.TicksDistance / width));
            chart.DefaultView.ChartArea.AxisX.Step = step;
#else
            // it happens for certain constants, such as 8 (but not for 9 etc)...
            chart.DefaultView.ChartArea.AxisX.Step = 8;
#endif
        }
 
 
 
        private void UpdateLabels()
        {
            foreach (var tickPoint in chart.DefaultView.ChartArea.AxisX.TickPoints)
            {
                // Reconstitutes the date string for the label from the integer value in x column..
                var dt = new DateTime((int)tickPoint.Value / 12, ((int)tickPoint.Value % 12) + 1, 1);
                tickPoint.Label = dt.ToString("MMM-yy");
            }
        }

4 Answers, 1 is accepted

Sort by
0
Petar Kirov
Telerik team
answered on 22 Feb 2013, 01:30 PM
Hi Francois,

I've managed to reproduce the issue, by using your code.

The problem is that RadChart internally overwrites the TickPoints collection after its size gets changed. To work around this you need to update the labels a bit later. You can do this by attaching to the LayoutUpdate event on size changed:
private void chart_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (e == null || e.WidthChanged)
    {
        RecalcRange();
        //UpdateLabels(); update the labels later
        chart.DefaultView.ChartArea.LayoutUpdated += ChartArea_LayoutUpdated;
    }
}
 
void ChartArea_LayoutUpdated(object sender, EventArgs e)
{
    UpdateLabels();
    //to avoid causing a lauout cycle
    chart.DefaultView.ChartArea.LayoutUpdated -= ChartArea_LayoutUpdated;
}

I hope this helps.
 
All the best,
Petar Kirov
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Francois
Top achievements
Rank 2
answered on 22 Feb 2013, 01:53 PM
Hi Petar,

Thanks, that is a great help.

I am seeing now, that on re-populate of a complex chart the ChartArea_LayoutUpdated event fires a bit late, so that I'm seeing briefly the numbers appearing before they change to the labels and some chart layout readjustments.

But I'm thinking, a combined solution (my original plus your suggestion) will probably allow me to produce the correct results in all cases...

Regards
0
Francois
Top achievements
Rank 2
answered on 22 Feb 2013, 02:33 PM
Nope, there's still a nulling of the labels and layout/screen refresh occurring before the effects of the ChartArea_LayoutUpdated come through, which is very apparent on a more complex chart (e.g. 500 points) and doesn't look that great.

There isn't possibly another event that occurs a bit earlier on to which I could attach? Or is this something that ultimately should be addressed on the Telerik side?

0
Petar Kirov
Telerik team
answered on 27 Feb 2013, 01:12 PM
Hi Francois,

There are two solutions that I can currently think of.

1. Do not manually update the labels at all. Instead create a new property on your DataItem which will return the "x" as a DataTime value: 
public class DataItem
{
    public double x;
    public double y;
 
    public DateTime Date
    {          
        get
        {              
            return new DateTime((int)x / 12, ((int)x % 12) + 1, 1);
        }
    }
}
Then you can specify a LabelFormat like this:
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "MMM-yy";
(You can also just return a string in the Date property, but the first approach separates the data from its visual representation.)
This should also provide a performance improvement, because your not causing unnecessary layout cycles.

2. The second solution is more of workaround - set an empty label format, so that initially nothing is displayed: 
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = " ";

Regards,
Petar Kirov
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

Tags
Chart
Asked by
Francois
Top achievements
Rank 2
Answers by
Petar Kirov
Telerik team
Francois
Top achievements
Rank 2
Share this question
or