Porting iOS* Custom Charts and Graphs to Windows* 8 UI

Overview

This article takes a look at the various ways custom graphs and charts are used in iOS* to create rich enterprise healthcare applications and how these same custom graphics can be created in an equivalent Windows* 8 Store app experience. An example written in C# and XAML will be used to illustrate the steps needed to create a chart in a Windows 8 Store app.

Contents

  1. Charts and Graphs
  2. iOS Charts and Graphs
  3. Quartz 2D and Core Graphics
  4. Core Plot
  5. Windows 8 Store UI Charts and Graphs
  6. XAML Charts and Graphs
  7. DirectX* Charts and Graphs
  8. Conclusion
  9. About the Author

Charts and Graphs

One common element in applications is the use of charts and graphs to convey information in a compelling, visually rich medium instead of just rows and columns of numbers.  The primary content focus of this article is healthcare applications but the lessons learned here can easily be applied to other contexts.

The following screen shots are some real-life examples of how healthcare apps are making use of charts and graphs to convey information.

Figure 1 : Screen shot of application E.M.R. by K.M. Medical Software Limited

 

Figure 2 : Screen shot of application Wand* by Allscripts

Figure 3 : Screen shot of application AirStrip - Patient Monitoring by AirStrip Technologies, LLC

These are just a small set of the different charts and graphs found in applications on the iOS platform. The charts sometimes take center stage within an app and at other times they are used in a supporting role. In either case, the charts and graphs provide a rich visual experience for looking at data and are important in various applications.

iOS Charts and Graphs

Charts and graphs on iOS can be created in a variety of ways. Using a library is one easy way to quickly integrate complex graphics into your application. For total control of the look and feel, using the drawing primitives of the platform is perhaps the way to go.

Quartz 2D and Core Graphics

The Cocoa* framework provides a powerful and lightweight 2D drawing engine named Quartz 2D. This API exposes all the drawing mechanisms you need to draw custom charts and graphs from scratch. In addition to Quartz 2D, the Core Animation framework provides the ability to add animations or handle real-time data charts with features that improve performance.

A closer look at programming techniques used to build graphs and charts this way will not be discussed, but the following examples and articles are great references for getting started down this path:

Core Plot

One of the quickest ways to create custom graphs and charts is to use a library. Fortunately, when developing applications for iOS, a mature framework is available that allows you to easily create rich charts and graphs. This library is Core Plot and is an open source project that works on both iOS and Mac OS X platforms. The following section goes into detail about how you might incorporate Core Plot SDK into your application and also discusses the type of customizations available.

The Core Plot framework is found at http://code.google.com/p/core-plot/ and is available for download under the New BSD License.

The first step in the configuration is to integrate Core Plot into your project. This is not an extremely simple task and may take a few trials to set up your environment correctly. When finished, depending on how you incorporate the project, the Project Navigator should show a similar nested CorePlot-CocoaTouch project as in Figure 4.  In addition, the QuartzCore.framework needs to be manually added to your project.

Figure 4: Project Navigator view of a sample project from within Xcode*

Creating a graph using Core Plot is a straightforward exercise and with a minimum amount of effort a functional and nice looking graph can be created. Figure 5 is a simple example of a line graph with two sets of data, two axes with labels, and an easy to read legend.

Figure 5 : Screen shot of sample blood pressure chart created with Core Plot

The Objective-C* code for this graph is not very complex, and a few of the highlights are shown below.

First, initialize the graph and ready the current view to display the graph.

graph = [[CPTXYGraph alloc] initWithFrame: self.view.bounds];
CPTGraphHostingView *hostView = (CPTGraphHostingView *)self.view;
hostView.hostedGraph = graph;

Sample Code 1**

Set up a background color and padding from the edge of the view.

hostView.backgroundColor = [UIColor whiteColor];
graph.paddingLeft = 60.0;
graph.paddingTop = 20.0;

Sample Code 2**

Next, set up this graph to be a XY scatter plot and configure the axes. The example below shows the setup of the Y-axis. The last line configures the graph to allow touch-based pan and zoom within the graph.

CPTXYPlotSpace *plot = (CPTXYPlotSpace *)graph.defaultPlotSpace;
plot.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0)
length:CPTDecimalFromFloat(300.0)];
plot.allowsUserInteraction = YES;

Sample Code 3**

Next, the axis and tick style are formatted with a custom width black line.

CPTMutableLineStyle *tickStyle = [CPTMutableLineStyle lineStyle];
tickStyle.lineColor = [CPTColor blackColor];
tickStyle.lineWidth = 1.0f;

CPTXYAxisSet *axes = (CPTXYAxisSet *)graph.axisSet;
CPTXYAxis *y = axes.yAxis;
y.orthogonalCoordinateDecimal = CPTDecimalFromFloat(0);
y.majorIntervalLength = CPTDecimalFromString(@"20");
y.minorTicksPerInterval = 5;
y.majorTickLineStyle = tickStyle;
y.majorTickLength = 8;
y.minorTickLineStyle = tickStyle;

Sample Code 4**

Format and add the plot line. Notice that the data source is set to our self in this case.

CPTScatterPlot *systolicSourceLinePlot = [[CPTScatterPlot alloc] init];
systolicSourceLinePlot.identifier = @"Systolic";

CPTMutableLineStyle* lineStyle = [systolicSourceLinePlot.dataLineStyle
mutableCopy];
lineStyle.lineWidth = 3.f;
lineStyle.lineColor = [CPTColor greenColor];
systolicSourceLinePlot.dataLineStyle = lineStyle;
systolicSourceLinePlot.dataSource = self;

[graph addPlot:systolicSourceLinePlot];

Sample Code 5**

For a nice visual touch, a graph legend can be added with a few lines of code.

CPTLegend* myLegend = [CPTLegend legendWithGraph:graph];
myLegend.textStyle = x.titleTextStyle;
myLegend.fill = [CPTFill fillWithColor:[CPTColor lightGrayColor]];
myLegend.borderLineStyle = x.axisLineStyle;
myLegend.cornerRadius = 5.0f;
myLegend.swatchSize = CGSizeMake(50.0,50.0);
myLegend.anchorPoint = CGPointMake(0.0, 0.0);
graph.legend = myLegend;

Sample Code 6**

Last, we add some data to an NSArray, and then within the UIViewController methods the data points are provided to the framework.


// populate array with data
NSArray *plotSystolicData;

-(NSNumber *)numberForPlot:(CPTPlot *)thePlot
              field:(NSUInteger)theField
              recordIndex:(NSUInteger)idx
{    
   if ( plot.identifier == @"Systolic" )
   {
     NSDecimalNumber *value = [plotSystolicData objectAtIndex:idx];
     return value;
   }
}

Sample Code 7**

Although this might look like quite a bit of code, most of it just defines the look and feel of the graph, and a large amount of customization is possible. In addition to customizing, a variety of plot styles are available for use (e.g., CPTBarPlot.)

Some additional resources to assist you in integrating and using the Core Plot framework in your project:

Windows 8 Store UI Charts and Graphs

Now that we have looked at possible solutions for iOS, the question is what does it take to get an equivalent graph running and looking as nice in Windows 8 Store UI?

Some concepts will be unfamiliar to an iOS developer when thinking about porting or designing a Windows 8 Store app. Some of these items are choosing one or multiple programming languages, familiarizing oneself with new UI paradigms such as App Bars and Snap View, and learning to use new development tools and a completely new IDE.  

Here are some articles that go into greater details about the basics of Windows 8 Store apps from an iOS developer’s perspective along with some more general learning resources.

 

There are obviously other ways to accomplish creating graphs and charts, and some other methods will be mentioned in the sections below. Figure 6 shows a screen shot of a similar blood pressure graph created using WinRT XAML Toolkit.

Figure 6: Screen shot of sample blood pressure chart created with WinRT XAML Toolkit

XAML Charts and Graphs

Free or Open Source options for graphing libraries that support the Windows 8 Store app platform are not as robust and plentiful as with iOS, but one option stands out as having all the features needed to create compelling graphs.  WinRT XAML Toolkit available under the MIT License provides a variety of customizable chart controls, including line, pie, scatter, and bar charts.

Let’s take a look at the C# code and XAML source used to create this graph. As in iOS, you first need to set up the project to reference the graphing library.

Figure 7: Solution Explorer view of sample graph project in Visual Studio* 2012

The WinRT XAML Toolkit solution has several projects, but only two projects are needed to build the graph and are highlighted in Figure 7. This figure also shows the outcome of configuring the references from the main user application project, PRApp in this case, located under the References folder.

The approach taken to implement the graph was to use a combination of both C# code and XAML to configure the graph. The example source below is obviously not the only way to code the graph.

The first thing needed is a graph component defined in XAML. To simplify things, the page I used inherits from LayoutAwarePage. Within the definition tag, the proper WinRTXamlToolkit namespace is pulled in and the <charting:Chart> tag starts the definition of the graph.

<common:LayoutAwarePage
   x:Class="PRApp.VitalsGraph"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="using:PRApp"
   xmlns:common="using:PRApp.Common"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
   mc:Ignorable="d">
…
<charting:Chart
    x:Name="MyLineChart"
    Margin="301,70,299,70"
    Padding="10" BorderThickness="5" BorderBrush="#FF233083" 
    Background="#FF406295" Title="Blood Pressure">
   <charting:LineSeries 
DependentValueBinding="{Binding BloodPressureSystolic}" 
       IndependentValueBinding="{Binding CreateDayTime}" 
       IsSelectionEnabled="True" 
       Title="Systolic"/>
   <charting:LineSeries 
DependentValueBinding="{Binding BloodPressureDiastolic}" 
IndependentValueBinding="{Binding CreateDayTime}" 
       IsSelectionEnabled="True" 
       Title="Diastolic"/>
</charting:Chart>

Sample Code 8**

 

This defines an object with the name MyLineChart that can easily be used from within C# code. The interesting XAML code above defines two LineSeries to be used to plot the data. In this case, the data will use a data binding to a collection object that contains the named elements BloodPressureSystolic, BloodPressureDiastolic, and CreateDayTime.  

Within C# code, the first thing to do is to set up the connection between the data and the graph.  Using data binding and the VitalsViewModel, this can be accomplished with a minimum amount of code as seen below:


// retrieve the view model that will supply the data for our graph
VitalsViewModel vvm = new VitalsViewModel();
ObservableCollection<VitalsViewModel> vitals = vvm.GetVitals(((PatientsViewModel)navigationParameter).Id);

// assign view model as the source of the data
foreach (LineSeries series in MyLineChart.Series)
{
series.ItemsSource = vitals;
}

Sample Code 9**

Some of the hidden magic is contained within the VitalsViewModel class. This handy method takes a parameter, an ID to a Patient DB record in this case, and returns an enumerable collection of information about the patient’s vitals.  This collection is then assigned directly to the ItemsSource property of the line series. The XAML code shown previously then defines which VitalsViewModel property is used as the source for the data points. In this case we will use CreateDayTime, BloodPressureSystolic, and BloodPressureDiastolic.

This next section of code sets up the look and feel of the axes.

// setup the y-axis
LinearAxis yAxis = new LinearAxis();
yAxis.ShowGridLines = true;
yAxis.Orientation = AxisOrientation.Y;
yAxis.Minimum = 0;
yAxis.Maximum = 225;
yAxis.Interval = 25;
yAxis.MajorTickMarkStyle = (Style)Resources["MajorYTickStyle"];

// setup the x-axis
DateTimeAxis xAxis = new DateTimeAxis();
xAxis.Orientation = AxisOrientation.X;
xAxis.IntervalType = DateTimeIntervalType.Months;
xAxis.Interval = 2;
xAxis.MajorTickMarkStyle = (Style)Resources["MajorXTickStyle"];
xAxis.AxisLabelStyle = (Style)Resources["ShortDateTimeAxisLabelStyle"];

Sample Code 10**

The tick marks and date axis label have an additional level of customization that is accomplished with XAML and is pulled in with the code by specifying a resource name (e.g., “ShortDateTimeAxisLabelStyle”). Here is what the customization looks like in the XAML code:

<Style TargetType="Line" x:Key="MajorXTickStyle">
    <Setter
                Property="Stroke"
                Value="Gray" />
    <Setter
                Property="Y2"
                Value="6" />
    <Setter 
            Property="StrokeThickness"
            Value="4"
        />
</Style>

<Style x:Key="ShortDateTimeAxisLabelStyle" 
        TargetType="charting:DateTimeAxisLabel">
    <Setter
            Property="MonthsIntervalStringFormat"
            Value="{}{0:M/yy}" />
    <Setter
            Property="RenderTransform">
        <Setter.Value>
            <RotateTransform Angle="30"/>
        </Setter.Value>
    </Setter>
</Style>

Sample Code 11**

The style modifications for the tick mark increase the thickness and length of the line slightly and also update the color of the mark.  The modifications to the date axis are a little more interesting. A rotation transform is applied to the label values with the RenderTransform element. In addition, a shortened format for the date is created by overriding the MonthsIntervalStringFormat element.  

The code below continues some style optimizations and then applies the axis formatting to each series in the graph.

// increase the font of the axis and legend labels
MyLineChart.FontSize = 24;

// use a custom style for the title of the chart
MyLineChart.TitleStyle = (Style)Resources["TitleStyle"];

// apply the axis formatting to each series in the chart
foreach (LineSeries series in MyLineChart.Series)
{
    series.IndependentAxis = xAxis;
    series.DependentRangeAxis = yAxis;
}

Sample Code 12**

The chart font size only seems to update the axis and legend labels so an additional custom style for the Title is needed.

<Style TargetType="datavisual:Title" x:Key="TitleStyle">
    <Setter
                Property="FontSize"
                Value="34" />
    <Setter
                Property="HorizontalAlignment"
                Value="Center" />
    <Setter
                Property="Margin"
                Value="0,10,0,10" />
</Style>

Sample Code 13**

That’s it! The graph is ready to go with a level of customization that is easily equal to what was accomplished in the iOS example.

DirectX* Charts and Graphs

Other ways to create graphical charts in Windows 8 Store apps include using the powerful Direct2D engine found in DirectX. Although DirectX is a C++ only component, there are ways to combine the ease of XAML and C# with the power of Direct2D graphics.

The primary way of using DirectX is to create a C++ Windows 8 Store app. XAML can still be used, and one of the template projects in Visual Studio* 2012 provides a great starting point for an application built with this model (see Figure 8).  The preview image at the bottom right gives you an idea of what to expect. One string is drawn with XAML, the other with DirectX.

Figure 8: Screenshot Visual Studio* 2012

Although all the functionality for building a Windows 8 Store app is available with the previous method, a more interesting way of integrating Direct2D graphics is to use a hybrid approach where you combine a C# Store app with C++ code from a DirectX Windows Runtime component. First, a  C++ component based on SurfaceImageSource is created to perform the DirectX work. Next, you simply set the ImageSource of a XAML component to this SurfaceImageSource object and you now have DirectX graphics displayed in your C# application.

For a more in-depth look at the strategies discussed, including details on using the additional method, VirtualSurfaceImageSource, check out the article Combining XAML and DirectX. To see this method in action, the Microsoft provided sample XAML SurfaceImageSource DirectX interop sample is an excellent place to start.

Conclusion

Hopefully with some of the methods outlined here, creating a visually rich Windows 8 Store app is a little easier. The variety of programming languages, UI drawing options, and kits available to you, all contribute in making the port of an existing iOS as easy as possible.

About the Author

Nathan Totura is an application engineer in Intel's Software and Services Group. Currently working on the Intel® Atom™ processor-enabling team, he helps connect software developers with Intel technology and resources. Primarily these technologies include tablets and handsets on the Android*, Windows 8, and iOS platforms. 

 

**This sample source code is released under the Intel OBL Sample Source Code License (MS-LPL Compatible) , Microsoft Limited Public License and Visual Studio 2012 License. Copyright© 2012 Intel Corporation. All rights reserved.

Per informazioni più dettagliate sulle ottimizzazioni basate su compilatore, vedere il nostro Avviso sull'ottimizzazione.