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.

Linking Charts Demo – Connecting TeeChart for NET to data binding sources

This demo shows how TeeChart’s Charts can be made to work with each other on a Form in a Winform application.

The demo uses the basis of the company data of a small technical products supplier; the company sells electronic based products, small computers, tablets and mobile phones and has markets in some countries around the globe.

Demo code project, written with Visual Studio 2015, can be downloaded here: https://www.steema.com/files/public/teechart/net/LinkingCharts_TChartNET.zip. Version on Github here: https://github.com/Steema/TeeChart-for-.NET-CSharp-WinForms-samples/tree/master/LinkingCharts

Create the project

We create a new Winforms project, and rename the main form to ReportForm.
1. Add objects to Form. We add three charts, a dataGridView and a textbox to the form and position them to take up the full space of the form (see fig. 1).


Fig.1 Designtime in Visual Studio

2. Using the Chart Editor, we have added a dataseries to the charts plus a smoothing function that you can see populated with random data at this stage. Data series can be added at designtime or at runtime.
3. We’ve placed the company sales data in an MS Access file called TechProducts Ltd and placed the file in the LinkingCharts solution folder.

Add the data to the project

4. We’ve added the database to the project, publicising tables and queries (views).
5. Using BindingSource components we connect to the Access database. The database already has prepared queries for some of the data we wish to visualise. For other cases, during runtime execution, we will write some sql queries in the code.

Three BindingSources are added, connecting to the $_€_SalesByYear, $_SalesByCountry_2015 and $_SalesByContinent views.

Connect the Charts to the data

6. To connect to a bindingSource, open up the Chart Editor that you wish to modify and select the Series that has previously been added and go to its Datasource tab. If you click on the Dataset tab you can select the bindingSource to which to connect that Series. In the following selection boxes, you can relate the different source fields to the X and Y series valuelists. See Fig. 2.


Fig.2 Series datasource editor

7. Repeat the connection technique for the three charts. We have connected the left Bar Series Chart to the SalesByContinent data, the right, Pie chart, to the SalesByCountry_2015 data and the lower line chart to the SaleByYear data.

Linking the Charts

8. We now have three charts, data by continent, region (countries with sales, within a continent) and country. Our aim in this demo is to link the three chart so that, by actioning a click on a bar of the continent chart, we can fill the region chart and that by actioning a click on the region chart we can show the sales evolution, over the last few years, of the selected country. The dataGridView will update to show the contents of the region chart. Note that the demo will open showing all countries in the region chart. The textbox updates to show ‘simulation’ commentary by a “robot journalist”.

9. To link the different elements of the project page we’ll set up some chart events.

ClickSeries event
We are using the ClickSeries event to take the value from the clicked chart as argument in a query to populate the next, related chart.
We can add the event definition at designtime by selecting the chart from which we wish to action and selecting the events tab of Visual Studio’s Properties window and, in this case, selecting the click event from the list for the TChart. That creates the event method in the Form’s code page. Here we’ll need to add some content so relate the Series click with where we wish to go.
We have chosen to partially manually code the query for clarity. The method follows these steps:
Take the original query for the country/continent chart as the basis, modify it to collect data for all available years.

Modify region query:
@contiparam is the continent that you wish to view, sourced from the label of the Continents Chart value. One could use the index value or some other variable if that were to make a better index for the query.

string sqlStr = "SELECT Sum(Fact_Invoices.Invoice_Value) AS SumOfInvoice_Value, Lookup_Country.Country_name, Lookup_Continent.continent_name";
 sqlStr += " FROM((Fact_Invoices INNER JOIN Lookup_Customers ON Fact_Invoices.Cod_Customer = Lookup_Customers.Cod_Customer)";
 sqlStr += " INNER JOIN Lookup_Country ON Lookup_Customers.Country_code_A2 = Lookup_Country.Country_code_A2)";
 sqlStr += " INNER JOIN Lookup_Continent ON Lookup_Country.Continent = Lookup_Continent.continent";
 sqlStr += " where Lookup_Continent.continent_name = @contiParam GROUP BY Lookup_Country.Country_name, Lookup_Continent.continent_name";

DataSet customers = new DataSet();

System.Data.OleDb.OleDbDataAdapter myAdapter = new System.Data.OleDb.OleDbDataAdapter(sqlStr, this.___SalesByCountry_2015TableAdapter.Connection);

Modify country query:
We take the label of the country that we wish to review, as the argument for the country-over-time query.

string sqlStr = "SELECT Sum(Fact_Invoices.Invoice_Value)AS SumOfInvoice_Value, Fact_Invoices.Invoice_year AS AYear, Lookup_Country.Country_name";
sqlStr += " FROM(Lookup_Customers INNER JOIN Fact_Invoices ON Lookup_Customers.Cod_Customer = Fact_Invoices.Cod_Customer)";
sqlStr += " INNER JOIN Lookup_Country ON Lookup_Customers.Country_code_A2 = Lookup_Country.Country_code_A2";
sqlStr += " WHERE Lookup_Country.Country_name = @countryParam";
sqlStr += " GROUP BY Fact_Invoices.Invoice_year, Lookup_Country.Country_name";

Those queries are enough to link the charts. We have taken a click in the same way from the GridView. Taking just one of them as an example, for the region chart, you can see how a bindingSource is created for the recipient chart to ‘plug-into’.

Creating and using a new bindingSource with the sql query as source for Grid and Chart.

DataSet customers = new DataSet();

System.Data.OleDb.OleDbDataAdapter myAdapter = new System.Data.OleDb.OleDbDataAdapter(sqlStr, this.___SalesByCountry_2015TableAdapter.Connection);

//apply region code
myAdapter.SelectCommand.Parameters.Clear();
myAdapter.SelectCommand.Parameters.Insert(0, new System.Data.OleDb.OleDbParameter("contiParam", region));
myAdapter.Fill(customers, "Customers");

bindEurope = new BindingSource();
bindEurope.DataSource = customers;
bindEurope.DataMember = customers.Tables[0].ToString();

dataGridView1.DataSource = bindEurope;
dataGridView1.Refresh();

dataGridView1.Columns[0].HeaderText = "Sales Income";
dataGridView1.Columns[1].HeaderText = "Country";
dataGridView1.Columns[2].HeaderText = "Continent";

tChart3[0].DataSource = bindEurope;
tChart3[0].YValues.DataMember = "SumOfInvoice_Value";
tChart3[0].LabelMember = "Country_name";

tChart3[0].CheckDataSource();

The Chart Series’ CheckDatasource() method is used by TeeChart to refresh the Chart data.

Application output

The screenshots in Fig 3 & 4 show the application at runtime.


Fig. 3. Application startup


Fig. 4. Europe & Switzerland selected.

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!

Big files: XML or JSON ? TeeBI !!

TeeBI Dashboards

 

Introduction

XML and JSON are very typical text formats used to store data, designed to be more comfortable than plain old “csv” text and allowing hierarchical (parent -> child) relationships.

However, even if there are many wonderful standard libraries to process them, there is still a speed problem when loading big quantities of data (say, hundreds or thousands of megabytes).

Content has to be parsed and stored into memory structures representing the “tree” nature of data nodes and attributes, which can be very fast (milliseconds) for small files, but terribly slow (minutes !) for big ones.

TeeBI core base class (TDataItem) is an “agnostic” memory structure providing parent -> child connections, using simple arrays to store data (one array per field or column).

Pseudo-code:


TDataItem = class
Name : String;
Items : Array of TDataItem;   // <--- Children
Kind : TDataKind;  // <-- Integer, String, Float, DateTime, Boolean, or "List of TDataItem"
Data : Array of ...    //  <--  one array for each Kind: "Int32Data : Array of Int32"
end

 

With a TDataItem, loading and saving big quantities of data is insanely fast (0.2 seconds to load or save 1 million rows with 4 columns on a normal PC).

The arrays are saved / loaded to / from Streams directly in one Write / Read operation.

That means we can import data from XML or JSON (or any other format like database datasets, server connections, Excel, etc, etc) into a TDataItem and then save it to a binary TeeBI file for later reuse.


Data := TDataItemPersistence.Load( 'my data.bi ')

 

Once a TDataItem is created or loaded, we can use it in many ways:

  • Search and modify data, re-structure columns
  • Sort data by multiple fields, and by custom expressions
  • Run ultra-fast SQL-like queries and summaries against TDataItems
  • Set master -> detail relationships between different TDataItems
  • Filter rows by code or using expressions (as strings or as TExpression classes)
  • Create calculated columns (using code or expressions)
  • Merge several TDataItems
  • Compare the structure and / or full data of TDataItems to obtain difference sets
  • Present TDataItems using Grids, Charts, Dashboards and PDF output
  • Connect TDataItems to a super-fast TBIDataset (a normal memory TDataset class)
  • Export to any other format (for example XML to JSON and vice-versa)
  • Access remote TDataItems from web servers transparently
  • Apply machine-learning algorithms using R or Python Scikit-learn
  • Access basic statistics of any TDataItem or child item

 

Note to TeeChart developers:

TeeBI includes a new TBIChart control (derived from TChart) that is capable of automatically creating new chart series and fill them from any TDataItem.

BIChart1.Data := MyDataItem;

A planned new feature is to integrate the Data property editor dialog inside the normal TeeChart editor, for design-time support (zero code charting !)

 

TeeBI library is available for download at the following link:

https://github.com/Steema/BI

Supported development environments:

  • Embarcadero Studio (Delphi, C++) from XE4 version and up
  • Lazarus FreePascal
  • …and soon for Microsoft Visual Studio .NET

Several 3rd party products can be optionally used with TeeBI:

https://github.com/Steema/BI/wiki/3rd-party-supported-products

 

For more information:

Please visit the TeeBI community at Google+ and the TeeBI home website for more information and technical details.

 

 

TeeBI Web Server returning a summary chart

TeeBI teaser

Small teaser of TeeBI.

This is the custom web server calculating a query and returning a chart. The only dependencies are TeeChart and Indy http web server component.

TeeBI Web Server returning a summary chart

 

Data is in column-oriented, memory-based custom format for speed reasons, no database or FireDac, etc are necessary. Data is imported from many sources (excel, text files, firedac or any other datasets, etc) to the custom format, which is then transparent to the user apps.

The web server is optional if data is not remote, and it can also return binary streams to normal desktop (vcl or fmx), or mobile apps (fmx).

A table of 2000×5 cells takes 180msec via web. For visual display, an fmx or vcl dbgrid easily handles 1 or more million rows with a custom “TBIDataset” linked to an in-memory data, allowing grouping, filtering and sorting using complex expressions involving columns from the same table or foreign-key(s) fields.

This speed is the basis for a next-coming set of machine-learning and data-mining algorithms, currently under development.

 

DashBoards and Data Visualization using Xamarin.Forms and TeeChart

DashBoards and Data Visualization using Xamarin.Forms and TeeChart

This article is going to show how easy it is to create dashboard designs to show information, especially data, by using the layouts included in Xamarin.Forms and how to use the TeeChart charting library, which fully supports Xamarin.Forms.

In this example several Charts will be added to the layout, choosing a specific design which will be adjusted for each device depending on the platform chosen.

The first step is to create a new Xamarin.Forms application (Portable)
New Project -> Visual C# -> Mobile Apps -> Blank App (Xamarin.Forms.Portable)

image1

 

Now, we’re going to add the TeeChart for Xamarin.Forms libraries as a reference to our projects to be able to use it.

Find the dlls for each specific platform on your hard disk (at the TeeChart for Xamarin.Forms installation folder). The TeeChart.PCL library also has to be referenced in all the projects.

 

Adding the TeeChart for Xamarin.Forms library as reference to the project.

 

TeeChart.PCL for the TeeChartDashBoards Portable project

TeeChart.PCL.Android.dll for the TeeChartDashBoards.Droid project

TeeChart.PCL.iOS.Unified.dll for the TeeChartDashBoards.iOS project

TeeChart.WP8.dll for the TeeChartDashBoards.WinPhone project

 

In order to make use of the TeeChart charting library we need to create a View which will contain the Chart component on it to be able to add it on our project, this “ChartView.cs” class has to be defined into the main project  (all the platforms will make use of it).

 

To create, just add a new class to the TeeCahrtDashBoards Portable project and add the following code to it :

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace TeeChartDashBoards
{
public class ChartView: View
{
public ChartView()
{
}

public EventHandler OnInvalidateDisplay;

public static readonly BindableProperty ModelProperty =
BindableProperty.Create(“ModelProperty”, typeof(Steema.TeeChart.Chart), typeof(ChartView), null);

public Steema.TeeChart.Chart Model
{
get { return (Steema.TeeChart.Chart)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
}

public void InvalidateDisplay()
{
if (OnInvalidateDisplay != null)
OnInvalidateDisplay(this, null);
}
}
}

 

 

Now, in order to make use of the ChartView, a renderer class is needed for each specific platform, so we’ll create a new class named “ChartViewRenderer.cs” for each platform and add the following code :

image2

This is the code for the Android project :

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Steema.TeeChart;
using TeeChartDashBoards;

[assembly: ExportRenderer(typeof(ChartView), typeof(ChartViewRenderer))]
namespace TeeChartDashBoards.Droid
{
public class ChartViewRenderer: ViewRenderer
{
protected TChart NativeControl
{
get
{
return ((TChart)Control);
}
}

protected ChartView NativeElement
{
get
{
return ((ChartView)Element);
}
}

protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);

if (Control == null)
{
var chartView = new Steema.TeeChart.TChart(Context);

chartView.Chart = NativeElement.Model;

SetNativeControl(chartView);
}

if (e.OldElement != null)
{
//unhook old events
}

if (e.NewElement != null)
{
//hook old events
}
}
}
}

 

Do the same for the iOS project, adding the following code :

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using TeeChartDashBoards;

[assembly: ExportRenderer(typeof(ChartView), typeof(TeeChartDashBoards.iOS.ChartViewRenderer))]
namespace TeeChartDashBoards.iOS
{
public class ChartViewRenderer : ViewRenderer<ChartView, Steema.TeeChart.TChart>
{
protected override void OnElementChanged(ElementChangedEventArgs<ChartView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;

var chartView = new Steema.TeeChart.TChart();

chartView.Chart = Element.Model;

SetNativeControl(chartView);
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
}

 

and the for the WinPhone project :

 

using TeeChartDashBoards;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Platform.WinPhone;

[assembly: ExportRenderer(typeof(ChartView), typeof(TeeChartDashBoards.WP8.ChartViewRenderer))]
namespace TeeChartDashBoards.WinPhone
{
public class ChartViewRenderer : ViewRenderer<ChartView, Steema.TeeChart.TChart>
{
protected override void OnElementChanged(ElementChangedEventArgs<ChartView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;

var chartView = new Steema.TeeChart.TChart();

chartView.Chart = Element.Model;
chartView.Aspect.ClipPoints = false;
chartView.Aspect.ExtendAxes = true;

SetNativeControl(chartView);
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
}

 

At this point, if we do “build solution” all should compile fine.

That being the case, it’s time to create a new Content Page in the main project, the initial page which will appear once the app is launched in one of the available platforms. Set “Home.cs” as the name for the page.

The solution should look like :

image3

Inside this page, the first thing we’ll do is to remove all the code that has been generated by default and will add a code that creates a Navigation page which will allow us to navigate through all the Charts inside the dashboard :

 

public Home()
{
NavigationPage.SetHasNavigationBar(this, false);
}

 

Now, we’ll define the layout, choosing the Grid layout that fits our needs. The grid layout allows one to specify the number of rows and columns to show and define the rowspan or colspan if needed (this will allow us to make the dashboard nicer) :

 

Grid grid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
Padding = 5,
RowDefinitions =
{
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }
},
ColumnDefinitions =
{
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
}
};

Content = grid;

 

Now it’s time to create the Charts that will be added on our dashboard. We’ll add seven Charts. First of all, we’ll create a “DashBoards” folder in the main project to add the content pages there, one for each chart. This folder has to be created one time in the main project (Portable).

Each page will define a specific chart with the Series used on it and its customised appearance. We’ll start with the content page named Page1.cs. We’ll use the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using Steema.TeeChart;

using Xamarin.Forms;

namespace TeeChartDashBoards.DashBoards
{
public class Page1 : ContentPage
{
Chart dashBoard0;
public ChartView DashView0;
public Page1()
{
// NavigationPage.SetHasNavigationBar(this, false);

dashBoard0 = new Chart();
dashBoard0.Aspect.View3D = false;
Steema.TeeChart.Styles.Bar bar1 = new Steema.TeeChart.Styles.Bar();
Steema.TeeChart.Styles.Line line1 = new Steema.TeeChart.Styles.Line();
dashBoard0.Series.Add(bar1);
dashBoard0.Series.Add(line1);
//barline chart
bar1.Clear();
bar1.Add(20);
bar1.Add(50);
bar1.Add(10);
bar1.Add(70);
bar1.Add(46);
bar1.Pen.Visible = false;
bar1.BarStyle = Steema.TeeChart.Styles.BarStyles.RectGradient;
bar1.Marks.Visible = false;
bar1.Gradient.StartColor = Color.White;
bar1.Gradient.EndColor = Color.FromRgb(102, 205, 170);
bar1.Marks.Visible = true;
bar1.Marks.Shadow.Visible = false;
bar1.Marks.Color = Color.White;
bar1.Marks.Font.Size = 15;
bar1.Marks.Font.Color = Color.FromRgb(102, 205, 170);
bar1.Marks.Pen.Visible = false;
bar1.Marks.ArrowLength = 5;
bar1.Color = Color.White;

line1.Clear();
line1.Add(0, 45);
line1.Add(0.444444444444444, 55);
line1.Add(0.888888888888889, 75);
line1.Add(1.33333333333333, 65);
line1.Add(1.77777777777778, 45);
line1.Add(2.22222222222222, 80);
line1.Add(2.66666666666667, 85);
line1.Add(3.11111111111111, 98);
line1.Add(3.55555555555556, 75);
line1.Add(4, 68);
line1.Color = Color.FromRgb(255, 255, 240);

line1.LinePen.Width = 3;
line1.Smoothed = true;

dashBoard0.Panel.Color = Color.FromRgb(102, 205, 170);
dashBoard0.Panel.Gradient.Visible = false;
dashBoard0.Walls.Back.Visible = false;
dashBoard0.Title.Text = “Bars and Lines”;
dashBoard0.Title.Alignment = TextAlignment.Start;
dashBoard0.Title.Font.Size = 22;
dashBoard0.Title.Font.Color = Color.White;
//dashBoard0.Title.Font.Name = “”;
dashBoard0.Axes.Left.AxisPen.Visible = false;
dashBoard0.Axes.Bottom.AxisPen.Color = Color.White;
dashBoard0.Legend.Visible = false;
dashBoard0.Axes.Left.Grid.Color = Color.White;
dashBoard0.Axes.Left.Grid.Style = Steema.TeeChart.Drawing.DashStyle.Dot;
dashBoard0.Axes.Left.Labels.Font.Color = Color.White;
dashBoard0.Axes.Bottom.Labels.Font.Color = Color.White;
dashBoard0.Axes.Left.Increment = 25;

DashView0 = new ChartView
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
DashView0.Model = dashBoard0;

Content = new StackLayout
{
Children = {
DashView0
}
};
}
}
}

 

The code above defines a Chart component for the page, adds some series styles to it and then set some custom aspects. I’m not going to add all the seven code pages here, as the same code structure has to be used, just that we can use other Series types and change the aspect settings as preferred. You will find the ones I have chosen by downloading the complete example at the bottom of the page. In order to follow this article do the same for other pages (Page2.cs, Page3.cs,…) as you have done for the first page.

 

 

Now that we have all the Chart styles (pages) we want to appear in our dashboard we’ll go back to the home page to add the necessary code to call each one of the pages and add it to the dashboard.

From here we call each one of the pages generated to be added to each of the cells of the grid; in this way we avoid having to recreate the charts.

First we create the seven DashBoard Charts, just before assigning the content to the grid:

 

// Create the Chart pages whose contain a specific Chart on each one
DashBoards.Page1 dashchart0 = new DashBoards.Page1();
DashBoards.Page2 dashchart1 = new DashBoards.Page2();
DashBoards.Page3 dashchart2 = new DashBoards.Page3();
DashBoards.Page4 dashchart3 = new DashBoards.Page4();
DashBoards.Page5 dashchart4 = new DashBoards.Page5();
DashBoards.Page6 dashchart5 = new DashBoards.Page6();
DashBoards.Page7 dashchart6 = new DashBoards.Page7();

 

To be able to navigate between each Content Page (available Chart pages) I’ve decided to create a button and add it to each cell. Some other ways might also work in order to execute part of code once cell is tapped, but this one allows one to do it easily, and also this way, lets us take advantage of the button class functionalities.

We need to create a button for each dashChart. Here’s the code for one of them :

 

Button button0 = new Button()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Command = new Command(() => Navigation.PushAsync(new DashBoards.Page1()))
};

 

And now we’ll add all the Charts and buttons created to the Grid layout. I’ve chosen some specific rowspan and colspan for some cells, just to look better, but anyone can customise it their way:

 

// Grid
grid.Children.Add(dashchart0.DashView0, 0,2,0,1);
grid.Children.Add(button0,0,2,0,1);
grid.Children.Add(dashchart1.DashView1, 2,4,0,1);
grid.Children.Add(button1,2,4,0,1);
grid.Children.Add(dashchart2.DashView2, 0, 1);
grid.Children.Add(button2,0, 1);
grid.Children.Add(dashchart3.DashView3, 1, 3, 1, 2);
grid.Children.Add(button3, 1,3,1,2);
grid.Children.Add(dashchart4.DashView4, 3,4,1,3);
grid.Children.Add(button4, 3, 4, 1, 3);
grid.Children.Add(dashchart5.DashView5, 0, 2, 2, 3);
grid.Children.Add(button5, 0, 2, 2, 3);
grid.Children.Add(dashchart6.DashView6, 2, 2);
grid.Children.Add(button6, 2, 2);

 

The last steps are to modify the App.cs class. This one needs to call the Home.cs content page :

 

public App()

{
// The root page of your application
MainPage = new NavigationPage(new Home());
}

 

We’ve also to modify the MainPage.xaml.cs page in order for it to work fine on WinPhone :

 

public MainPage()
{
InitializeComponent();

Forms.Init();
Content = new TeeChartDashBoards.Home().ConvertPageToUIElement(this);
}

 

Now we’re going to try to do a build for all the projects, run the app on each platform (IOS, Android y WinPhone) and see the results.

 

The source code of this example can be downloaded from the Steema GitHub page.

 

The result should look like:

grid

 

Theme persistence

2015 brings some aesthetic improvements for TeeChart VCL/FMX and .NET versions with the intention to make it easier for our users to create visually appealing charts. In this article I’m going to speak about different aspects about new theme usage and possibilities in TeeChart VCL/FMX to accomplish that objective. The concept and the result is almost the same in TeeChart .NET. The only differences are mostly internal and hence transparent to the user. We’ve started by creating two new themes: Lookout and Andros, with their associated color palettes: Lookout and Seawash respectively. This is how those themes look when displaying the full color palette or single color series:
LookoutExample2
Lookout theme example with one single color series
LookoutExample
Lookout theme example showing all the colors in the so called Lookout palette
SeaWashExample
Andros theme example showing all the colors in the associated Seawash palette
AndrosExample
Andros theme example showing a series with one single color from the Seawash palette.
However, this is only the tip of the iceberg because new themes also come with more theme related internal functionality. That is, when a custom theme is applied to a chart, new objects (series, axes and tools) added to it will also inherit the aspect of those themed objects which already exist in the chart. This didn’t occur before. So, for example, if you add a new series to a chart with one of those themes, series in in the chart will perpetuate their settings to additional series added afterwards. An example can be seen in the chart below, an additional series to a chart with the Andros theme will set the series marks to be exactly in the same format without having to perform any custom setting by the user.
Andros2Series
All series in this chart share series marks custom settings without the need of any specific code.
In the VCL/FMX version this applies to series, tools and custom axes, for now. Going even further, users can add their own themes by exporting the charts they created to the TeeChart native template  format (.tee files). There’s just one thing they should bear in mind is that for series to be “themed” they should be of a special type in the custom theme file, TThemedSeries. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  //Add themed series
  Chart1.AddSeries(TThemedSeries.Create(Self)).FillSampleValues;
  
  for i:=0 to Chart.SeriesCount-1 do
  begin
    Chart[i].Marks.Arrow.Visible:=False;
    Chart[i].Marks.Transparent:=True;
    Chart[i].Marks.Font.Color:=clWhite;
    Chart[i].Marks.Font.Name:='Verdana';
    Chart[i].Marks.Font.Size:=9;
  end;
 
  //Export theme
  SaveTeeToFile(Chart1, SourceFolder + 'TeeAndrosTheme.tee');
  //Add themed series
  Chart1.AddSeries(TThemedSeries.Create(Self)).FillSampleValues;
  
  for i:=0 to Chart.SeriesCount-1 do
  begin
    Chart[i].Marks.Arrow.Visible:=False;
    Chart[i].Marks.Transparent:=True;
    Chart[i].Marks.Font.Color:=clWhite;
    Chart[i].Marks.Font.Name:='Verdana';
    Chart[i].Marks.Font.Size:=9;
  end;

  //Export theme
  SaveTeeToFile(Chart1, SourceFolder + 'TeeAndrosTheme.tee');
Existing series in the chart can be switched to TThemedSeries using the self-explanatory ChangeSeriesType method. Once the custom custom .tee templates are ready, they can be applied using TThemeList.Apply method from TeeThemes unit, for example:
1
TThemesList.Apply( DestinationChart, 'MyChart.tee' );
TThemesList.Apply( DestinationChart, 'MyChart.tee' );
Worth noting that functionality described in this article is intended to be spread across all TeeChart versions in following releases throughout the year so stay tuned as new product updates start rolling out.  

TeeChart NET for iOS and Xamarin.iOS Unified API

As you know the TeeChart NET for iOS have been available since the first MonoTouch version created by Xamarin team. The TeeChart product allows to add professional Charts to your apps easily.

With a very few lines of code you can create the Chart, customize it, set a DataSource for the data and add as a View to the app. You just need to reference to the TeeChart.IOS dll and start working with it. Here you can find an article which shows how to display data from SQLite database into the Chart. This all using a Classic Xamarin iPnone project and the MonoTouch API.

Now Xamarin has introduced the new Xamarin.iOS Unified API. This new api allow to support 32 and 64 bit applications with the same binary. See more info about the Unified API here.

In order to continue supporting all the available Xamarin options, Steema has prepared the new TeeChart NET for Xamarin.iOS Unified version. Now both (the Classic and UInified versions) are included into the TeeChart NET for Xamarin.iOS product, so you can decide which one to use depending on your needs.

You can download the new installer at the customers download page or at the Evaluation versions page.

Here I’m going to show how to create a small dashboard application which uses several Charts on it by using the new Xamarin.iOS Unified and the TeeChart.iOS.Unified versions.

All from the Xamarin Studio IDE.

1) Creating the new Xamarin.iOS app from Xamarin Studio :
Open Xamarin Studio and go to File -> New -> Solution and then select c# -> iOS -> Unified API -> iPhone -> Single View Application.

TeeChartNET_IOS_Unified_1
2) Adding the TeeChart NET for Xamarin.iOS reference to the project :
Right click over the Project references and click “Add references”. Find the TeeChart.iOS.Unified.dll on your hard disk and add it to the the project references.

TeeChartNET_IOS_Unified_2
3) Adding code to the App’s RootViewController :
In order to add some Charts to our application we have to add the using Steema.TeeChart to the top of the unit :

using Steema.TeeChart;
Then create and customize the Charts inside the main View. First we define all the Chart View object we’re going to use, and also an image which will be used to load the logo.


TChart chart1;
TChart chart2;
TChart chart3;
TChart chart4;
UIImageView imageview;

Inside the ViewDidLoad method we’ll create all the Charts, create the Series we want to use and set the specific aspect and features for each Chart. We’ll add the following code :


public override void ViewDidLoad()
{
base.ViewDidLoad();

// Create the Chart View objects
chart1 = new TChart();
chart2 = new TChart();
chart3 = new TChart();
chart4 = new TChart();

// Setting specific frame for each Chart
CoreGraphics.CGRect r1 = this.View.Frame;
r1.Height = r1.Height / 4;
r1.Y = (r1.Height*3)-50;
chart1.Frame = r1 ;

CoreGraphics.CGRect r4 = this.View.Frame;
r4.Height = r4.Height / 4;
r4.Y = (r4.Height*2)-50;
chart4.Frame = r4 ;

CoreGraphics.CGRect r2 = this.View.Frame;
r2.Height = r2.Height / 2 - 50;
r2.Width = r2.Width / 2;
r2.Y = 0;
chart2.Frame = r2 ;

CoreGraphics.CGRect r3 = this.View.Frame;
r3.Height = r3.Height / 2 - 50;
r3.Width = r3.Width / 2;
r3.X = r3.Width;
r3.Y = 0;
chart3.Frame = r3 ;

// Adding Series and features for Chart 1
chart1.Series.Add (new Steema.TeeChart.Styles.Volume ());
chart1.Series [0].FillSampleValues ();
chart1.Aspect.View3D = false;
chart1.Axes.Bottom.Grid.Visible = false;
chart1.Legend.Visible = false;
chart1.Axes.Left.Visible = false;
chart1.Panel.Gradient.Visible = false;
chart1.Panel.Color = UIColor.Black.CGColor;
chart1.Walls.Back.Transparent = true;
chart1.Legend.Visible = false;
chart1.Axes.Bottom.Labels.Font.Color = UIColor.FromRGB (220, 220, 220).CGColor;

// Adding Series and features to the Chart 2
Steema.TeeChart.Styles.CircularGauge gauge1 = new Steema.TeeChart.Styles.CircularGauge ();
chart2.Series.Add (gauge1);
chart2.Axes.Left.Labels.Font.Size = 5;
gauge1.Frame.Width = 15;
gauge1.Ticks.VertSize = 3;
gauge1.Center.Shadow.Visible = false;
gauge1.Axis.AxisPen.Visible = false;
gauge1.FaceBrush.Gradient.Visible = false;
gauge1.FaceBrush.Color = UIColor.FromRGB(220,220,220).CGColor;
gauge1.Hand.Color = UIColor.FromRGB (255, 65, 56).CGColor;
gauge1.Hand.Gradient.Visible = false;
gauge1.Hand.Shadow.Visible = false;
gauge1.Axis.AxisPen.Visible = false;
gauge1.Value = 75;
gauge1.Ticks.VertSize = 3;
chart2.Panel.Gradient.Visible = false;
chart2.Panel.Color = UIColor.Black.CGColor;
chart2.Panel.MarginTop = 0;
chart2.Panel.MarginLeft = 0;
chart2.Panel.MarginBottom = 0;
chart2.Panel.MarginRight = 0;

// Adding Series and features to the Chart 3
Steema.TeeChart.Styles.CircularGauge gauge2 = new Steema.TeeChart.Styles.CircularGauge ();
chart3.Series.Add (gauge2);
chart3.Axes.Left.Labels.Font.Size = 5;
gauge2.Frame.Width = 15;
gauge2.Ticks.VertSize = 3;
gauge2.Axis.AxisPen.Visible = false;
gauge2.Center.Shadow.Visible = false;
gauge2.FaceBrush.Gradient.Visible = false;
gauge2.FaceBrush.Color = UIColor.FromRGB(220,220,220).CGColor;
gauge2.Hand.Color = UIColor.FromRGB (255, 65, 56).CGColor;
gauge2.Hand.Gradient.Visible = false;
gauge2.Hand.Shadow.Visible = false;
gauge2.Axis.AxisPen.Visible = false;
gauge2.Value = 50;
gauge2.Ticks.VertSize = 3;
chart3.Panel.Gradient.Visible = false;
chart3.Panel.Color = UIColor.Black.CGColor;
chart3.Panel.MarginTop = 0;
chart3.Panel.MarginLeft = 0;
chart3.Panel.MarginBottom = 0;
chart3.Panel.MarginRight = 0;

// Adding series and features for the Chart 4
chart4.Series.Add (new Steema.TeeChart.Styles.Line ());
chart4.Series.Add (new Steema.TeeChart.Styles.Line ());
chart4.Series.Add (new Steema.TeeChart.Styles.Line ());
chart4.Series [0].FillSampleValues (20);
chart4.Series [1].FillSampleValues (20);
chart4.Series [2].FillSampleValues (20);
chart4.Series [0].Marks.Visible = false;
chart4.Series [1].Marks.Visible = false;
chart4.Series [2].Marks.Visible = false;
(chart4.Series [0] as Steema.TeeChart.Styles.Line).LinePen.Width = 3;
(chart4.Series [1] as Steema.TeeChart.Styles.Line).LinePen.Width = 3;
(chart4.Series [2] as Steema.TeeChart.Styles.Line).LinePen.Width = 3;
chart4.Aspect.View3D = false;
chart4.Axes.Bottom.Increment = 3;
chart4.Panel.Gradient.Visible = false;
chart4.Panel.Color = UIColor.Black.CGColor;
chart4.Walls.Back.Transparent = true;
chart4.Legend.Visible = false;
chart4.Axes.Left.Visible = false;
chart4.Axes.Botto.Labels.Font.Color = UIColor.FomRGB (220, 220, 220.CGColor;

// Adding Chart views to the main View
this.View.AddSubview(chart1);
this.View.AddSubview(chart2);
this.View.AddSubview(chart3);
this.View.AddSubview(chart4);

// Setting general properties for alll the Charts
chart1.Header.Visible = false;
chart2.Header.Visible = false;
chart3.Header.Visible = false;
chart4.Header.Visible = false;

this.View.BackgroundColor = UIColor.Black;

// Adding logo to the App
imageview = new UIImageView (UIImage.FromBundle ("logo.png"));
CoreGraphics.CGRect rimage = new CoreGraphics.CGRect(95,this.View.Frame.Height-50,this.View.Frame.Width/2-30,50);

imageview.Frame = rimage;
this.View.AddSubview (imageview);
}

Before to try to cmpile and run the app, we’ll have to add the “logo.png” image at the “Resources” folder of our project.

Once this has been done, we can try to run our application, and the result should look like the following image :

TeeChartNET_IOS_Unified_3

 

You can download the source code of this example here.

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!