Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using Candlestick Chart in Xamarin Forms

DZone's Guide to

Using Candlestick Chart in Xamarin Forms

This article walks though how you can utilize a Candlestick chart in your Xamarin Forms application.

· Mobile Zone
Free Resource

Get gorgeous, multi-touch charts for your iOS application with just a few lines of code.

Whether or not a feature is supported in Xamarin Forms, if it's supported in the underlying native platforms, you can still make use of it. We walk though how you can utilize a Candlestick chart in your Xamarin Forms application.

In this post we will demonstrate that you can utilize financial series in the Telerik Chart for Xamarin Forms.

This type of series is not exposed in the current version of Telerik UI for Xamarin suite out of the box. However, the native platforms (Telerik UI for Android, Telerik UI for iOS and Telerik UI for Windows Universal) support such series. This means that despite the fact that the API is not unified for Xamarin Forms, you are still capable of using such visualizations in your applications with a bit of manual work. The final look should be similar:

candlestickseries-xamarin-renderer

Requirements

You need to download the 2016.2.513 (2016 Q2) or later version of the Telerik UI for Xamarin Forms suite of controls and the latest Xamarin Forms version.

In order to control the visualization of the financial series among the different platforms, a customized RadCartesianChart renderer (one per platform) is required.
The next and actually last piece of the puzzle would be a level of abstraction representing the financial series. It should be used in the portable project of the solution. This abstraction would simply be a class deriving from the base series class included in our suite.

Creating Financial Series

This step is the easiest. In the portable project users can create a class deriving from the Telerik.XamarinForms.Chart.CartesianSeries. In order to follow the series naming convention it would be wise to call this class CandlesticskSeries since it will visualize candlesticks.

public class CandlestickSeries : CartesianSeries

Why Custom RadCartesianChart Renderer?

Customizing the renderer is required because this is the only place where you have access to the Telerik UI for XamarinForms (or XF for short) and the native control (Android, iOS, and WinRT charts) at the same time.

The ItemsSource of the native component can be populated based on the ItemsSource of the XF control. For every newly created CandlestickSeries the respective native series will be created, populated with data and added to the native charting component.

Android Renderer

Creating a custom renderer is a straightforward operation. Simply create a new class deriving from Telerik.XamarinForms.ChartRenderer.Android.CartesianChartRenderer and you are ready to continue with the customization itself. The default renderer exposes the OnElementPropertyChanged() method which should be overridden for the purposes of our attempt. This is the entry point for the customization.

public class FinancialSeriesChartRenderer : CartesianChartRenderer
       protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            base.OnElementPropertyChanged(sender, e);

Next we need to ensure that the default renderer has done its job, initializing the XF and the native controls. This can be done by simply implementing our code after the base logic.

This base logic will initialize for us the chart itself, its axes, grid lines, behaviors and all known types of series along with the settings that are applied to those components. If there is something unknown to the RadCartesianChart, it will ignore it. This is why our custom code should take care of the custom series that will be used.

public class FinancialSeriesChartRenderer : CartesianChartRenderer
       protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals("Renderer"))

The easiest way to correctly create native series is to refer to the Telerik UI for Android documentation. According to the article you need to create two additional classes:

  • Java.Lang.Object representing a single data point
  • Customized DataPointBinding class mapping the properties of the newly created Java.Lang.Object to the properties defined in the business data object used in the portable project

For the purpose of this article we will create a CustomDataPointBinding which will use reflection to get the Java.Lang.Object through Xamarin. This will help you to fill the required data for the Android series. The implementation of the class should look like this:

public class CandlestickDataBinding : Com.Telerik.Widget.Chart.Engine.Databinding.DataPointBinding
       public CandlestickDataBinding(string name)
            this.propertyName = name;
       private MethodInfo getMethod;
       private string propertyName;
       public override Java.Lang.Object GetValue(Java.Lang.Object item)
           var instance = item.GetType().GetProperty("Instance").GetValue(item);
           if (this.getMethod == null)
                this.getMethod = instance.GetType().GetProperty(this.propertyName).GetGetMethod();
           var value = this.getMethod.Invoke(instance, null);
           return value.ToJavaObject();

The next step is to create, populate and add a Com.Telerik.Widget.Chart.Visualization.CartesianChart.Series.Categorical.CandlestickSeries for every custom CandlestickSeries (defined earlier in the post):

namespace ChartFinancialSeries.Android
       public class FinancialSeriesChartRenderer : CartesianChartRenderer
           protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
               base.OnElementPropertyChanged(sender, e);
               if (e.PropertyName.Equals("Renderer"))
                   var chart = sender as RadCartesianChart;
                   if (chart != null)
                       foreach (var series in chart.Series)
                           if (series as Portable.CandlestickSeries == null) 
                               continue;
                               var androidSeries = new Com.Telerik.Widget.Chart.Visualization.CartesianChart.Series.Categorical.CandlestickSeries();
                               androidSeries.CategoryBinding = new CandlestickDataBinding("Category");
                               androidSeries.OpenBinding = new CandlestickDataBinding("Open");
                               androidSeries.HighBinding = new CandlestickDataBinding("High");
                               androidSeries.LowBinding = new CandlestickDataBinding("Low");
                               androidSeries.CloseBinding = new CandlestickDataBinding("Close");
                               if (series.ItemsSource != null)
                                   androidSeries.Data = new Java.Util.LinkedList(series.ItemsSource.OfType().ToArray());
                               else
                                   androidSeries.Data = new Java.Util.LinkedList(new object[] { });
                               this.Control.Series.Add(androidSeries);                           }

With this, the implementation of the custom renderer is done. It now should be able to properly handle the custom CandlestickSeries.

The last step is to register and use the customized RadCartesianChart renderer instead of the default one. This can be done in the MainActivity.cs file:

[assembly: Xamarin.Forms.ExportRenderer(typeof(Telerik.XamarinForms.Chart.RadCartesianChart), typeof(ChartFinancialSeries.Android.FinancialSeriesChartRenderer))]
  namespace ChartFinancialSeries.Android

The customization of the Android project is now finished and we can now move to the next platform.

iOS Renderer

Creating a custom iOS renderer in its essence is not different from creating an Android renderer. You need to derive a custom class from the default Telerik.XamarinForms.ChartRenderer.iOS.CartesianChartRenderer. The entry point of the customization is the same—the OnElementPropertyChanged() method:

namespace ChartFinancialSeries.iOS
       public class FinancialSeriesChartRenderer : CartesianChartRenderer
           protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
               base.OnElementPropertyChanged(sender, e);
               if (e.PropertyName.Equals("Renderer"))

In order to properly create the respective TKChartCandlestickSeries, you can refer to the Telerik UI for iOS documentation. The provided code in the article uses the TKChartFinancialDataPoint class as a data point of the candlestick series. Having this class provided in the Telerik suite means one less class to create.

You need to create one instance of this class for each candlestick defined as ItemsSource of the custom CandlestickSeries. This will allow the native charting component to visualize the financial data as expected.

public class FinancialSeriesChartRenderer : CartesianChartRenderer
       protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals("Renderer"))
                 var chart = sender as RadCartesianChart;
                 if (chart != null)
                      foreach (var sereis in chart.Series)
                           if (series as Portable.CandlestickSeries == null)
                               continue;
                               var financialDataPoints = new List();
                               foreach (BusinessDataObject dataPoint in sereis.ItemsSource)
                                   if (dataPoint != null)
                                      financialDataPoints.Add(TKChartFinancialDataPoint.DataPoint(
                                          new NSString(dataPoint.Category).ToNSObject(),
                                          new NSNumber(dataPoint.Open),
                                          new NSNumber(dataPoint.High),
                                          new NSNumber(dataPoint.Low),
                                          new NSNumber(dataPoint.Close)));
                              var CandlestickSeries = new TKChartCandlestickSeries(financialDataPoints.ToArray());
                              this.Control.AddSeries(CandlestickSeries);

The issue that you can encounter is related to the required NSObject that will be used as category. In most cases the category is defined as string and converting it to NSObject does not seem to be a straightforward operation. The easiest solution is to use the ToNSObject() extension method that is defined in the Telerik.XamarinForms.Common.iOS.IOSTypeConversionExtensions class.

After having this implemented we are ready to register and use this renderer instead of the default one. This can be done in the AppDelegate class.

[assembly: Xamarin.Forms.ExportRenderer(typeof(Telerik.XamarinForms.Chart.RadCartesianChart), typeof(ChartFinancialSeries.iOS.FinancialSeriesChartRenderer))]
  namespace ChartFinancialSeries.iOS
       [Register("AppDelegate")]
       public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
           public override bool FinishedLaunching(UIApplication app, NSDictionary options)
                 new FinancialSeriesChartRenderer();
                 global::Xamarin.Forms.Forms.Init();
                 Telerik.XamarinForms.Common.iOS.TelerikForms.Init();
                 LoadApplication(new Portable.App());
                 return base.FinishedLaunching(app, options);

WinRT Renderer

The approach with the custom WinRT renderer is the same.

namespace ChartFinancialSeries.WinRT
      public class FinancialSeriesChartRenderer : CartesianChartRenderer
          protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
              base.OnElementPropertyChanged(sender, e);
              if (e.PropertyName.Equals("Renderer"))

The difference is how the native series are created and populated. In order to do this correctly, you can refer to the WinRT documentation. Fortunately, the type of the data required by the native CandlestickSeries is the same as the one used in our portable project—a list of objects. This means no additional type to type conversion will be needed. The only missing pieces are the bindings of the separate properties.

public class FinancialSeriesChartRenderer : CartesianChartRenderer
       protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals("Renderer"))
                 var chart = sender as XF.RadCartesianChart;
                 if (chart != null)
                      foreach (var series in chart.Series)
                           if (series as Portable.CandlestickSeries == null)
                                continue;
                           var winSeries = new CandlestickSeries();
                           winSeries.ItemsSource = series.ItemsSource;
                           androidSeries.CategoryBinding = new CandlestickDataBinding("category");
                           androidSeries.OpenBinding = new CandlestickDataBinding("open");
                              androidSeries.HighBinding = new CandlestickDataBinding("high");
                           androidSeries.LowBinding = new CandlestickDataBinding("low");
                           androidSeries.CloseBinding = new CandlestickDataBinding("close");
                           this.Control.Series.Add(winSeries);

As always, the last step is to register the customized renderer. This can be done in the MainPage.xaml.cs file

[assembly: Xamarin.Forms.Platform.WinRT.ExportRenderer(typeof(Telerik.XamarinForms.Chart.RadCartesianChart), typeof(ChartFinancialSeries.WinRT.FinancialSeriesChartRenderer))]
  namespace ChartFinancialSeries.WinRT
       public sealed partial class MainPage
            public MainPage()
                 Telerik.XamarinForms.Common.WinRT.TelerikForms.Init();
                 this.InitializeComponent();
                 LoadApplication(new Portable.App());

Using the Customized Renderers

After the renderers are prepared we are ready to continue with setting up their use case. For simplicity we will use a ContentPage with just one RadCartesianChart visualizing only one CandlestickSeries with hardcoded data.

  <ContentPage xmlns="<a href="http://xamarin.com/schemas/2014/forms">http://xamarin.com/schemas/2014/forms</a>"
               xmlns:x="<a href="http://schemas.microsoft.com/winfx/2009/xaml">http://schemas.microsoft.com/winfx/2009/xaml</a>"
               xmlns:telerikChart="clr-namespace:Telerik.XamarinForms.Chart;assembly=Telerik.XamarinForms.Chart"
               xmlns:local="clr-namespace:Portable"
               x:Class="Portable.StartPage">
    <telerikChart:RadCartesianChart >
      <telerikChart:RadCartesianChart.BindingContext>
        <local:ViewModel/>
      </telerikChart:RadCartesianChart.BindingContext>
      <telerikChart:RadCartesianChart.HorizontalAxis>
        <telerikChart:CategoricalAxis PlotMode="BetweenTicks" LabelFitMode="MultiLine"/>
      </telerikChart:RadCartesianChart.HorizontalAxis>
      <telerikChart:RadCartesianChart.VerticalAxis>
        <telerikChart:NumericalAxis />
      </telerikChart:RadCartesianChart.VerticalAxis>
      <telerikChart:RadCartesianChart.Series>
        <local:CandlestickSeries ItemsSource="{Binding Items}"/>
      </telerikChart:RadCartesianChart.Series>
    </telerikChart:RadCartesianChart>
  </ContentPage>

The business object holding the data representing one point is defined like this:

namespace Portable
       public class BusinessDataObject
            public string Category { get;set;}
            public double Open { get; set; }
            public double High { get; set; }
            public double Low { get; set; }
            public double Close { get; set; }
            public BusinessDataObject(string category, double open, double close, double low, double high)
                 this.Category = category;
                 this.Open = open;
                 this.Close = close;
                 this.Low = low;
                 this.High = high;

The ViewModel holding the hardcoded business data looks like this:

namespace Portable
       public class ViewModel
            public ICollection Items { get; set; }
            public ViewModel()
                 this.Items = this.GetData();
            private ICollection GetData()
                 var result = new ObservableCollection();
                 result.Add(new BusinessDataObject("2/29/2016", 72.75, 71.4, 71.4, 73));
                 result.Add(new BusinessDataObject("2/26/2016", 71.0883, 71.52, 70.895, 71.7794));
                 result.Add(new BusinessDataObject("2/25/2016", 69.68, 70.1, 69.68, 70.1));
                 result.Add(new BusinessDataObject("2/24/2016", 66.5, 67.12, 65.99, 67.12));
                 result.Add(new BusinessDataObject("2/23/2016", 68.26, 67.2, 66.832, 68.26));
                 result.Add(new BusinessDataObject("2/22/2016", 70.391, 68.855, 68.855, 70.391));
                 result.Add(new BusinessDataObject("2/19/2016", 67.6, 67.76, 67.6, 67.76));
                 result.Add(new BusinessDataObject("2/18/2016", 67.57, 68.04, 67.57, 68.04));
                 return result;

Let's Review

To sum up, in this blog post we provided guidelines on how to use CandlestickSeries in the RadCartesianChart, even though this series is not exposed in the Telerik UI for XamarinForms suite. We also took a look at the issues that are most likely to be encountered during implementation and resolved them. At the end we created a simple use case chart which utilized the customized renderers and series.

Happy coding!

.Net developers: use Highcharts, the industry's leading interactive charting library, without writing a single line of JavaScript.

Topics:
ios ,xamarin forms ,component ,ui ,charting

Published at DZone with permission of Nikolay Diyanov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}