Exporting Silverlight charts to images has been the most coveted feature. In this tutorial, I am going to show you to export Visifire Silverlight chart as image from managed code. So before we start lets check out the live example below.
Project Setup
Create a “Silverlight Application” project. Download the latest Visifire binaries from here. Extract the Zip file and add reference for the file named “SLVisifire.Charts.dll”. In this sample, I am going to use WriteableBitmap class in order to extract the pixel wise color information of the chart rendered inside Silverlight content. We will take help of JpegEncoder provided by a third party library called FJ.Core to encode the raster information to file stream.
Please download FJ.Core library from here and add the FJ.Core.dll as reference to the project.

Below are the steps that we are going to follow.
- Construct the Chart XAML inside MainPage.xaml.
- Exporting Chart as image.
Constructing Chart XAML
Open MainPage.xaml page, add a Chart and a button inside it as shown below:
<UserControl x:Class="SLSave2Image.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vc="clr-namespace:Visifire.Charts;assembly=SLVisifire.Charts"
mc:Ignorable="d" Width="500" Height="480">
<Grid x:Name="LayoutRoot">
<StackPanel>
<vc:Chart Name="MyChart" Width="500" Height="300" Theme="Theme1">
<vc:Chart.Titles>
<vc:Title Text="Visifire Chart"/>
</vc:Chart.Titles>
<vc:Chart.Series>
<vc:DataSeries RenderAs="Column" LabelEnabled="True">
<vc:DataSeries.DataPoints>
<vc:DataPoint AxisXLabel="Jan" YValue="35"/>
<vc:DataPoint AxisXLabel="Feb" YValue="32"/>
<vc:DataPoint AxisXLabel="Mar" YValue="27"/>
<vc:DataPoint AxisXLabel="Apr" YValue="17"/>
<vc:DataPoint AxisXLabel="May" YValue="16"/>
</vc:DataSeries.DataPoints>
</vc:DataSeries>
</vc:Chart.Series>
</vc:Chart>
<Button Name="ButtonSave" Height="25" Width="160" Content="Save Chart" ></Button>
</StackPanel>
</Grid>
</UserControl>
Exporting Chart as image
Now open MainPage.xaml.cs page and add the following namespaces.
using FluxJpeg.Core;
using FluxJpeg.Core.Encoder;
using System.Windows.Media.Imaging;
using System.IO;
using Visifire.Charts;
Now attach an event handler for Click event of Button inside the Page constructor.
public MainPage()
{
InitializeComponent();
ButtonSave.Click += new RoutedEventHandler(ButtonSave_Click);
}
void ButtonSave_Click(object sender, RoutedEventArgs e)
{
SaveToImage(MyChart);
}
Inside the ButtonSave event handler declare a function called SaveToImage() which will accept Chart as parameter as shown above.
Now inside the function definition, create a WriteableBitmap out of a Chart and collect the raster information from it. Then encode the raster information to file stream using JpegEncoder present in FJ.Core library as shown below. Finally save the file stream as image into hard drive.
/// <summary>
/// Save Visifire chart as Image
/// </summary>
/// <param name="Chart">Visifire.Charts.Chart</param>
private void SaveToImage(Chart chart)
{
try
{
WriteableBitmap bitmap = new WriteableBitmap(chart, null);
if (bitmap != null)
{
SaveFileDialog saveDlg = new SaveFileDialog();
saveDlg.Filter = "JPEG Files (*.jpeg)|*.jpeg";
saveDlg.DefaultExt = ".jpeg";
if ((bool)saveDlg.ShowDialog())
{
using (Stream fs = saveDlg.OpenFile())
{
MemoryStream stream = GetImageStream(bitmap);
//Get Bytes from memory stream and write into IO stream
byte[] binaryData = new Byte[stream.Length];
long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);
fs.Write(binaryData, 0, binaryData.Length);
}
}
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine("Note: Please make sure that Height and Width of the chart is set properly.");
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
/// <summary>
/// Get image MemoryStream from WriteableBitmap
/// </summary>
/// <param name="bitmap">WriteableBitmap</param>
/// <returns>MemoryStream</returns>
public static MemoryStream GetImageStream(WriteableBitmap bitmap)
{
byte[][,] raster = ReadRasterInformation(bitmap);
return EncodeRasterInformationToStream(raster, ColorSpace.RGB);
}
/// <summary>
/// Reads raster information from WriteableBitmap
/// </summary>
/// <param name="bitmap">WriteableBitmap</param>
/// <returns>Array of bytes</returns>
public static byte[][,] ReadRasterInformation(WriteableBitmap bitmap)
{
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
int bands = 3;
byte[][,] raster = new byte[bands][,];
for (int i = 0; i < bands; i++)
{
raster[i] = new byte[width, height];
}
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int pixel = bitmap.Pixels[width * row + column];
raster[0][column, row] = (byte)(pixel >> 16);
raster[1][column, row] = (byte)(pixel >> 8);
raster[2][column, row] = (byte)pixel;
}
}
return raster;
}
/// <summary>
/// Encode raster information to MemoryStream
/// </summary>
/// <param name="raster">Raster information (Array of bytes)</param>
/// <param name="colorSpace">ColorSpace used</param>
/// <returns>MemoryStream</returns>
public static MemoryStream EncodeRasterInformationToStream(byte[][,] raster, ColorSpace colorSpace)
{
ColorModel model = new ColorModel { colorspace = ColorSpace.RGB };
FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);
//Encode the Image as a JPEG
MemoryStream stream = new MemoryStream();
FluxJpeg.Core.Encoder.JpegEncoder encoder = new FluxJpeg.Core.Encoder.JpegEncoder(img, 100, stream);
encoder.Encode();
// Back to the start
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
You can download the complete solution here
(Hat tip to Visifire user Sagi Karni, Intel Corp)
Cheers,
Team Visifire