Labels for out of range values inside line series graph

Hello,

I'm usign a LineSeries chart to display some date where I customized the label template. I also have the range of the graph for the Y axis, minimum and maximum. Is there a way to display the label for an out of range value ? As you can see from the image below, there is a 3rd point, out of range somewhere above.







9 Replies

LA Lavanya Anaimuthu Syncfusion Team June 27, 2018 03:45 AM UTC

Hi Codrina Merigo, 
  
We do not have direct support for display the out of range data marker label in Chart. However, we have achieved this requirement by using the TextAnnotation and ActualRangeChanged event in ChartAxis. In this event, we have created the label and added the TextAnnotation to Chart control. Please refer the code snippet for your information. 
 
  
Code snippet [Xaml]:   
  
<chart:SfChart.SecondaryAxis> 
     <chart:NumericalAxis Minimum="70" Maximum="150" ActualRangeChanged ="NumericalAxis_ActualRangeChanged" /> 
</chart:SfChart.SecondaryAxis> 
  
  
Code snippet [C#]:   
  
private void NumericalAxis_ActualRangeChanged(object sender, ActualRangeChangedEventArgs e) 
{ 
     if (chart.ChartAnnotations != null) 
     { 
           chart.ChartAnnotations.Clear(); 
     } 
    var data = viewModel.Collection; 
    for (int i = 0; i < data.Count-1; i++) 
     { 
        if (data[i].YValue > (double)e.ActualMaximum || data[i].YValue < (double)e.ActualMinimum) 
        { 
                   TextAnnotation text = new TextAnnotation(); 
                    text.Text = data[i].Date.ToString("dd"); 
                    text.X1 = data[i].Date; 
                    text.Y1 = data[i].YValue > (double)e.ActualMaximum ? (double)e.ActualMaximum - 1 : (double)e.ActualMinimum + 1; 
                    chart.ChartAnnotations.Add(text); 
         } 
    } 
} 
   
We have prepared sample for this solution. Please download the sample from the following location.  
  
  
Please let us know, if you need further assistance on this.     
   
Regards,   
Lavanya Anaimuthu.  



CM Codrina Merigo June 28, 2018 07:32 AM UTC

Hello and thank you for your quick answer. I'm trying to adapt your solution, but I realised that I'm generating multiple charts inside the page ( for different values collections) using a Template Selector so foreach chart I don't have an x:name.
Is there a quick way to locate each chart inside the page in order to add the annotations to that specific chart ? 
Maybe something like a dynamic x:name lets' say chart1 , chart2 ...


Thank you




LA Lavanya Anaimuthu Syncfusion Team June 29, 2018 11:59 AM UTC

Hi Codrina Merigo, 
 
We have analyzed your requirement and we have achieved this by getting children in page and raising ActualRangeChanged event for each children as per the below code snippet.  
 
Code snippet [XAML]:   
<StackLayout x:Name="stack"> 
</StackLayout> 
  
Code snippet [C#]:    
for (int i = 0; i < stack.Children.Count; i++) 
   { 
      if (stack.Children[i] is SfChart) 
         { 
            ((SfChart)stack.Children[i]).SecondaryAxis.ActualRangeChanged += SecondaryAxis_ActualRangeChanged; 
         } 
   } 
 
 
private void SecondaryAxis_ActualRangeChanged(object sender, ActualRangeChangedEventArgs e) 
{ 
    var axis = sender as NumericalAxis; 
    ObservableCollection<Model> model = null; 
 
    foreach (var item in stack.Children) 
    { 
         if(item is SfChart && (item as SfChart).SecondaryAxis == axis) 
         { 
                 SfChart chart = item as SfChart; 
                 chart.ChartAnnotations?.Clear(); 
                 foreach (var series in chart.Series) 
                { 
                      model = series.ItemsSource as ObservableCollection<Model>; 
                      for (int j = 0; j < model.Count - 1; j++) 
                      { 
                               if (model[j].YValue > (double)e.ActualMaximum || model[j].YValue < (double)e.ActualMinimum) 
                               { 
                                     TextAnnotation text = new TextAnnotation(); 
                                      text.Text = model[j].Date.ToString("dd"); 
                                      text.X1 = model[j].Date; 
                                      text.Y1 = model[j].YValue > (double)e.ActualMaximum ? (double)e.ActualMaximum - 1 : (double)e.ActualMinimum + 1; 
                                       chart.ChartAnnotations.Add(text); 
                                   } 
                         } 
                   } 
 
           break; 
        } 
    } 
} 
 
  
Please let us know, if you need further assistance on this.     
   
Regards,   
Lavanya Anaimuthu.  



AD Alessandro Del Sole July 3, 2018 08:55 AM UTC

I'm interested too in this topic. The solution is nicely working, but what if you have your charts within a data template you conditionally assign with data template selectors? In this case, giving x:names to XAML visual elements is of no help.

Thanks for any suggestions on this.


MP Michael Prabhu M Syncfusion Team July 6, 2018 06:41 AM UTC

Hi Allesandro, 
 
We have achieved this requirement by hooking BindingContextChanged, ActualRangeChanged event in each SfChart in template with same handler as like in below code snippet. 
 
Code snippet [XAML]:   
<chart:SfChart VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="chart" Grid.Row="0"     BindingContextChanged="Chart_BindingContextChanged" > 
 
  
Code snippet [C#]:    
List< SfChart> SfChartCollection; 
 
private void chart_BindingContextChanged(object sender, EventArgs e) 
  { 
   SfChartCollection.Add(sender as SfChart); 
    (sender as SfChart).SecondaryAxis.ActualRangeChanged += SecondaryAxis_ActualRangeChanged; 
} 
 
 
Code snippet [C#]:    
private void SecondaryAxis_ActualRangeChanged(object sender, ActualRangeChangedEventArgs e) 
        { 
            var axis = sender as NumericalAxis; 
            ObservableCollection<Model> model = null; 
 
            foreach (var chart in SfChartCollection) 
            { 
                if (chart.SecondaryAxis == axis) 
                { 
                    chart.ChartAnnotations?.Clear(); 
                    foreach (var series in chart.Series) 
                    { 
                        model = series.ItemsSource as ObservableCollection<Model>; 
                        for (int j = 0; j < model.Count - 1; j++) 
                        { 
                            if (model[j].YValue > (double)e.ActualMaximum || model[j].YValue < (double)e.ActualMinimum) 
                            { 
                                TextAnnotation text = new TextAnnotation(); 
                                text.Text = model[j].Date.ToString("dd"); 
                                text.X1 = model[j].Date; 
                                text.Y1 = model[j].YValue > (double)e.ActualMaximum ? (double)e.ActualMaximum - 3 : (double)e.ActualMinimum + 3; 
                                chart.ChartAnnotations.Add(text); 
                            } 
                        } 
                    } 
                } 
            } 
        } 
 
 
We have modified our sample based on this, please find the sample from the following location.  
 
Sample : SimpleSample
 
 
Thanks, 
Michael 




AS Alessandro Stamera July 17, 2018 08:17 AM UTC

Hello, I'm interested too in this topic, but I noticed that the event Chart_BindingContextChanged, in my own project, occurs when the design of the chart is not completed, so the parameter called "sender" is null and we cannot add the event SecondaryAxis_ActualRangeChanged. The cause of this situation is that minimum and maximum values are set at runtime because we load data from a web service.

Can you help me to solve this problem?



MP Michael Prabhu M Syncfusion Team July 18, 2018 10:41 AM UTC

Hi Alessandro, 
 
We have analyzed your query and you can overcome your issue by hooking PropertyChanged event of ChartAxis and DataMarkerLabelCreated event of ChartSeries as like in the below code snippet.  
 
private void CustomNumericalAxis_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "Maximum" || e.PropertyName == "Minimum") 
    { 
        var axis = sender as CustomNumericalAxis; 
         
        var sfchart = axis.Chart; 
        var chartAnnoatations = new ChartAnnotationCollection(); 
        foreach (var series in sfchart.Series) 
        { 
            var viewModel = series.ItemsSource as ObservableCollection<Model>; 
            for (int i = 0; i < viewModel.Count; i++) 
            { 
                var model = viewModel[i]; 
                if (model.YValue > axis.Maximum || model.YValue < axis.Minimum) 
                { 
                    TextAnnotation text = new TextAnnotation(); 
                    text.Text = model.Date.ToString("dd"); 
                    text.X1 = model.Date; 
                    text.Y1 = (double)(model.YValue > axis.Maximum ? axis.Maximum - 3 : axis.Minimum + 3); 
                    chartAnnoatations.Add(text); 
                } 
            } 
        } 
        sfchart.ChartAnnotations = chartAnnoatations; 
    } 
} 
         
void Handle_DataMarkerLabelCreated(object sender, Syncfusion.SfChart.XForms.ChartDataMarkerLabelCreatedEventArgs e) 
{ 
    var series = sender as CustomLineSeries; 
    var sfchart = series.Chart; 
    var viewModel = series.ItemsSource as ObservableCollection<Model>; 
    var model = viewModel[e.DataMarkerLabel.Index]; 
     
    if (model.YValue > sfchart.SecondaryAxis.VisibleMaximum || model.YValue < sfchart.SecondaryAxis.VisibleMinimum) 
    { 
        TextAnnotation text = new TextAnnotation(); 
        text.Text = model.Date.ToString("dd"); 
        text.X1 = model.Date; 
        text.Y1 = model.YValue > sfchart.SecondaryAxis.VisibleMaximum ? sfchart.SecondaryAxis.VisibleMaximum - 3 : sfchart.SecondaryAxis.VisibleMinimum + 3; 
        sfchart.ChartAnnotations.Add(text); 
    } 
} 
public class CustomLineSeries : LineSeries 
{ 
    public SfChart Chart { get; set; } 
} 
public class CustomNumericalAxis : NumericalAxis 
{ 
    public SfChart Chart { get; set; } 
} 
 
Note: DataMarkerLabelCreated event is available from Volume 2 2018 release. 
We have prepared a sample based on this requirement and it can be downloaded from the link below. 
Sample: 138326_new 
Regards, 
Michael 



CM Codrina Merigo July 23, 2018 03:16 PM UTC

Hi,

I tried to use the samples provided by you both on Windows pc and on Mac and I cannot get them work. 
On mac I have this error ' /Users/***Downloads/138326_new1676365312/SimpleSample/SimpleSample.iOS/MTOUCH: Error MT2101: Can't resolve the reference 'System.String Syncfusion.Licensing.FusionLicenseProvider::GetLicenseType(Syncfusion.Licensing.Platform)', referenced from the method 'System.Void Syncfusion.RangeNavigator.XForms.SfDateTimeRangeNavigator::ValidateLicenseKey()' in 'Syncfusion.Licensing, Version=16.2.0.41, Culture=neutral, PublicKeyToken=null'. (MT2101) (SimpleSample.iOS)' 
while on Windows I manged to build it on the ipad and it shows the Licensing popup, after cliking the 'ok' button I can only see a blank page.

I also try to set up Android as a start up project, but Is not building. 
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(2,2): Error: Exception while loading assemblies: System.IO.FileNotFoundException: Could not load assembly 'Syncfusion.Licensing, Version=16.2.0.41, Culture=neutral, PublicKeyToken='. Perhaps it doesn't exist in the Mono for Android profile?
File name: 'Syncfusion.Licensing.dll'
  at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference reference, Mono.Cecil.ReaderParameters parameters) [0x0009a] in /Users/builder/data/lanes/5945/dffc5912/source/monodroid/external/xamarin-android/external/Java.Interop/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:229 
  at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference reference) [0x00000] in /Users/builder/data/lanes/5945/dffc5912/source/monodroid/external/xamarin-android/external/Java.Interop/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:179 
  at Xamarin.Android.Tasks.ResolveAssemblies.AddAssemblyReferences (Java.Interop.Tools.Cecil.DirectoryAssemblyResolver resolver, System.Collections.Generic.ICollection`1[T] assemblies, Mono.Cecil.AssemblyDefinition assembly, System.Boolean topLevel) [0x0014a] in <96027da06dd5411688a31011072e256f>:0 
  at Xamarin.Android.Tasks.ResolveAssemblies.Execute (Java.Interop.Tools.Cecil.DirectoryAssemblyResolver resolver) [0x00237] in <96027da06dd5411688a31011072e256f>:0  (SimpleSample.Droid)


Is there something I'm missing ? I'm using the last version of Visual Studio,Xcode, simulators etc both on Windows and Mac.3

Thank you for your cooperation.

Codrina


MP Michael Prabhu M Syncfusion Team July 24, 2018 10:08 AM UTC

Hi Codrina, 
 
Sorry for the inconvenience caused, we were able to reproduce this issue while using 16.2.0.41 version of our assemblies, but the same is fixed in 16.2.0.42 version, can you please update the NuGet packages for controls to 16.2.0.42 version? 
 
Note: Please clear the NuGet cache before installing the new version.  
 
Thanks, 
Michael  
  


Loader.
Up arrow icon