TeeChart for NET with MVC

TeeChart’s MVC & ASP.NET Charting options

For MVC, TeeChart for .NET can either render as a static image format such as PNG or JPEG or as a live javascript HTML5 Canvas object. See this page for some examples:
TeeChart MVC examples page. Some of the techniques used by TeeChart’s native ASP.NET Webform chart have been enabled for MVC. For further examples from ASP.NET see the full demo here: http://www.steema.net/TeeChartForNET/index.aspx.

This article concentrates on TeeChart’s implementation for MVC.

TeeChart for MVC

To create/call a Chart, from the view, we place have several options:



Render formats


Chart as an HTML5 canvas

The TeeChart may be rendered as an HTML5 Canvas coded by javascript. Steema provides a public Javascript Library that may be used at render time from NET projects to the webpage. TeeChart for .NET for the web extends capabilities across two possible programming environments, native .NET and Javascript. You can create a managed chart under the Visual Studio framework, within the controller and TeeChart will create the required Javascript to render the Chart (for MVC or ASP.NET) – plus – you can enhance the Javascript chart in virtually any way you desire by using javascript at the view or by generating it in the controller to render at the view. It’s your choice; we hope you like the flexibility it offers.

Chart as a static Image

Charts may be rendered as a static image, typical formats are PNG and JPEG.

Chart as a static Ajax enabled Image

Although this is a mode originally implemented for TeeChart’s Webform (ASP.NET) chart only, some of the techniques used may be extended for use in MVC. MVC can use the tools created for ASP.NET to generate clientside javascript for hotspot maps.


Call formats


Chart as an iframed live Javascript/HTML5 Chart

Example 1.
The first example uses an ActionResult to return a Chart. For these examples we’ve put several Chart routines in a Controller, here called “TeeChartMVCController”

The ActionResult does this:
public ActionResult GetHTML5Chart(int? number)
{
Steema.TeeChart.TChart wChart3 = new Steema.TeeChart.TChart();

if (number==0) //react to input variables
wChart3.Series.Add(new Steema.TeeChart.Styles.Pie());
else
wChart3.Series.Add(new Steema.TeeChart.Styles.Line());

wChart3.Series[0].FillSampleValues(); //data fill routines

continued...

We create the Chart according to the parameters received; in this case simply to choose between the type of Chart required. This is all native .NET code. You can build the Chart and render it to the page without the need to add any Javascript. The Chart export generates the required Javascript view code and links to TeeChart’s Javascript source. For this example we can and will add some Javascript though, to enhance features of the Chart on the page.

... continued

//optional - add in some custom javascript
//setting a clientside visualisation theme
string[] customCode = new string[] {" chart1.applyTheme(\"minimal\");" };
wChart3.Export.Image.JScript.CustomCode = customCode;

//setup the Chart export stream
tempStream2 = new System.IO.MemoryStream(); //create stream for transport
wChart3.Export.Image.JScript.Width = 800; //size the Chart
wChart3.Export.Image.JScript.Height = 300;
//build the Chart
wChart3.Export.Image.JScript.Save(tempStream2); //write to stream

tempStream2.Position = 0;
System.IO.StreamReader sr = new System.IO.StreamReader(tempStream2);

return Content(sr.ReadToEnd()); //return the Chart to the caller
}

This results in the following chart, well, nearly; the chart also includes a Tooltip, the description of which is covered in the next section:


Note. Examples on this page have been generated on a non-SSL server and cannot be viewed as frames here under SSL. If you can’t see the charts re-load page for non SSL (to view non-SSL demo charts)



ie. http://www.steema.net/TeeChartMVC/TeeChartMVC/GetHTML5Chart?number=2.


As an iframe this would be called in the following way from the page:

<iframe src=”http://www.steema.net/TeeChartMVC/TeeChartMVC/GetHTML5Chart?number=2″ width=”100%” height=”350″>

Example 2.
The second example shows how view code may be extended to use TeeChart’s Javascript Tools. This example, interpolation, brings together several elements to show how you can use cursor mobility with the chart to feedback information about its contents.

Note. Examples on this page have been generated on a non-SSL server and cannot be viewed as frames here under SSL. If you can’t see the charts re-load page for non SSL (to view non-SSL demo charts)

This is the controller code:
public ActionResult GetInterpolatedChart(int? type)
{

lock (renderLock)
{
Steema.TeeChart.TChart wChart3 = new Steema.TeeChart.TChart();

// wChart3.Export.Image.JScript.DoFullPage = false;

List<string> customCode = new List<string>();
List<string> externalCode = new List<string>();
List<string> externalHTML = new List<string>();

string[] externalHTMLS = new string[] {
"<font face=\"verdana\" size=1><span id=\"xpos\"></span><br/>",
"<span id=\"yposBar\"></span></font><br>"
};

string[] externalCodeS = new string[] {
" ",
" var posXLabel, posYLabel;",
" ",
" function interpolateLineSeries(s, xval) {",
" var yValues=s.data.values;",
" var len=yValues.length;",
" var xValues=[];",
"",
" if (s.data.x)",
" xValues=s.data.x;",
" else {",
" for (i=0;i<len;i++)",
" xValues[i]=i;",
" }",
"",
" var index;",
" for (index=0;index<len;index++) {",
" if (xValues[index]>xval)",
" break;",
" }",
"",
" if (index<1)",
" index=1;",
" else",
" if (index>=len)",
" index=len-1;",
"",
" var dx=xValues[index] - xValues[index-1];",
" var dy=yValues[index] - yValues[index-1];",
"",
" if (dx!=0)",
" return dy*(xval - xValues[index-1])/dx + yValues[index-1];",
" else",
" return 0;",
"}",

};

string[] customCodeS = new string[] {
" ",
" ",

" var ypos=document.getElementById('yposBar'); ",
" for (i=0;i<3;i++) {",
" if (i>0) {",
" var br = document.createElement('br');",
" ypos.appendChild(br);",
" }",
" posYLabel=document.createElement('span');",
" posYLabel.id='ypos'+i;",
" ypos.appendChild(posYLabel);",
" }",

" ",
"//tooltip",
"tip=new Tee.ToolTip(chart1);",
"tip.render=\"dom\";",
"tip.domStyle = \"padding-left:8px; padding-right:8px; padding-top:2px; padding-bottom:4px; margin-left:5px; margin-top:0px; \";",
"tip.domStyle = tip.domStyle + \"background-color:#FCFCFC; border-radius:4px 4px; color:#FFF; \";",
"tip.domStyle = tip.domStyle + \"border-style:solid;border-color:#A3A3AF;border-width:1; z-index:1000;\";",
"chart1.tools.add(tip);",
"tip.onhide=function() { scaling=0; poindex=-1; }",

"tip.ongettext=function( tool, text, series, index) { ",
" var s = '<font face=\"verdana\" color=\"black\" size=\"1\"><strong>'+ series.title+'</strong></font>';",
" s = s + '<br/><font face=\"verdana\" color=\"darkblue\" size=\"1\">Series point: <strong>'+ index.toFixed(0)+'</strong></font>';",
" s = s +'<br/><font face=\"verdana\" color=\"red\" size=\"1\">Value: '+series.data.values[index].toFixed(2)+'</font>'; ",
" return s;",
"}",
" ",

" var t=new Tee.CursorTool(chart1); ",
" t.direction='vertical';",
" t.format.stroke.size=1;",
" t.format.stroke.fill='gray';",
" chart1.tools.add(t);",
" var xValue;",
" ",
" posXLabel=document.getElementById('xpos'); ",
" t.onchange=function(p) {",
" xValue=chart1.axes.bottom.fromPos(p.x);",
" posXLabel.textContent='X Value = '+xValue.toFixed(2);",
" for (var i=0; i<chart1.series.items.length; i++){",
" posYLabel=document.getElementById('ypos'+i);",
" posYLabel.textContent=chart1.series.items[i].title+' Y Value = '+interpolateLineSeries(chart1.series.items[i],xValue).toFixed(2);",
" }",
"",
" // changeTheme(chart1, 'minimal');",
" chart1.draw();",
" };",
"",

"chart1.ondraw=function() {",
" var xs=this.axes.bottom.calc(xValue);",
"",
" for (var i=0;i<this.series.items.length;i++) {",
" var ys=this.axes.left.calc(interpolateLineSeries(this.series.items[i],xValue));",
" var f=new Tee.Format(this);",
" f.fill=this.series.items[i].format.fill;",
" if ((!isNaN(xs)) && (!isNaN(ys)))",
" f.ellipse(xs,ys,8,8);",
" }",
" }",
" "

//"chart1.tools.items[0].setRender('layer'); chart1.draw();"

};
//********* end client code *********************

customCode.AddRange(customCodeS);

customCode.Add(" chart1.applyTheme(\"minimal\");");

Steema.TeeChart.Styles.Line line1 = new Steema.TeeChart.Styles.Line();
Steema.TeeChart.Styles.Line line2 = new Steema.TeeChart.Styles.Line();
Steema.TeeChart.Styles.Points p3 = new Steema.TeeChart.Styles.Points();

p3.Pointer.Gradient.Visible = false;

line1.Pointer.Visible = true;
line2.Pointer.Visible = true;

p3.Pointer.Style = Steema.TeeChart.Styles.PointerStyles.Circle;

wChart3.Series.Add(line1);
wChart3.Series.Add(line2);
wChart3.Series.Add(p3);

wChart3.Legend.Visible = false;

for (int i = 0; i < 3; i++)
{
wChart3.Series[i].FillSampleValues(50);
}

wChart3.Export.Image.JScript.CustomCode = customCode.ToArray();
wChart3.Export.Image.JScript.ExternalCode = externalCodeS.ToArray();
wChart3.Export.Image.JScript.BodyHTML = externalHTMLS.ToArray();

tempStream2 = new System.IO.MemoryStream();
wChart3.Export.Image.JScript.Width = 950;
wChart3.Export.Image.JScript.Height = 300;
wChart3.Export.Image.JScript.Save(tempStream2);

tempStream2.Position = 0;

System.IO.StreamReader sr = new System.IO.StreamReader(tempStream2);

return Content(sr.ReadToEnd());
}
}

This is the view code:

<div class="col-md-5">
<iframe id="fr0" src="/TeeChartMVC/GetInterpolatedChart?number=2" style="border:none; width:970px; height:450px"></iframe>
</div>

This could be converted to an inline call. See the next section for details.



Chart as an inline live Javascript/HTML5 Chart

TeeChart’s Javascript Export class includes a property called DoFullPage. If set to “false” TeeChart hands you control to decide what elements to output to the page. TeeChart will create all of the code lines to generate the Chart but you’ll need to add the Canvas tag and the call to the Chart Draw method. This can of course be automated with your choice of naming convention. We’ve added an example to the TeeChartMVCController.


Note. Examples on this page have been generated on a non-SSL server and cannot be viewed as frames here under SSL. If you can’t see the charts re-load page for non SSL (to view non-SSL demo charts)

public ActionResult CreateInlineChart(int flag, int width, int height)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();

StringBuilder builder = new StringBuilder();

Steema.TeeChart.TChart wChart3 = new Steema.TeeChart.TChart();
Steema.TeeChart.Styles.Bubble bubble = new Steema.TeeChart.Styles.Bubble();
wChart3.Series.Add(bubble);
wChart3.Legend.Visible = false;
wChart3.Header.Visible = false;
bubble.FillSampleValues(50);

Steema.TeeChart.Export.JavascriptFormat format = (Steema.TeeChart.Export.JavascriptFormat)(wChart3.Export.Image.JScript);

format.DoFullPage = false; //embedding code without headers

//name the related page elements
string ClientID = "w3";
format.CanvasName = ClientID + "img";
format.ChartName = ClientID + "_chart";

format.Width = width;
format.Height = height;

string[] customCodeS = new string[] {
" ",
" //tooltip",
" tip=new Tee.ToolTip("+format.ChartName+");",
" tip.render=\"dom\";",
" tip.domStyle = \"padding-left:8px; padding-right:8px; padding-top:2px; padding-bottom:4px; margin-left:5px; margin-top:0px; \";",
" tip.domStyle = tip.domStyle + \"background-color:#FCFCFC; border-radius:4px 4px; color:#FFF; \";",
" tip.domStyle = tip.domStyle + \"border-style:solid;border-color:#A3A3AF;border-width:1; z-index:1000;\";",
" "+format.ChartName+".tools.add(tip);",
" tip.onhide=function() { scaling=0; poindex=-1; }",

" tip.ongettext=function( tool, text, series, index) { ",
" var s = '<font face="verdana" color="black" size="1"><strong>'+ series.title+'</strong></font>';",
" s = s + '<br /><font face="verdana" color="darkblue" size="1">Series point: <strong>'+ index.toFixed(0)+'</strong></font>';",
" s = s +'<br /><font face="verdana" color="red" size="1">Value: '+series.data.values[index].toFixed(2)+'</font>'; ",
" return s;",
" }",
" ",
" "+format.ChartName+".applyTheme(\"minimal\");",
" ",
};

format.CustomCode = customCodeS.ToArray();

System.IO.MemoryStream chartImg = new System.IO.MemoryStream();

format.Save(chartImg);

chartImg.Position = 0;
var sr = new System.IO.StreamReader(chartImg);
var javaScr = sr.ReadToEnd();

//add the source paths
builder.AppendLine("");
builder.AppendLine("");

//add our code block and load caller
builder.AppendLine("");
builder.AppendLine(" window.onload = function(){ draww3() };");
builder.AppendLine("var " + format.ChartName + ";\r\nfunction draw" + ClientID.ToString() + "() {\r\n");

//add-in chart code
builder.AppendLine(javaScr);

builder.AppendLine("}\r\n");
builder.AppendLine("\r\n");

string styleStr = ""; //optional

//add the HTML5 canvas
builder.AppendLine(" 0 ? (styleStr + "\" ") : " style=\"\"")
+ " width=\"" + format.Width + "\" height=\"" + format.Height + "\">");

builder.AppendLine("\r\nThis browser does not seem to support HTML5 Canvas.");
builder.AppendLine("\r\n\r\n");

return Content(builder.ToString());
}

The above chart generation routine is called from the MVC View in the following way:
@{ Html.RenderAction(“CreateInlineChart”, “TeeChartMVC”, new { flag = 2, width = 970, height = 400 }); }


Chart as an enhanced image

All TeeChart’s Chart types may be rendered as a static image in MVC. Some Ajax ‘enabling’ techniques may be borrowed from tools developed for TeeChart’s ASP.NET (Webform) version chart. In this example we’ll use the hotspot tool to generate a mouseover hint HTML map for the Chart. The technique may extended to offer inline or click-back drill down.

eg.

Note. Examples on this page have been generated on a non-SSL server and cannot be viewed as frames here under SSL. If you can’t see the charts re-load page for non SSL (to view non-SSL demo charts)


<img usemap="#MAPWebChart1" id="Chartimg3" src="/TeeChartMVC/GetStaticBarChart?w=950&h=350&image=true" style="border:none; width:970px; height:350px">
@{ Html.RenderAction("GetStaticBarChart", "TeeChartMVC", new { w = 950, h = 350, image = false }); }


The RenderAction calls the chart generation function generating the map only.

Introducing TeeChart XAML

We’re excited to introduce you yet another addition to our TeeChart .NET line of products.

This time, we’re focusing on comfort and ease of use for programmers. While TeeChart for WinForms presents the programmers with a fully-featured designer, the visual support in other platforms is more lacking. I’m referring to what we call the XAML platforms: WPF, Silverlight, UWP and Xamarin.Forms.

In order to solve this usability problem, we’re making TeeChart and XAML understand each other better. Until now, if you wanted to design a chart using one of these XAML platforms, you had to place all the code to configure it by hand in the code-behind. Now, you will be able to build your charts with XAML code, as well as using the Visual Studio Designer for XAML.

We’re also introducing bindings to pretty much every property of your charts, thus enabling support for MVVM-oriented workflows. You will be able to tell the chart to grab your data from, for example, an ObservableCollection, and it will even update the series when the collection changes.

This will also bring support for Styles to your charts. Being able to set the font and background for all the charts in your window in a single declaration has never been easier!

Once TeeChart and XAML understand each other, the Visual Studio designer will be able to edit most of the properties out of the box. However, we’re not satisfied with that, and we’ll bring custom editors and designers to extend the Visual Studio designer’s functionality.

Here’s a sneak peek of what’s to come (click the image to enlarge):

presentation

Keep in tune for new announcements and details soon, including a Beta for interested clients!

Real-time charting with TeeChart for Xamarin.Android

To complete the zooming and panning functionality description in the multi-touch article, I should also speak about one last case which concerns real-time performance. Actually, this is not a zoom/pan style but the lack of it.  It’s the the TChart.Zoom.Style property value not covered on that article, ZoomStyles.None.

So, what does ZoomStyles.None consists of exactly?  It disables zooming and scrolling and its main priority is refreshing the chart at the highest rate possible for real-time purposes. This is achieved via multi-threading and forcing the chart to paint on the UI thread. For this to work the RunOnUiThread delegate need to be implemented in your real-time charting applications.

Before getting too abstract, let’s brake things into pieces. This article is based on the RealTimeCharting demo project shipped with TeeChart for Xamarin.Android evaluation and registered versions.  We will start setting up the chart with the minimum elements required to represent our data trying to add as little work for the CPU as possible and therefore get a more responsible application. As you can see in the code snippet below, this involves disabling: 3D, legend, gradient, walls, automatic axes range calculation, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Add the chart
tChart1 = new Steema.TeeChart.TChart(this);
tChart1.Aspect.View3D = false;
tChart1.Zoom.Style = Steema.TeeChart.ZoomStyles.None;
tChart1.Legend.Visible = false;
tChart1.Panel.Gradient.Visible = false;
tChart1.Walls.Back.Gradient.Visible = false;
tChart1.Walls.Back.Visible = false;
tChart1.Axes.Left.Grid.Visible = false;
tChart1.Axes.Bottom.Grid.Visible = false;
tChart1.Axes.Left.Automatic = false;
tChart1.Axes.Bottom.Automatic = false;
tChart1.Axes.Left.SetMinMax(MinValue, MaxValue);
tChart1.Axes.Bottom.SetMinMax(0, NumPoints);
tChart1.ClickSeries += new Steema.TeeChart.TChart.SeriesEventHandler(tChart1_ClickSeries);
 
//Left axis disabled for performance purposes.
tChart1.Axes.Left.Visible = false;
//Add the chart
tChart1 = new Steema.TeeChart.TChart(this);
tChart1.Aspect.View3D = false;
tChart1.Zoom.Style = Steema.TeeChart.ZoomStyles.None;
tChart1.Legend.Visible = false;
tChart1.Panel.Gradient.Visible = false;
tChart1.Walls.Back.Gradient.Visible = false;
tChart1.Walls.Back.Visible = false;
tChart1.Axes.Left.Grid.Visible = false;
tChart1.Axes.Bottom.Grid.Visible = false;
tChart1.Axes.Left.Automatic = false;
tChart1.Axes.Bottom.Automatic = false;
tChart1.Axes.Left.SetMinMax(MinValue, MaxValue);
tChart1.Axes.Bottom.SetMinMax(0, NumPoints);
tChart1.ClickSeries += new Steema.TeeChart.TChart.SeriesEventHandler(tChart1_ClickSeries);

//Left axis disabled for performance purposes.
tChart1.Axes.Left.Visible = false;

Besides setting the Zoom.Style to ZoomStyles.None, disabling some objects’ visibility and automatic axes range calculation, vertical axis is completely disabled to avoid it having to calculate labels or anything else that would require some precious computing time. Next thing to consider is the kind of chart style (we call it series in TeeChart) we will use for that kind of chart. This example uses a FastLine series, a specific line, reduced to the minimum expression for performance purposes.

1
2
3
var fastLine1 = new Steema.TeeChart.Styles.FastLine(tChart1.Chart);
fastLine1.FillSampleValues(NumPoints);
fastLine1.DrawAllPoints = false;
var fastLine1 = new Steema.TeeChart.Styles.FastLine(tChart1.Chart);
fastLine1.FillSampleValues(NumPoints);
fastLine1.DrawAllPoints = false;

It’s also worth mentioning the use of the DrawAllPoints property in FastLine series. This property controls how many points in a FastLine series will be displayed. When True (the default), all points are displayed. When set to False, it will only display points that have a different “X” position in screen pixels. So, when the series has several points that share the same X pixel position, but with different Y position, it will only display the first point (or use another chosen method via DrawAllPointsStyle property). When set to True (the default), only points that have a different X or a different Y pixel position are displayed. In some cases, setting DrawAllPoints can dramatically speed up displaying a FastLine series with lots lots of points. But, as not all points are displayed, the final output might not be as accurate.

After having explained that, I should only add that, in this project initialisation, a timer is being created to add data to the chart at a fixed continuous rate simulating a real-time data capturing environment.

1
2
3
4
timer1 = new System.Timers.Timer();
timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed);
timer1.Interval = 100;
timer1.Start();
timer1 = new System.Timers.Timer();
timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed);
timer1.Interval = 100;
timer1.Start();

The event that the timer triggers just implements RunOnUiThread delegate, which calls a method (AnimateSeries) that updates the series in the chart.

1
2
3
4
5
6
7
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
  RunOnUiThread(delegate
  {
    AnimateSeries(tChart1);
  });
}
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
  RunOnUiThread(delegate
  {
	AnimateSeries(tChart1);
  });
}

After the chart has been set up for work, the AnimateSeries method is where almost everything happens:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void AnimateSeries(Steema.TeeChart.TChart chart)
{
  var rnd = new Random();
  double newX, newY;
 
  tChart1.AutoRepaint = false;
 
  foreach (Steema.TeeChart.Styles.Series s in chart.Series)
  {
    // show only 50 points - delete the rest
    while (s.Count > NumPoints) s.Delete(0);
    if (s.Count > NumPoints) s.Delete(0);
    newX = s.XValues.Last + 1;
    newY = rnd.Next(MaxValue);
    if ((Math.Abs(newY) > MaxValue) || (Math.Abs(newY) < MinValue)) newY = 0.0;
    s.Add(newX, newY);
  }
 
  tChart1.Axes.Bottom.SetMinMax(tChart1.Axes.Bottom.Minimum + 1, tChart1.Axes.Bottom.Maximum + 1);
  
  tChart1.AutoRepaint = true;
  tChart1.Chart.Invalidate();
}
void AnimateSeries(Steema.TeeChart.TChart chart)
{
  var rnd = new Random();
  double newX, newY;

  tChart1.AutoRepaint = false;

  foreach (Steema.TeeChart.Styles.Series s in chart.Series)
  {
	// show only 50 points - delete the rest
	while (s.Count > NumPoints) s.Delete(0);
	if (s.Count > NumPoints) s.Delete(0);
	newX = s.XValues.Last + 1;
	newY = rnd.Next(MaxValue);
	if ((Math.Abs(newY) > MaxValue) || (Math.Abs(newY) < MinValue)) newY = 0.0;
	s.Add(newX, newY);
  }

  tChart1.Axes.Bottom.SetMinMax(tChart1.Axes.Bottom.Minimum + 1, tChart1.Axes.Bottom.Maximum + 1);
  
  tChart1.AutoRepaint = true;
  tChart1.Chart.Invalidate();
}

Besides generating some random data for the example chart, the most important thing here is the AutoRepaint property, which toggles chart repainting when new data is being added to the chart. To avoid the chart being repainted after every new point being added to it, we will disable it and will only enable it back, followed by a Chart.Invalidate() call, after all data has been added/removed from the series to force the chart being refreshed at this stage. In the meantime, if there are more points in the chart than those being specified by NumPoints constant, first points in the sequential range will be removed and new data will be added to the series in the same fashion. Finally, horizontal axis scale will be updated accordingly.

So we are done with it. In this article we have covered which are the basic TeeChart aspects you should consider modifying to get the most speed out of it. You probably know this is very important when handling large data volumes.

As mentioned at the beginning of the article, the complete example project is included with both TeeChart for Xamarin.Android evaluation and registered versions. However, for clarity and completeness of purpose, here’s the complete code listing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System;
 
using Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;
 
namespace RealTimeCharting
{
  [Activity(Label = "RealTimeCharting", MainLauncher = true, Icon = "@drawable/icon")]
  public class Activity1 : Activity
  {
    Steema.TeeChart.TChart tChart1;
    const int NumPoints = 50;
    const int MinValue = 0;
    const int MaxValue = 1000;
    System.Timers.Timer timer1;
 
    protected override void OnCreate(Bundle bundle)
    {
      base.OnCreate(bundle);
 
      // Set our view from the "main" layout resource
      SetContentView(Resource.Layout.Main);
 
      // Get our button from the layout resource,
      // and attach an event to it
      Button button = FindViewById<button>(Resource.Id.MyButton);
 
      button.Click += delegate 
      {
        timer1.Enabled = !timer1.Enabled;
        button.Text = (timer1.Enabled) ? Resources.GetString(Resource.String.Stop) : Resources.GetString(Resource.String.Start);
      };
 
      //Add the chart
      tChart1 = new Steema.TeeChart.TChart(this);
      tChart1.Aspect.View3D = false;
      tChart1.Zoom.Style = Steema.TeeChart.ZoomStyles.None;
      tChart1.Legend.Visible = false;
      tChart1.Panel.Gradient.Visible = false;
      tChart1.Walls.Back.Gradient.Visible = false;
      tChart1.Walls.Back.Visible = false;
      tChart1.Axes.Left.Grid.Visible = false;
      tChart1.Axes.Bottom.Grid.Visible = false;
      tChart1.Axes.Left.Automatic = false;
      tChart1.Axes.Bottom.Automatic = false;
      tChart1.Axes.Left.SetMinMax(MinValue, MaxValue);      
      tChart1.Axes.Bottom.SetMinMax(0, NumPoints);
      tChart1.ClickSeries += new Steema.TeeChart.TChart.SeriesEventHandler(tChart1_ClickSeries);
      
      //Left axis disabled for performance purposes.
      tChart1.Axes.Left.Visible = false;
 
      var fastLine1 = new Steema.TeeChart.Styles.FastLine(tChart1.Chart);
      fastLine1.FillSampleValues(NumPoints);
      fastLine1.DrawAllPoints = false;
 
      LinearLayout layout = FindViewById(Resource.Id.linearLayout1);
      layout.AddView(tChart1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FillParent, 400));
 
      timer1 = new System.Timers.Timer();
      timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed);
      timer1.Interval = 100;
      timer1.Start();
    }
 
    void tChart1_ClickSeries(object sender, Steema.TeeChart.Styles.Series s, int valueIndex, MotionEvent e)
    {
      Toast
          .MakeText(this, "point " + valueIndex, ToastLength.Short)
          .Show();
    }    
 
    void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
      RunOnUiThread(delegate
      {
        AnimateSeries(tChart1);
      });
    }
 
    void AnimateSeries(Steema.TeeChart.TChart chart)
    {
      var rnd = new Random();
      double newX, newY;
 
      tChart1.AutoRepaint = false;
 
      foreach (Steema.TeeChart.Styles.Series s in chart.Series)
      {
        // show only 50 points - delete the rest
        while (s.Count > NumPoints) s.Delete(0);
        if (s.Count > NumPoints) s.Delete(0);
        newX = s.XValues.Last + 1;
        newY = rnd.Next(MaxValue);
        if ((Math.Abs(newY) > MaxValue) || (Math.Abs(newY) < MinValue)) newY = 0.0;
        s.Add(newX, newY);
      }
 
      tChart1.Axes.Bottom.SetMinMax(tChart1.Axes.Bottom.Minimum + 1, tChart1.Axes.Bottom.Maximum + 1);
      
      tChart1.AutoRepaint = true;
      tChart1.Chart.Invalidate();
    }
  }
 
}</button>
using System;

using Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;

namespace RealTimeCharting
{
  [Activity(Label = "RealTimeCharting", MainLauncher = true, Icon = "@drawable/icon")]
  public class Activity1 : Activity
  {
    Steema.TeeChart.TChart tChart1;
    const int NumPoints = 50;
    const int MinValue = 0;
    const int MaxValue = 1000;
    System.Timers.Timer timer1;

    protected override void OnCreate(Bundle bundle)
    {
      base.OnCreate(bundle);

      // Set our view from the "main" layout resource
      SetContentView(Resource.Layout.Main);

      // Get our button from the layout resource,
      // and attach an event to it
      Button button = FindViewById<button>(Resource.Id.MyButton);

      button.Click += delegate 
      {
        timer1.Enabled = !timer1.Enabled;
        button.Text = (timer1.Enabled) ? Resources.GetString(Resource.String.Stop) : Resources.GetString(Resource.String.Start);
      };

      //Add the chart
      tChart1 = new Steema.TeeChart.TChart(this);
      tChart1.Aspect.View3D = false;
      tChart1.Zoom.Style = Steema.TeeChart.ZoomStyles.None;
      tChart1.Legend.Visible = false;
      tChart1.Panel.Gradient.Visible = false;
      tChart1.Walls.Back.Gradient.Visible = false;
      tChart1.Walls.Back.Visible = false;
      tChart1.Axes.Left.Grid.Visible = false;
      tChart1.Axes.Bottom.Grid.Visible = false;
      tChart1.Axes.Left.Automatic = false;
      tChart1.Axes.Bottom.Automatic = false;
      tChart1.Axes.Left.SetMinMax(MinValue, MaxValue);      
      tChart1.Axes.Bottom.SetMinMax(0, NumPoints);
      tChart1.ClickSeries += new Steema.TeeChart.TChart.SeriesEventHandler(tChart1_ClickSeries);
      
      //Left axis disabled for performance purposes.
      tChart1.Axes.Left.Visible = false;

      var fastLine1 = new Steema.TeeChart.Styles.FastLine(tChart1.Chart);
      fastLine1.FillSampleValues(NumPoints);
      fastLine1.DrawAllPoints = false;

      LinearLayout layout = FindViewById(Resource.Id.linearLayout1);
      layout.AddView(tChart1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FillParent, 400));

      timer1 = new System.Timers.Timer();
      timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed);
      timer1.Interval = 100;
      timer1.Start();
    }

    void tChart1_ClickSeries(object sender, Steema.TeeChart.Styles.Series s, int valueIndex, MotionEvent e)
    {
      Toast
          .MakeText(this, "point " + valueIndex, ToastLength.Short)
          .Show();
    }    

    void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
      RunOnUiThread(delegate
      {
        AnimateSeries(tChart1);
      });
    }

    void AnimateSeries(Steema.TeeChart.TChart chart)
    {
      var rnd = new Random();
      double newX, newY;

      tChart1.AutoRepaint = false;

      foreach (Steema.TeeChart.Styles.Series s in chart.Series)
      {
        // show only 50 points - delete the rest
        while (s.Count > NumPoints) s.Delete(0);
        if (s.Count > NumPoints) s.Delete(0);
        newX = s.XValues.Last + 1;
        newY = rnd.Next(MaxValue);
        if ((Math.Abs(newY) > MaxValue) || (Math.Abs(newY) < MinValue)) newY = 0.0;
        s.Add(newX, newY);
      }

      tChart1.Axes.Bottom.SetMinMax(tChart1.Axes.Bottom.Minimum + 1, tChart1.Axes.Bottom.Maximum + 1);
      
      tChart1.AutoRepaint = true;
      tChart1.Chart.Invalidate();
    }
  }

}</button>

 

Multi Touch with TeeChart for Xamarin.Android

Now that you know how to get started with TeeChart for Xamarin.Android, let’s get into a more interesting topic, multi-touch with TeeChart and Xamarin.Android.

From its inception, TeeChart for Xamarin.Android supports multi-touch gestures. However, since the release of build 4.14.6.25 in June 2014, the multi-touch offering has been extended with the implementation of the entire ZoomStyles.Classic functionality. In this article we will explain the different options presented to the programmer/user and what they can offer.

There are several ways to perform zooming and panning with TeeChart for Xamarin.Android. The door to the different possibilities is the TChart.Zoom.Style property. So we will elaborate on each specific value of Steema.TeeChart.ZoomStyles enum.

ZoomStyles.Classic

This is the most complete and versatile option available and the one which came the latest, as mentioned above. Choosing it the chart will zoom and scroll in a very similar way to the desktop version. However, instead of pressing a mouse button and drawing the zoom rectangle while dragging the mouse over the chart, it will respond to pinch gestures zooming the chart according to the virtual rectangle comprised between two finger pointers. This means dragging the fingers apart will zoom in the chart while closing them together will zoom the chart out. I must add this is automatically activated when two pointers are pressing the chart. If only one single pointer is pressing it panning will be activated instead. Actually, this is not 100% true, those options will be automatically activated only if the Allow property is also active (e.g.: TChart.Zoom.Allow and TChart.Panning.Allow). A little code snippet will help understanding this better:

1
2
3
tChart1.Zoom.Allow = true;
tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.Both;
tChart1.Panning.Allow = Steema.TeeChart.ScrollModes.Horizontal;
tChart1.Zoom.Allow = true;
tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.Both;
tChart1.Panning.Allow = Steema.TeeChart.ScrollModes.Horizontal;

The chart in the code above will be allowed to zoom in horizontal and vertical directions while will only allow scrolling in horizontal directions. Zoom has Allow and Direction self-explanatory properties, Panning does everything with one single property. To disable panning one should use Steema.TeeChart.ScrollModes.None. BTW, should ask to the TeeChart “fathers” about the reason behind this difference! writing this article has been useful to rethink this, deprecate Zoom.Allow property and add a new ZoomDirections.None enum value for Zoom.Direction property. Having that in mind, versions published after mid-July 2014 should use this code instead:

1
2
tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.Both;
tChart1.Panning.Allow = Steema.TeeChart.ScrollModes.Horizontal;
tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.Both;
tChart1.Panning.Allow = Steema.TeeChart.ScrollModes.Horizontal;

Finally, double tapping on the screen will undo any scroll or zooming action.

So, in resume, this options includes exactly the same functionality as the desktop version and gives complete control to the user about which scaling or translation will the chart perform.

ZoomStyles.FullChart

The two following options are simpler and are based on image scaling and translation instead of drawing directly to the chart canvas as the previous option does. So, FullChart will also perform to pinch and drag gestures but scrolling or zooming the chart as an image in its entirety.

ZoomStyles.InChart

This adds some sophistication to the FullChart option. Internally it separates the chart in 4 areas: background, chart rectangle, left axis and bottom axis. This is because when zooming or scrolling, performing pinch or drag gestures, on the chart rectangle (the area comprised between the axes where the series are being painted), this area will be transformed as an image, as ZoomStyles.FullChart but, this time, axes will also be transformed as individual images to keep in synch with the chart rectangle. The chart background won’t be affected by those changes. So, all in all, this is some kind of hybrid version between ZoomStyles.Classic and ZoomStyles.FullChart.

ZoomStyles.None

This option won’t allow zooming nor scrolling the chart. This is only intended for real-time charting applications where performance is optimized and therefore, zooming and panning not allowed. It’s not only that some chart settings are modified to optimise performance but the way the chart is internally painted also changes. Threads running on the UI should be used to add data to the chart and refresh it for real-time smoothness. An example of this can be seen in the RealTimeCharting example included with both evaluation and registered versions.

Summary

In a nutshell, in this article we can see that TeeChart for Xamarin.Android supports a varied multi-touch offering to fit a wide range of requirements, giving many options to the programmer/user. It’s also worth mentioning all of this doesn’t forget touch events on the chart and series!

Getting started with TeeChart for Xamarin.Android

It’s been some time now since TeeChart for Xamarin.Android was released, in August 2012, following the path Xamarin started drawing about one year before. While Xamarin has made huge progress during this time, the corresponding TeeChart version has also evolved and improved during this time.

If you are reading this, you probably got started with Xamarin.Android. We will elude the Xamarin products details and focus on using TeeChart on them.

Xamarin Studio

After creating a new blank Android application, the easiest and fastest way is using the TeeChart version in the Xamarin Component Store. Here’s a Xamarin guide on how to use it. Let’s apply that to TeeChart now. In an Android application, choose Project > Edit Components > Open Component Store (or Get More Components). This will load the component store for you:

ComponentStore

In the image above, you can see the TeeChart Charting Library as the 5th overall option. You can also find it in the Libraries category or find it with the given search option. Anyway, selecting the TeeChart Charting Library turns up to this screen:

TeeChartComponentStore

Besides the product info, getting started link, license, etc. there are two green buttons here. They will let you either evaluate the component or purchase it. A couple of things to comment here. First, the evaluation version is fully functional and the only limitation you’ll experience with it is a watermark over the charts. Second, evaluation and registered versions are also available at www.steema.com. Later on I will explain how to use the components outside the Component Store but now let’s continue with that. To do so I’ll choose the Try button option.

After agreeing to the licensing terms, this will add the TeeChart for Xamarin.Android trial version to your project, as an item in the Components folder and also as a TeeChart.Android.dll assembly reference in the References folder. The “references” part is all we will have to take care of to use TeeChart.Android.dll from outside the Component Store. The TeeChart entry in the Components list will also open the corresponding tab in Xamarin Studio’s main window:

TeeChartProjectComponents

The mentioned TeeChart tab has 3 sub tabs: Getting Sarted, Samples and Assemblies. Actually, those names are self-explanatory. The first one contains some basic information and code snippets to get one quickly started developing Android applications with TeeChart. The second one includes sample projects for iOS and Android. The third tab contains information about the assemblies included with the component and their version.

Prior to start developing our own application, we will try with the Android example pressing the corresponding button in the Samples tab. Doing so will add the MonoAndroidDemo project to our solution. The example project comes with a reference to the TeeChart.Android.dll we have chosen (trial or registered) and is ready to run on your emulator or device of choice.

Now, back to the Getting Started tab, there’s a little Android code snippet which we can copy and paste at the OnCreate method on our Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);
 
    Steema.TeeChart.TChart tChart1 = new Steema.TeeChart.TChart(this);        
    Steema.TeeChart.Styles.Bar bar1 = new Steema.TeeChart.Styles.Bar();       
    tChart1.Series.Add(bar1);        
    bar1.Add(3, "Pears", Color.Red);       
    bar1.Add(4, "Apples", Color.Blue);       
    bar1.Add(2, "Oranges", Color.Green);        
    Steema.TeeChart.Themes.BlackIsBackTheme theme = new Steema.TeeChart.Themes.BlackIsBackTheme(tChart1.Chart);       
    theme.Apply();        
    SetContentView(tChart1);
}
protected override void OnCreate (Bundle bundle)
{
	base.OnCreate (bundle);

	Steema.TeeChart.TChart tChart1 = new Steema.TeeChart.TChart(this);        
	Steema.TeeChart.Styles.Bar bar1 = new Steema.TeeChart.Styles.Bar();       
	tChart1.Series.Add(bar1);        
	bar1.Add(3, "Pears", Color.Red);       
	bar1.Add(4, "Apples", Color.Blue);       
	bar1.Add(2, "Oranges", Color.Green);        
	Steema.TeeChart.Themes.BlackIsBackTheme theme = new Steema.TeeChart.Themes.BlackIsBackTheme(tChart1.Chart);       
	theme.Apply();        
	SetContentView(tChart1);
}

so that we have our first TeeChart for Xamarin.Android application ready to go.

GettingStartedSample

Let me explain what those lines of code exactly mean. We start creating a TChart object, the basic object of the component set, which is the chart container. A Bar series comes after, it’s being created and added to the chart component. After that, some bars are added to the bar series: Y values, text labels and bar colors. Afterwards a chart theme is being created and applied to the chart to change the overall aspect. Finally, the chart component is being added to fill the parent view entirely. Getting a chart into your Android application is as simple as that.

If are no not using the Xamarin Component Store because you are using a TeeChart.Android.dll downloaded directly from Steema it wouldn’t much different. You should just manually browse for TeeChart.Android.dll in your hard drive at the References folder in your project: References > right mouse button -> Edit References > .Net Assembly. Here you’ll need to browse for the assembly in your disk and add it to the project.

ManualReference

Changing the trial version assembly from the Component Store to the registered version I have on my computer, I now get the same example without the evaluation watermark.

GettingStartedSampleRegistered

Visual Studio

There are no substantial differences on the basics of creating Android projects in Xamarin Studio and Visual Basic. As Xamarin explains in the Components Walkthrough article, Component Store is being used the same way in Visual Studio. A Components is added to each project. From there you can access the store with your Xamarin license credentials. Also, manually adding assembly references to your project works very much the same way.

Summary

Now that you know how to use TeeChart in your C# Android applications, you are all good to start representing your data graphically in Android with C#. TeeChart for Xamarin.Android installers for Windows and Mac OSX, supplied by Steema Software, include some more demos, help files and a number of tutorials completing a wide range of TeeCharting aspects. Also, at the Steema Support forums for registered customers, you’ll find a huge number of questions with examples covering almost aspects of TeeChart. Non-registered users can post their technical inquiries at StackOverflow tagged with “TeeChart” and the platform/language.

If you are a native Java Android developer, Steema Software also has a native component for you, TeeChart Java for Android. Those targeting Android from Embarcadero IDEs, can use the TeeChart VCL/FMX version.