July 17, 2014

Using Custom Controls in Xamarin.Forms on Windows Phone

By

Xamarin.Forms lets developers create native user interfaces on iOS, Android and Windows Phone from a single, shared C# codebase. Since the UI is rendered using the native controls of the target platform, it gives you great flexibility in customizing the controls separately on each platform. Each control is rendered differently on each platform using a Renderer class, which in turn creates a native control, arranges it on the screen and adds the behavior specified in the shared code.

Previously, we showed you how to build custom renderers in Xamarin.Forms on iOS and Android platforms to extend custom built controls. In case you missed it, its all here:

In practice,  you will use the same techniques to create custom renderers on Windows Phone as well.

WP-CustomControls-Xamarin.Forms

Windows Phone Custom Controls

.NET third party vendors provide you with wide range of top quality reusable UI controls on the Windows Phone platform. Sometimes it is easier to buy and use them than to build something on your own. Especially, the data visualization controls such as charts that transform your tabular data into something beautiful on the Windows Phone screen.

In this blog post, I will take you through steps involved in integrating a Infragistics XamDataChart control for Windows Phone in Xamarin.Forms. Xamarin.Forms-CustomControl-Charts

There are two main parts to implementing a custom control with a renderer –

  1. Create your own custom Xamarin.Forms control with bindable properties in Shared Project so that Xamarin.Forms API can refer them.
  2. Create a renderer in Windows Phone platform that will be used to display the Infragistics XamDataChart control and subscribe to property changed notifications

CustomChartView Control

In my shared project, I’m going to create a new control called CustomChartView that will be used in my Xamarin.Forms page. CustomChartView must inherit from Xamarin.Forms.View.

public class CustomChartView : View
{
  public static readonly BindableProperty ItemSourceProperty =
    BindableProperty.Create<CustomChartView, StockMarketDataSample>(p =>
    p.ItemSource, new StockMarketDataSample());
  public StockMarketDataSample ItemSource
  {
    get { return (StockMarketDataSample)GetValue(ItemSourceProperty); }
    set { SetValue(ItemSourceProperty, value); }
  }
  // Additional bindable properties
}

Notice the BindableProperty called ItemSourceProperty – this property is my DataModel which is of type StockMarketDataSample that has some necessary logic to simulate live data. In a real scenario, this will be replaced with the data that comes from a web service or something similar. In the code, you will see two more properties PriceDisplayType and ShowSpline. These are the properties that will help us interact with the chart for e.g. for changing the Price Type (CandleStick or OHLC) or Show/Hide a SplineAreaSeries from Xamarin.Forms controls.

CustomChartView Renderer

Now, with my Xamarin.Forms control in place I can write some Windows Phone platform specific code. I will implement a CustomChartViewRenderer class that inherits from a ViewRenderer and layout the XamDataChart on it.

public class CustomChartViewRenderer : ViewRenderer<CustomChartView, XamDataChart>
{
  XamDataChart DataChart;
  public CustomChartViewRenderer()
  {
    DataChart = new XamDataChart();
    //..code
  }
  //.. code
}

Since this renderer on Windows Phone inherits from View, which means Xamarin.Forms will handle all of the size calculations and will have the normal properties of a standard Windows Phone View. Add the XamDataChart to the project and update the references accordingly. Additionally, you will need to add more code to set your XAxis, YAxis and the FinancialPriceSeries to the XamDataChart. That code has been omitted from this post for brevity.

Now, I will set my renderer to display the XamDataChart control when the CustomChartView is added to the page layout. This is done by overriding the OnElmentChanged method and calling the SetNativeControl method in it.

protected override void OnElementChanged(ElementChangedEventArgs<CustomChartView> e)
{
  base.OnElementChanged(e);
  if (e.OldElement != null || this.Element == null)
    return;
  UpdateChart();
  SetNativeControl(DataChart);
}

UpdateChart() method is a private method that sets the DataContext of the XamDataChart control to the Xamarin.Forms CustomChart control’s ItemSource property.

private void UpdateChart()
{
  DataChart.DataContext = this.Element.ItemSource;
  DateXAxis.ItemsSource = this.Element.ItemSource;
  series.ItemsSource = this.Element.ItemSource;
  //.. code
}

Export Renderer Attribute

[assembly: ExportRenderer((typeof(CustomChartView)), typeof(CustomChart.WinPhone.ViewRenderers.CustomChartViewRenderer))]
namespace CustomChart.WinPhone.ViewRenderers
{
  public class CustomChartViewRenderer : ViewRenderer<CustomChartView, XamDataChart>
  {
  }
}

To get the control to show up in the actual view, I need to set an attribute ExportRenderer to the CustomChartViewRenderer class.

Wiring up the UI in XAML

Finally, I will add our custom control to the Page.  There are two approaches to create user interfaces in Xamarin.Forms. The first one is to create UI views entirely with source code using the Xamarin.Forms API. The other option available is to use Extensible Application Markup Language (XAML) which is the approach I’ve taken to build this demo.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
		     x:Class="CustomChart.HomePage"
             xmlns:custom="clr-namespace:CustomChart.CustomControls;assembly=CustomChart.WinPhone">
    <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
        <Grid.RowDefinitions>
            <RowDefinition Height="80"/>
            <RowDefinition Height="80"/>
            <RowDefinition Height="*" />
            <RowDefinition Height="1" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="1"/>
        </Grid.ColumnDefinitions>
        <Grid.Padding>20</Grid.Padding>
        <StackLayout Orientation="Horizontal" Grid.Row="0" Grid.Column="0" HorizontalOptions="Center">
            <Button Text="Start" x:Name="StartButton" />
            <Button Text="Stop"  x:Name="StopButton"  />
            <Label Text="Spline:" VerticalOptions="Center"/>
            <Switch x:Name="ShowSplineSwitch"/>
        </StackLayout>
        <Picker x:Name="chartPicker" Title="Chart Type" HorizontalOptions="FillAndExpand" VerticalOptions="Center" Grid.Row="1"/>
        <custom:CustomChartView x:Name="chart" Grid.Row="2" />
    </Grid>
</ContentPage>

This page contains a Start Button – that starts a live feed, a Stop Button – thats stops the live feed, a Switch – that lets you show/hide a SplineAreaSeries in the Chart, and a Picker – that let’s you choose the Price Display Type (CandleStick or OHLC).

Xamarin.Forms-CustomControl-Charts-Spline

Handling Property Changes 

In the CustomChartControl of the shared project, along with the ItemSourceProperty, I have exposed the PriceDisplayTypeProperty and ShowSplineProperty as well. We can now easily set these property to interact with the XamDataChart. To make this work, we need to listen to the property changed event and set appropriate property of the XamDataChart. We will override the OnElementPropertyChanged method in the CustomChartViewRenderer class to do so.

protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
  base.OnElementPropertyChanged(sender, e);
  if (Control == null || Element == null)
    return;
  if (e.PropertyName == CustomChart.CustomControls.CustomChartView.PriceDisplayTypeProperty.PropertyName)
    series.DisplayType = Element.PriceDisplayType.ToIGPriceType();
  if (e.PropertyName == CustomChart.CustomControls.CustomChartView.ShowSplineProperty.PropertyName)
    ShowHideSpline();
}
private void ShowHideSpline()
{
  if (this.Element.ShowSpline)
    DataChart.Series.Add(splineSeries);
  else if (DataChart.Series.Contains(splineSeries))
    DataChart.Series.Remove(splineSeries);
}

Bringing it all together

That’s it! Now we can set these properties in the appropriate event handlers of the controls. Also, setting the ItemSource property to a StockMarketDataSample will populate the chart with the values.

public HomePage ()
{
  InitializeComponent ();
  chart.ItemSource = _data = new StockMarketDataSample();
}
void ShowSplineSwitch_Toggled(object sender, Xamarin.Forms.ToggledEventArgs e)
{
  chart.ShowSpline = e.Value;
}
void chartPicker_SelectedIndexChanged(object sender, EventArgs e)
{
  PriceDisplayType priceDisplayType;
  if (Enum.TryParse<PriceDisplayType>(chartPicker.Items[chartPicker.SelectedIndex], out priceDisplayType))
    chart.PriceDisplayType = priceDisplayType;
}

To simulate a Live Data, StockMarketServiceClient is used which has a timer that ticks every few milliseconds and raises an event with new value. Subscribing to that event and updating the ItemSource will change the visualization in the chart.

Xamarin.Forms-CustomControl-WPTo learn more about customizing your Xamarin.Forms controls and applications be sure to read our documentation.

Infragistics controls were used as an example in this post to show you how well Xamarin.Forms can integrate with third party controls. You can use any third party controls or custom controls that you have built with Xamarin.Forms and the procedure to get that working remains the same. 

Source Code

You can download the full source code for this project from my GitHub. Make sure you download and install Infragistics libraries before compiling.

Discuss this blog post in the Xamarin Forums

TwitterFacebookGoogle+LinkedInEmail