Flexcel Conceptual Docs Net PDF
Flexcel Conceptual Docs Net PDF
User Guides
In this section we cover the concepts that you need to know in order to use FlexCel effectively.
This is not a full reference for every type and method in FlexCel: If you are looking for reference
in the methods and classes, you can find it in the API Documentation tab.
In this section:
Installation Guide
Because before using FlexCel you need to install it.
Getting started
An introduction to start using FlexCel quickly.
Performance Guide
How to get the most of FlexCel performance wise.
iOS Guide
Special considerations when working in iOS.
Android Guide
Special considerations when working in Android.
Mono Guide
How to use FlexCel with Mono.
.NET Core
Using FlexCel with .NET Core.
1. If installing in Windows, you can just run the setup.exe downloaded from our website. It
will install all the examples, the dlls, the NuGet packages and the docs.
2. If installing in macOS, we don't have an automatic setup, but you can download the
NuGet packages from our website, register them, and use them. You should still install the
full FlexCel with setup.exe in a Windows machine to be able to see and run all the
examples.
IMPORTANT
Because FlexCel is not open source, the FlexCel NuGet packages must be put inside a private
repository that only people with license can access it. Please don't put it them in a public
repository like nuget.org.
There are many ways to put FlexCel in a private repository, and depending on your needs you
might find some better than others. You can read about it here: https://docs.microsoft.com/en-
us/nuget/hosting-packages/overview
But the simplest way is just to add the folder where the FlexCel NuGet packages are to the
NuGet feeds. Even when https://docs.microsoft.com/en-us/nuget/hosting-packages/overview
mentions putting them in a network share, there is no need to share the folder. You can use a
simple not shared folder, and this is what the FlexCel Setup does. To do it manually when you are
not using Setup.exe follow the steps below:
2. Either from the Package Manager console, a command line prompt or a terminal, type:
NOTE
You can also visually manage your package sources by going to Menu->Tools->NuGet
Package Manager->Package Manager Options:
But in this guide we preferred to focus on the command line way, beause it allows you to
register nuget sources even if Visual Studio is not available.
For uninstalling the source, just go to the package manager options and remove it, or type:
1. Select Browse
2. Select All as the package source.
3. Search for FlexCel
4. Select the TMS.FlexCel package.
5. Press Install.
NOTE
So to be safe, make sure to select All and search for FlexCel, instead of selecting the
flexcel.tmssoftware.com feed
NOTE
Normally the only package you need to install is TMS.FlexCel. This includes almost all the
functionality in FlexCel. But when doing WinForms apps, there is an Excel previewer available
in the TMS.FlexCel.WinForms package that you might also want to use. For WebForm apps,
there is also a viewer which is available in the TMS.FlexCel.WebForms package.
The extra packages only work in Windows and in WinForms or WebForms apps. For all other
kind of apps, the only package that you need and can install is TMS.FlexCel.
NOTE
In FlexCel for .NET Core you can't manually reference the assemblies and the only choice is via
NuGet.
1. Download the exe setup. This is the preferred way to install FlexCel in Windows, since it
will install the NuGet package, the libraries, the examples and docs.
2. Download the NuGet packages. This includes only the NuGet packages (which are also
included in the exe setup), but doesn't include example code or docs, and won't register
the NuGet source in your machine. This is the preferred way to install FlexCel in platforms
different from Windows, and you can find more information on how to install it on the
installation guide.
Once you have FlexCel installed, you need to decide how to reference it. There are 2 ways you
can reference FlexCel:
1. Install via NuGet packages. This is a standard installation same as any other NuGet
installation, but with the difference that FlexCel is not stored in nuget.org How to install
via NuGet is detailed step by step in the installation guide
2. Install by manually referencing the assemblies. This is possible in all platforms except in
.NET Core, where the only option is via NuGet.
How you decide to reference the assemblies is up to you: In general .NET is moving from a
monolithic framework to a framework "on demand" via NuGet, and so we would recommend
you to use NuGet too. But if you prefer to reference the assemblies directly, you can do that too.
TIP
If you are unsure, just install the exe setup and use the FlexCel NuGet packages.
To get started, create an empty Console application and save it. Add the TMS.FlexCel NuGet
package to your application or add a manual reference to FlexCel.dll
using System;
using FlexCel.Core;
using FlexCel.XlsAdapter;
namespace Samples
{
class MainClass
{
public static void Main(string[] args)
{
//Create a new empty Excel file, with default formatting as if it
was created by Excel 2016.
//Different Excel versions can have different formatting when they
create
//an empty file, so for example
//Excel 2003 will have a default font of Arial, and 2016 will use
Calibri.
//This format is anyway the starting format, you can change it all
later.
XlsFile xls = new XlsFile(1, TExcelFileFormat.v2016, true);
And that is it. You have just made an application that creates an Excel file. Of course we are just
scratching the surface: we will see more advanced uses later in this guide.
NOTE
This sample deduces the file format from the file name. If you saved as "test.xls", the file
format would be xls, not xlsx. You can specify the file format in a parameter to the "Save"
method if needed; for example when saving to streams.
You can also download APIMate for your operating system from the following locations:
Or you can compile it from source (sources are included when you install FlexCel).
Using simple files will make it much easier to find the relevant code in APIMate
You can keep the xls/x file open in both Excel and APIMate, modify the file in Excel, save, press
"Refresh" in APIMate to see the changes.
When you open it in apimate, you should see this code which is the code you need to write in
FlexCel to generate the same file:
Note that there is a language button in the toolbar where you can choose which language you
want the code to be.
3. Reading a file
There is a complete example on Reading files in the documentation. But for simple reading, you
can do as follows:
using System;
using FlexCel.Core;
using FlexCel.XlsAdapter;
namespace FileReader
{
class MainClass
{
public static void Main(string[] args)
{
XlsFile xls = new XlsFile(System.IO.Path.Combine(Environment.GetFold
erPath(Environment.SpecialFolder.Personal), "test.xlsx"));
4. Manipulating files
APIMate will tell you about a huge number of things, like how to paint a cell in red, or how to
insert an autofilter. But there are some methods that APIMate can't tell you about, and from
those the most important are the manipulating methods:
• Use ExcelFile.InsertAndCopyRange for inserting rows or column or ranges of cells. Also for
copying ranges or cells or full rows or full columns. Or for inserting and copying cells/
columns/rows in one operation (like pressing "Copy/Insert copied cells" in Excel). It can
also copy the cels from one sheet to the same sheet, to another sheet, or to another sheet
in another file. InsertAndCopyRange is a heavily overloaded method, and it can do many
things depending on the parameters you pass to it.
• Use ExcelFile.MoveRange to move a range, full rows or full columns from one place to
another.
5. Creating Reports
You can create Excel files with code as shown above, but FlexCel also includes a reporting engine
which uses Excel as the report designer. When using reports you create a template in Excel, write
some tags on it, and run the report. FlexCel will replace those tags by the values from a database
or memory.
3. In the ribbon, go to "Formulas" tab, and press "Name manager" (In Excel for OSX or Excel
2003, go to Menu->Insert->Name->Define)
4. Create a name "Customer" that refers to "=Sheet1!$A$1". The name is case insensitive,
you can write it in any mix of upper and lower case letters. It needs to start with two
underscores ("_") and end with two underscores too. We could use a single underscore for
bands that don't take the full row or "I_" or "I__" for column reports instead, but this is for
more advanced uses.
6. Create a new Console app, save it as "CustomerReport", and paste the following code:
using System;
using System.Collections.Generic;
using FlexCel.Core;
using FlexCel.Report;
namespace Samples
{
class MainClass
{
public static void Main(string[] args)
{
var Customers = new List<Customer>();
Customers.Add(new Customer { Name = "Bill", Address = "555 demo
line" });
Customers.Add(new Customer { Name = "Joe", Address = "556 demo
line" });
}
}
}
class Customer
{
public string Name { get; set; }
public string Address { get; set; }
}
}
NOTE
If doing an OSX Console application, you will need to add a reference to System.Data,
System.Xml and XamMac or MonoMac to the app. You might also need to copy XamMac.dll or
MonoMac.dll you your output folder. And in a console application, you will need to initialize
the Cocoa framework by calling MonoMac.AppKit.NSApplication.Init() For normal OSX
applications you will not need to do anything: This applies only to Console OSX apps.
For this getting started guide, we will show how to do an export with the default options of the
active sheet.
You can search for specific keywords at the top right of the main screen, to locate the demos
that deal with specific features. So for example if you are looking for demos which show
encryption, you will write "encrypt" in the search box:
Introduction
The FlexCel API (Application Programmer Interface) is what you use to read or write Excel files on
a low level way. By “low level” we mean that this API is designed to work really “close to the
metal” and there aren’t many layers between you and the xls/xlsx file being created. For example,
FlexCel API doesn’t know about datasets, because datasets are a higher level concept. If you
want to dump a dataset into an Excel file using the API, you need to loop in all records and enter
the contents into the cells.
In addition to the FlexCel API we provide a higher level abstraction, FlexCelReport, that knows
about datasets and in general works at a more functional level; a declarative approach instead of
an imperative approach. What is best for you depends on your needs.
Basic Concepts
Before starting writing code, there are some basic concepts you should be familiar with.
Mastering them will make things much easier down the road.
That is, cell A1 is (1,1) and not (0,0). To set the first sheet as ActiveSheet, you would write
ActiveSheet = 1 and not ActiveSheet = 0 .
So, in C# and C++ loops should read: for (int i = 1; i <= Count; i++) , and in VB.NET they
should read for i = 1 to Count .
The two exceptions to this rule are XF and Font indexes, which are 0 based because they are so
on Excel.
Cell Formats
All formats (colors, fonts, borders, etc) on an Excel workbook are stored into a list, and referred
by number. This number is known as the XF (eXtended Format) index. A simple example follows:
Here Cell B3 has XF = 0 and the XF definition for the background color is green. Row 6 has XF =
2, so all the empty cells on row 6 are orange. Column C has XF = 1, so all the empty cells on
column C that do not have a Row format are blue.
The priority goes, from higher to lower: Cell format->Row format->Column format. This means
that if column A has a column format red and and Row 1 has row format green, the cell A1 will
be green. If you apply a cell format to cell A1, it will be used instead of either row or column
formats as it has the highest priority.
NOTE
Sometimes when you work in Excel it might look like priority is different. For example if you
format a row green and after that a column red, you will see that the intersection between
column and row is red.
What is happening under the hood is that when you format the column red, Excel applies a
column format red to the column, and after that a cell format red to the cell in the
intersection. This way it can look like the column format is taking priority over the row, but it is
actually the cell format applied to the cell at the intersection which has the priority.
Most formatting methods at FlexCel return an XF index, and then you have to look at the XF list
(using the ExcelFile.GetFormat method) to get a class encapsulating the real format. There is a
helper method: ExcelFile.GetCellVisibleFormatDef that obtain the XF index and return the format
class in one step.
To create new formats, you have to use the ExcelFile.AddFormat method. Once you get the Id of
the new XF, you can use it as you wish.
NOTE
You don't have to worry also on inserting a format 2 times: If it already exists AddFormat will
return the existing id and not add a new XF entry.
You can create new Style formats with ExcelFile.SetStyle, and new Cell formats with
ExcelFile.AddFormat. Once you create a new style and give it a name, you can get its definition as
a TFlxFormat, change its TFlxFormat.IsStyle property to true, define which properties you want to
link with the TFlxFormat.LinkedStyle property, and add that format using AddFormat to get the
cell format. Once you have the cell style, you can just apply it to a cell.
NOTE
The TLinkedStyle class has a member, TLinkedStyle.AutomaticChoose, which if left to true (the
default) will compare your new format with the style and only link those properties that are the
same.
For example, let’s imagine you create a style “MyHyperlink”, with font blue and underlined.
Then you create a Cell format that uses “MyHyperLink” as parent style, but also has red
background, and apply it to cell A1. If you leave AutomaticChoose true, FlexCel will detect that
the cell format is equal in everything to its parent style except in the background, so it will not
link the background of the cell to the style. If you later change the background of
MyHyperlink, cell A1 will continue to be red.
This allows for having “partial styles” as in the example above: an “hyperlink” style defines that
text should be blue and underlined, but it doesn’t care about the cell background. If you want
to manually set which properties you want to have linked instead of FlexCel calculating that for
you, you need to set TLinkedStyle.AutomaticChoose to false and set the “Linked….” Properties
in TLinkedStyle to the values you want.
Font Indexes
The same way we have an XF list where we store the formats for global use, there is a Font list
were fonts are stored to be used by XFs. You normally don't need to worry about the FONT list
because inserting on this list is automatically handled for you when you define an XF format. But,
if you want to, you can for example change Font number 7 to be 15 points, and all XFs
referencing Font 7 will automatically change to 15 points.
Colors
While colors in Excel up to 2003 were indexes to a palette of 56 colors, Excel 2007 introduces
true colors, and FlexCel implements it too since version 5.0. One thing that is nice about the new
colors is that they can be retrofitted in the xls file format, so while you won’t be able to see true
color in Excel 2003 (Excel 2003 just doesn't know about them), the data is there in the xls file
anyway. So if you open the xls file with FlexCel 5 or later Excel 2007 and later you will see the
true colors, even if the xls file format originally didn't knew about them.
There are four kinds of colors that you can use in Excel:
• RGB: This is just a standard color specified by its Red, Green and Blue values.
• Theme: There are 12 theme colors, and each of them can have a different tint (brightness)
applied. Theme colors are the default in newer Excel versions and you are likely using
them even if not aware.
Given the importance of theme colors in newer Excel, and that they aren't trivial to
describe, we will cover them in a separate section below.
• Indexed Colors. This is kept for background compatibility with Excel 2003, but we
strongly recommend you forget about them.
You can access any of these variants with the struct TExcelColor.
TExcelColor also automatically converts from a Sytem.Drawing.Color, so you can just specify
something like:
MyObject.Color = Color.Blue;
MyObject.Color = TExcelColor.FromTheme(…);
The “NearestColorIndex” used in older version of FlexCel has been deprecated and you shouldn’t
use it.
Theme colors
Theme colors are an interesting topic well worth its own section in this manual. Excel uses theme
colors by default, which means that most spreadsheets you see around are using them, even if
the people who created the files didn't know what a "theme" color was.
The first thing to notice is that Excel has 12 theme colors, which FlexCel maps inside the
TThemeColor enumeration. Each one of those 12 colors can be made lighter or darker by
changing its TExcelColor.Tint leading to millions of possible colors derived from those basic 12
colors.
When you open a color dialog in Excel, the colors that are shown in the main square are different
variations of the 12 theme colors:
Each one of the 12 theme colors takes one column in the grid, and in the rows of the grid you
can see variations of those colors with different tints.
NOTE
We kind of lied here: If you tried to count the columns in the image, you will see that there are
10 columns instead of 12. This is because even when Excel has 12 defined theme colors, only
10 of them are visible. The other 2 are the color for an hyperlink and a visited hyperlink, and
they don't show in the Excel UI as usable colors.
The selected color you see in the image above is a color of the theme Accent1 (all colors in
column 4 are of theme Accent1). It also has applied a tint which makes it 40% lighter than the
standard Accent1 color, as shown in the Excel tooltip.
Themes are a nice way to specify colors, since you can change the theme later, and all colors will
change to match. This means that you can go to the theme selector in the Excel ribbon (in the
Page Layout tab) and change what Accent1 (and all the other theme colors) mean:
If you select for example the "Ion Boardroom" theme as shown in the image, you will see the
color palette changes to:
Note how the color Accent1 is now Plum instead of Blue. When you change the theme of your
spreadsheet, all cells which had Accent1 theme colors will change from blue to plum, while cells
which had a Blue RGB color set will continue to be blue.
When setting colors in a file you are creating, it is a nice thing to use theme colors instead of
RGB colors, so your users will be able to change the theme. FlexCel has full support of theme
colors, and you can easily find out how to use them in your code with APIMate.
http://tmssoftware.com/site/blog.asp?post=135
Date Cells
As you might already know, there is no DATE datatype in Excel.
Dates are saved as a double floating number where the integer part is the number of days that
have passed from 1/1/1900, and the fractional part is corresponding fraction of the day. For
example, the number 2.75 stands for "02/01/1900 06:00:00 p.m." You can see this easily at Excel
by entering a number on a cell and then changing the cell format to a date, or changing the cell
format of a date back to a number.
The good news is that you can set a cell value to a DateTimeValue, and FlexCel will automatically
convert the value to a double, keeping in count “1904” date settings (see below). That is, if you
enter
XlsFile.SetCellValue(1, 1, DateTime.Now) , and the cell (1,1) has date format, you will write
The bad news is that you have no way to know if a cell has a number or a date just by looking at
its value. If you enter a date value into a cell and then read it back, you will get a double. So you
have to look at the format of the cell. There is a helper function, FormatValue that can return if
the cell has a date or not by looking at the format.
This would be the correct way to find out if a cell contains a date:
Note how the actual value of the cell doesn't matter, only the format of the cell.
NOTE
Excel also has a “1904” date mode, where dates begin at 1904 and not 1900. This more was
used on Excel for mac before OSX: today both OSX and Windows use a 1900 date system. But
you can change this option in Excel for OSX or Windows today too, so you might come across
some file using the 1904 date system. FlexCel completely supports 1900 and 1904 dates, but
you need to be careful when converting dates to numbers and back.
NOTE
There is a bug in how Excel handles the 1900 year. It considers it to be a leap year but 1900
was a normal year and February 29 1900 never happened. This means that the serial number
used to represent the date gets one day out of sync with the serial number used by C#. The
serial number 0 will show in Excel as "January 0, 1900" (even if there was never a January 0
either)
Note that even if Excel cared to displays correctly the 0 as December 31, 1899 instead of
January 0, 1900 it would still be off by a day with the dates as used in C#, which displays
December 30.
FlexCel will try to do the best to replicate the Excel bug so you get the same values as Excel
(ironically, Excel has the bug only because it was replicating a bug in Lotus 1-2-3). This means
that for example FlxDateTime.FromOADate(0, false) will return a DateTime of January 31, 1899
which is the closest we can get to the value Excel shows. (we can't return a DateTime with a
value of January 0, that is not possible). But whatever we do, reality is that dates in Excel
before March 1, 1900 are wrong. Avoid using them.
Normally copying in Biff8 is good for pasting the data in Excel, Html is good if you later want to
paste in Word or PowerPoint, and text works for pasting into applications that don’t understand
Biff8 or HTML.
Copying and pasting in native BIFF8 format is a great advance over copying/pasting on plain text
only. It allows you to keep cell formats/colors/rounding decimals/merged cells/etc. But it has its
limitations too:
We would like to say that these limitations are not FlexCel's fault. The BIFF8 specification is
correctly implemented; those are limitations on Excel's part.
Of course, Excel can copy and paste everything without problems, but this is because Excel
doesn't use the clipboard to do the operation. If you close all instances of Excel, open a
Worksheet, copy some cells to the clipboard, close Excel and open it again you will run into the
same limitations. Copy/paste limitations on Excel don't show when it is kept in memory.
This class stores an Excel file in memory, and has methods allowing loading a new file into it,
modifying it, or saving its contents to a file or a stream.
NOTE
ExcelFile is the abstract class that provides the interface that XlsFile implements. In this
documentation, we use ExcelFile and XlsFile interchangeably, because both implement the
same methods and properties. The first is an abstract class which specifies the functionality,
and the second is the actual code.
IMPORTANT
Even when a FlexCel object is fully managed and you don't need to dispose it, keep in mind
that it stores a full spreadsheet in memory. Do not leave global XlsFile objects hanging
around when you don't need them because they can use a lot of memory. Use local XlsFile
instances or set them to null as soon as you are done using them.
1. By default, FlexCel never overwrites an existing file. So, before saving you always have to
call File.Delete, or set ExcelFile.AllowOverWritingFiles property = true. On this example, we
set AllowOverWritingFiles = true when we construct the object in the line: XlsFile xls
= new XlsFile(true); .
2. While you can explicitly specify the file format when saving, normally you don’t need to do
it. FlexCel is smart enough to know that files with extension “xlsx” or “xlsm” must be saved
in xlsx file format, and that files with extension “xls” must be saved as xls. When saving to
a stream, then you need to specify the file format, or it will be xls by default.
Modifying files
Once you have loaded a document with ExcelFile.Open or created a new empty file with
ExcelFile.NewFile you can proceed to modify it. Add cells, formats, images, insert sheets, delete
ranges, merge cells or copy ranges from one place to another. It is not on the scope of this
document to show each and every thing you can do because there are hundreds of commands
and that would take another book. To learn about specific methods, take a look at the examples
and use the APIMate tool as described here in the Getting Started guide.
Now, something that might be non intuitive if you are not aware of it, is that FlexCel provides
that functionality in a few very powerful and overloaded methods, instead of a million
standalone methods. That is, instead of having “InsertRow” “InsertColumns”, “InsertCells”
“CopyColumns”, “CopyCells” “CopyRows”, “InsertAndCopyRows” “InsertAndCopyColumns”, etc,
FlexCel just provides a single “InsertAndCopyRange” method that does all of this and more (like
copying rows from one file and inserting it into another)
The reason we kept it this way (few powerful methods instead of many specialized and simpler
methods) is that less methods are easier to remember. From the editor, you just need to
remember to start typing “InsertAnd…” and intellisense will show most options. No need to
guess if the method to add a new sheet was named “AddSheet” or “InsertSheet”.
Naming probably isn’t the best either, this is for historical reasons (because original
implementations did only insert and copy, and then, as we added more functionality it was
added to those methods), but also because a better naming isn’t easy either. Calling a method
InsertAndOrCopyCellsOrColumnsOrRanges() isn’t much better. So just remember that whatever
you want to do, you can probably do it with one of the following methods:
• DeleteRange: Deletes a range of cells, a row or a column in a sheet, moving the other
cells left or up as needed. If InsertMode is NoneDown or NoneRight, cells will be cleared,
but other cells won’t move.
• InsertAndCopySheets: Inserts and or copies a sheet. Use this method to add a new
empty sheet.
NOTE
InsertAndCopy operations could be theoretically implemented as “Insert” then “Copy”, but that
would be much slower. In many cases we want to insert and copy at the same time, and doing
it all at once is much faster. Also in some border cases where the inserted cells are inside the
range of the copied cells, inserting and then copying can give the wrong results.
While most changes are under the hood, and we made our best effort to keep the API
unchanged, some little things did change and could bring issues when updating:
• The number of rows by default in FlexCel is now 1048576, and the number of
columns is 16384. This can have some subtle effects:
◦ Named ranges that were valid before might become invalid. For example, the
name LAB1 would be a valid cell reference in 2007 mode, (Just like A1). In Excel
2003 you can have up to column “IV”, but in Excel 2007 you can have up to column
“XFD”, so almost any combination of 3 characters and a number is going to be a
valid cell reference, and thus can’t be a name. If you have issues with this and can’t
change the names, you will have to use the “Compatibility mode” by setting the
static variable:
ExcelFile.ExcelVersion
◦ “Full Ranges” might become partial. Full ranges have the property that they don’t
shrink when you delete rows. That is, if for example you set the print area to be
A1:C65536 in Excel 2003, if you delete or insert rows the range will stay the same. If
you open this file with FlexCel 5 or Excel 2007, and delete the row 1, the range will
shrink to A1:C65535, as it is not full anymore. To prevent this, FlexCel automatically
converts ranges that go to the last row or column in an xls file to go to the last row
or column in an xlsx file. If you don’t want FlexCel to autoexpand the ranges, you
can change this setting with the static variable:
ExcelFile.KeepMaxRowsAndColumnsWhenUpdating
Also, when using XlsFile, use references like “=Sum(a:b)” to sum in a column instead
of “=Sum(a1:b65536)”
And as you have exactly 2 bytes of rows and 1 byte of columns they are also the
same. It is the same to add 65535 rows (wrapping) or to subtract one, you will
always arrive to the cell above. Now, with more columns and rows this is not true
anymore. And you can’t know by reading a file if the value was supposed to mean
“-1” or “+65535”. So both Excel and FlexCel might interpret a CF formula wrong
when the cells are separated by too many rows or columns.
Again the solution if you have this issue is to turn ExcelFile.ExcelVersion back to
2003.
◦ If you relied in changing the palette to change cell colors, this code might break.
Before, you had only indexed colors, so if you changed the palette color 3 from red
to blue, all red cells would turn blue. Now, color will only change if the cell has
indexed color. You might have a cell with the exact same read but with color
specified as RGB, and this won’t change when you change the palette. It is
recommended that you use themes in situations like this now.
• Default File Format for Saving: In FlexCel 4 finding the default file format for saving was
simple; it saved everything xls. Now, it isn’t so simple. By default, if you do a simple
xls.Save(filename) without specifying a file format we will use the file extension to
determine it. When it is not possible (for example if you are saving to a stream) we will use
the format the file was originally in when you loaded it. If you prefer to change the default,
you can change the static property
ExcelFile.DefaultFileFormat
• Headers and footer changes: Now headers and footers can be set differently for odd and
even pages, or for the first page. While the properties PageHeader and PageFooter still
exist in XlsFile, you are advised to use GetPageHeaderAndFooter and
SetPageHeaderAndFooter instead for better control. Also the method to change the
images in the headers and footers changed to accept these different possibilities. Look at
APIMate to see how to use these new methods.
But autofitting is not done automatically, and we have good reasons for it to be this way.
1. Imagine that we create a new Excel File, write “Hello world” on cell A1, and go to “Format-
>Column->AutoFit Selection”. We will get something like this:
2. As you see, column “A” was resized so “Hello World” fits inside. Easy, isn't it? Well, not as
much as we would like it to be. Let's now change the zoom to 50%:
3. Now the text “Hello world” is using part of column “B”. We didn't change anything except
the zoom and now text does not fit anymore, in fact, you can autofit it again and column
“A” will get bigger.
What happened here? The easy answer is that Excel is resolution dependent. Fonts scale in
“steps”, and they look different at different resolutions. What is worse, printing also changes
depending on the printer, and as a thumb rule, it is not similar at what you see on the screen.
So, what should a FlexCel autofit do? Make column A the width needed to show “Hello world” at
100% zoom, 96 dpi screen resolution? Resize column A so “Hello world” shows fine when
printing? On a dot matrix printer? On a laser printer? Any answer we choose will lead us to a
different column width, and there is no really “correct” answer.
And it gets better. FlexCel uses GDI+, not GDI for rendering and calculating metrics, and GDI+ is
resolution independent. But GDI and GDI+ font metrics are different, for example the space
between letters on GDI+ for a font might be less than the space GDI gives to them. You will need
less column width to fit “Hello world” on GDI+ than in GDI for that font, so the width calculated
by FlexCel(GDI+) will be less than the width calculated by Excel(GDI).
As you can imagine, if we used all this space to describe the problem, is because there is not a
real solution. Autofit on FlexCel will try to adapt row heights and column widths so the text prints
fine from Excel on a 600 dpi laser printer, but text might not fit exactly on the screen. Autofit
methods on FlexCel also provide an “Adjustment” parameter that you can use to make a bigger
fit. For example, using 1.1 as adjustment, most text will display inside the cells in Excel at normal
screen resolution, but when printing you might find whitespace at the border, since columns or
rows are bigger than what they need to be.
Of course, autofit on FlexCel will work fine when printing from FlexCel, since FlexCel is resolution
independent and it should be always the same. The problem arises when opening the files in
Excel.
And this was the reason we do not automatically autofit rows (as Excel does). Because of the
mentioned differences between GDI+ and GDI (and also between GDI at different resolutions),
we cannot calculate exactly what Excel would calculate. If we calculated the row height must be
“149” and Excel calculated “155”, as all rows are by default autoheight, just opening an Excel file
on FlexCel would change all row heights, and probably move page breaks. Due to the error
accumulation, maybe in FlexCel you can enter one more row per page, and the header of the
new page could land in the bottom of the previous.
The lesson, do the autofit yourself when you need to and on the rows that really need autofit
(most don't). If you are using XlsFile, you have XlsFile.Autofit... methods that you can use for
that. If you are using FlexCelReport, use the <#Row Height(Autofit)> and <#Column Width
(Autofit)> tags to autofit the rows or columns you want.
By default, Excel autofits all rows. So, when opening the file in Excel, it will re calculate row
heights and show them fine. But when printing from FlexCel, make sure you autofit the rows you
need, since FlexCel will not automatically do that.
1. If you are autofitting a row, and a merged cell spans over more than one row, which row
should be expanded to autofit the cell? Imagine we have the following:
We could make the text fit by enlarging for example row 1, or row 5:
We could also enlarge all rows by the same amount instead of changing only one row.
FlexCel by default will use the last row of the merged cell to autofit, since in our
experience this is what you will normally need. But you can change this behavior by
changing the parameter “autofitMerged” when you call the “Autofit” methods, or, if you
are using reports, you can use the <#Autofit settings> tag to do so.
2. The second issue is that probably because of issue 1, Excel will never autofit merged cells.
Even in cases where there would be no problem doing so, for example you are autofitting
a row and the merged cell has only one row (but 2 columns). In all of those cases, Excel
will just make the row the standard height. So, if you apply FlexCel autofitting but leave
the autofit on, when you open the file in Excel, Excel will try to re-autofit the merged cell,
and you will end up with a single-size row always. So, when autofitting rows with merged
cells in FlexCel make sure you set autofitting off for Excel. You don’t need to do this for
columns, since they don’t autofit automatically in Excel.
NOTE
All the above was done in rows for simplicity, but it applies the same to columns and merged
cells over more than one column.
Make the “Height” box “Automatic, to allow your document have as many pages as it needs. You
can do this directly in Excel when using Templates to create your documents, or you can do this
in FlexCel API by setting ExcelFile.PrintToFit = true, ExcelFile.PrintNumberOfHorizontalPages = 1,
and ExcelFile.PrintNumberOfVerticalPages = 0.
This way your tables can keep their headers in every page. By the way, while you are in the
“Sheet” tab, you might want to look at the option to print the gridlines or the column and row
headings (The “A”, “B”, etc. at the top and “1”, “2”, etc. numbers at the left of the sheet)
You can do this directly in Excel when using Templates to create your documents, or you can do
this in FlexCel API by writing something like this:
xls.SetNamedRange(new TXlsNamedRange(TXlsNamedRange.GetInternalName(Internal
NameRange.Print_Titles), SheetIndex, 0, "=1:2,A:B"));
to set up the rows and columns to repeat, or set ExcelFile.PrintGridLines = true and
ExcelFile.PrintHeadings = true to set up printing of gridlines and headings.
From FlexCel API, use the ExcelFile.PageHeader and ExcelFile.PageFooter properties to set the
header and footer text. If using a template, you can just set those things in the template.
Here, the image in page 1 is being cut in the middle by a page break, so part of it appears at
page 1 and part at page 2. While this is no problem when looking at the spreadsheet in Excel, it
is clearly not what we want when printing or exporting to PDF. We want to place a page break
before that image, so it prints completely in page 2. But we don't want to put a break before
every image, since we can print many in page 2. We need page breaks that only apply when the
image does not fit in the current page.
FlexCel offers a way to deal with this. In short, you need to say which rows or columns you would
like to keep together by calling ExcelFile.KeepRowsTogether or ExcelFile.KeepColsTogether, and
once you have finished creating your file, call ExcelFile.AutoPageBreaks to paginate your whole
document and create page breaks in a way your rows and columns are kept together as much as
possible.
NOTE
The call to AutoPageBreaks must be the last before saving your file, so the document is in its
final state. If you insert rows or autofit things after calling AutoPageBreaks then the page
breaks will be moved out of place. Remember that this is not an Excel feature, so it is
simulated by dumb page breaks in Excel, and once set, those page breaks will remain at where
they were.
In this example, sheet 4 is almost empty, because there is only two lines from the current group
on it, and the next group starts at the next page.
When there is so little written in one page, you will normally want to start the next group in the
same page instead of having an empty sheet.
And you can control this in FlexCel with the “PercentOfUsedSheet” parameter when you call
AutoPageBreaks. PercentOfUsedSheet defaults at 20, which means that in order to add a page
break, the page must be filled in at least 20%.
In the example above, no page break would be made in page 4, since the 20% of the sheet has
not been used yet, and so the next group would start at page 4 too. If you set the
PercentOfUsedSheet parameter to 0% there will be no widow control, and the next group will
start at page 5 no matter if page 4 has one single line.
Setting PercentOfUsedSheet at 100% means no widow or orphan control at all, since in order to
set a page break the 100% of the page must have been used, so FlexCel has no margin to add
the page breaks. It will not be able to set the page break somewhere before the 100% of the
page, so all page breaks will be set at the last row, and it will not be able to keep any rows
together. You are advised to keep the PercentOfUsedSheet parameter around 20%
This can be problematic, since a document calculated to have breaks every 30 cm, will have a lot
of widow lines if printed in a page with an effective 29.5 cm printing area:
As you can see in the images above, reducing a little the page height might cause the last row
not to enter on that page and be printed in the next. As FlexCel added an automatic page break
after that row (so it would break correctly with the 30 cm it used for the calculation), you end up
with an empty page with only one row.
To solve this issue, the second parameter to AutoPageBreaks is the percentage of the page that
will be used by FlexCel to calculate the page breaks. It defaults at 95%, which means that it will
consider a page 30 cm tall to be 30*0.95 = 28.5 cm, and calculate the page breaks for that sheet.
So it will print correctly in a 29.5 cm sheet.
When calculating the page breaks to directly export to PDF you can keep this parameter at
100%, since FlexCel is resolution independent, and it will not have this issues. But when
calculating the page breaks to print from Excel, you need to have a smaller value here so it can
fit in all the printers where you might print this sheet. Normally if targeting only laser printers
you can have a larger value like 99%, but when other kind of printers can be used it might be
wise to lower this value.
Imagine you have a master detail report with a list of stores, then the customers of each store
and the orders for each customer. You might want to keep all customers of a store together in
one page, but if that is not possible, at least keep the orders together. You can do this by
assigning a level of “2” to the orders, and a level of “1” to the customers. The actual numbers
don't matter, you could use a level of “10” for orders and “5” for customers. The only important
thing is that one level is bigger than the other.
When you assign different levels to different rows, FlexCel will try to keep lower levels first, but if
not possible, it will try to fit at least the higher ones.
Introduction
User defined functions in Excel are macros that return a value, and can be used along with
internal functions in cell formulas. Those macros must be defined inside VBA Modules, and can
be in the same file or in an external file or addin.
While we are not going to describe UDFs in detail here, we will cover the basic information you
need to handle them with FlexCel. If you need a more in depth documentation in UDFs in Excel,
there is a lot of information on them everywhere.
So let's start by defining a simple UDF that will return true if a number is bigger than 100, false
otherwise.
We need to open the VBA editor from Excel (Alt-F11), create a new module, and define a macro
inside that module:
And then write “=NumIsBiggerThan100(120)” inside a cell. If everything went according to the
plan, the cell should read “True”
Now, when recalculating this sheet FlexCel is not going to understand the
“NumIsBiggerThan100” function, and so it will write #Name? in the cell instead. Also, if you want
to enter “=NumIsBiggerThan100(5)” say in cell A2, FlexCel will complain that this is not a valid
function name and raise an Exception.
In order to have full access to UDFs in FlexCel, you need to define them as a class in .NET and
then add them to the recalculation engine.
1. Don't use global variables inside Evaluate: Remember, the Evaluate() method might be
called more than once if the function is written in more than one cell, and you cannot
know the order in which they will be called. Also if this function is registered to be used
globally, more than a thread at the same time might be calling the same Evaluate method
for different sheets. The function must be stateless, and must return always the same
value for the same arguments.
2. As you can see in the help for the TUserDefinedFunction.Evaluate method, the evaluate
method has two arguments. The first one provides you with utility objects you might need
in your function (like the XlsFile where the formula is), and the second one is a list of
parameters, as an array of objects.
Each object in the parameters array might be a null, a Boolean, a String, a Double, a
TXls3DRange, a TFlxFormulaErrorValue, or a 2 dimensional array of objects, where each
object is itself of one of the types mentioned above.
While you could manually check for each one of the possible types by manually checking
all possible types for each parameter, this gets tiring fast. So the TUserDefinedFunction
class provides helper methods in the form of “TryGetXXX” like the
TUserDefinedFunction.TryGetDouble method used in the example.
3. There is the convention in Excel that when you receive a parameter that is an error, you
should return that parameter to the calling function. Again, this can be tiring to do each
time, so the TUserDefinedFunction class provides a
TUserDefinedFunction.CheckParameters method that will do it for you.
The only time you will not call TUserDefinedFunction.CheckParameters as the first line of
your UDF is when you are creating a function that deals with errors, like “IsError(param)”,
that will return true when the parameter is an error.
4. Do not throw Exceptions. Exceptions you throw in your function might not be trapped
by FlexCel, and will end in the recalculation aborting. Catch all expected exceptions inside
your method, and return the corresponding TFlxFormulaErrorValue when there is an error.
1. You need to define the scope of your function. If you want it to be globally accessible to
all the ExcelFile instances in your application, call ExcelFile.AddUserDefinedFunction with a
“Global” TUserDefinedFunctionScope. If you want the function to be available only to the
ExcelFile instance you are adding it to, specify “Local”. Global scope is easier if you have
the same functions for all the files, but can cause problems if you have different functions
in different files. Local scope is safer, but you need to add the functions each time you
create a ExcelFile object that needs them. If you are unsure, probably local scope is better.
2. You also need to tell FlexCel if the function will be defined inside the same file or if it will
be in an external file. This is not really needed for recalculating, but FlexCel needs to know
it to enter formulas with custom functions into cells.
There are four things you can do with formulas that contain UDFs, and there are different things
you need to do for each one of them:
1. Retrieve the formula in the cell: You do not need to do anything for this. FlexCel will
always return the correct formula text even if you do not define any udf. If the file was
calculated when saved in Excel (the default), then FlexCel will also return the correct value
of the formula.
2. Copy a cell from one place to another. Again, there is no need to do anything or define
any UDF object. FlexCel will always copy the right formula, even if copying to another file.
3. Calculate a formula containing UDFs. For this one you need to define an UDF class
describing the function and register it. You do not need to specify if the formula is
contained in the sheet or stored in an external addin.
4. Enter a formula containing an UDF into a sheet. In order to do this, you need to define
and register the UDF, and you must specify if the function is stored internally or externally.
For more examples on how to define your own UDFs, please take a look at the Excel User
Defined Functions API demo.
NOTE
Even when they look similar, UDFs are a completely different thing from Excel Built-in
functions. Everything said in this section applies only to UDFs, you cannot use this functionality
to redefine a standard Excel function like “Sum”. If recalculating some built in function is not
supported by FlexCel just let us know and we will try to add the support, but you cannot
define them with this.
The main issue with linked files is telling FlexCel where to find them.
Normally, if your files are on a disk, there should be not much problem to find a linked file. After
all, that information is inside the formula itself, for example if FlexCel finds the formula:
='..\Data\[linked.xls]Sheet1'!$A$3 * 2
inside a cell, it could automatically find “..\Data\linked.xls”, open it, and continue with the
recalculation.
1. Files might not be in a filesystem. FlexCel allows you to transparently work with streams
instead of physical files, and so you might have for example the files stored in a database.
In this case, trying to find “..\Data\linked.xls” makes no sense, and FlexCel would fail to
recalculate.
2. Much more important than 1), this approach could imply an important security risk.
Blindly following links in an unknown xls file is not a smart idea. Let's imagine that you
have a web service where your user submits an xls file, you use FlexCel to convert it to
PDF, and send back the converted PDF to him. If that user knows the location of an xls file
in your server, he could just submit a hand-crafted xls file filled with formulas like:
='c:\Confidential\[BusinessPlan.xls\]Sheet1'!A1,
='c:\Confidential\[BusinessPlan.xls\]Sheet1'!A2,
...
On return you would supply him with a PDF with the full contents of
your business plan. What is even worse, since FlexCel can read plain
text files, he might be also able to access any text file in your
server. (Imagine you are running on Linux and formulas pointing to
/etc/passwd)
NOTE
You might argue that in a well secured server your application should not have rights on those
files anyway, but on security, the more barriers and checks you add the better. So you should
have a way to verify the links inside an arbitrary file instead of having FlexCel opening them
automagically.
Because of reasons 1) and 2), FlexCel by default will not recalculate any linked formula, and just
return “#NA!” for them. Note that in most cases you will want to leave this that way, since most
spreadsheets don't have linked formulas anyway, and there is no need to add extra security risks
just because.
But if you do need to support linked files, adding that support is easy.
First thing you need to do is to create a TWorkspace object. Workspaces are collections of XlsFile
objects, and when you recalculate any of the files in a Workspace, all the others will be used in
the recalculation (and be recalculated) too.
So the simplest and more secure way to recalculate linked files is to create a Workspace, add the
needed XlsFile objects inside, and just recalculate any of the files as usual. For example:
In the example above, we opened two files and added them to a workspace, giving each one a
name to be used in the recalculation process. Note that here we don't have issues 1) or 2) at all.
We could have opened those files from a stream and it would be the same, since the name
“file1.xls” needed to calculate is actually given in the “work.Add()” method. The actual name and
location (if any) of the file “file1.xls” is irrelevant. And also we don't have the security concern of
FlexCel opening files by itself, since we opened the files we wanted, and FlexCel will not open
any more files.
Now, in some cases you don't know a priori which files you are going to need in order to
recalculate the file, and so you cannot use the approach above. In those cases, you still use the
Workspace object, but you assign an event where you load those files when FlexCel asks for
them. The code would be something like:
//Create a workspace
TWorkspace Work = new TWorkspace();
//Recalc will recalculate xls1 and all the other files used in the
recalculation.
//At the end of the recalculation, all the linked files will be loaded in
the workspace,
//and you can use the methods in it to access them.
Work.Recalc(true);
In the LoadLinkedFile event you can load the needed files, checking that they are not trying to
access folders they shouldn't be looking to. For example, remove all path information and only
load files from the same folder the original file is. Or only allow paths that are children of the
path of the main file, or maybe paths from an allowed list of paths. The choice is yours.
You can take a look at the Validate Recalc demo to see a real implementation of this. In that
example there is no validation of the loaded filenames, but just because it is an application
designed to run locally with full trust.
IMPORTANT
If XlsFile objects can consume lots of memory, Workspace objects can consume much more,
since they are a collection of XlsFiles themselves. So same as with XlsFile objects, don't leave
global Workspace objects hanging around. Set them to null as soon as possible if they are
global variables, or use local objects which are freed when going out of scope.
Take also a look at the example Recalculation of linked files for more information.
Miscellanea
Even if you do not have exposure to any C-based language, C# examples are not so difficult to
read. But there are some basic concepts that you might help you understanding them, based on
the feedback we get from our users, and we will explain them there. If you already are fluent at
C#, just skip this section.
Operators
C-based languages are a little less verbose than others when specifying operators, and this
might result a little cryptic when you are not used to them. For example, to specify an “if”
statement in C# that evaluates to true if either a and b are true or c is true you would write:
if ((a && b) || c)
if (a and b) or c then
A small list of operators you might find while reading the source code is here:
Logical Operators
OperatorMeaningExample
! Not if (!a) means “If not a”
OperatorMeaningExample
|| Or if (a || b) means “if a or b”
&& And if (a && b) means “if a and b”
Bitwise Operators
OperatorMeaningExample
~ Not ~ 1 means “not 1”
| Or 2 | 1 means “2 or 1” ( this is 3)
& And 2 & 1 means “2 and 1” ( this is 0)
^ Xor 1 ^ 1 means 1 xor 1 (this is 0)
We will not make a detailed explanation of the operators here, as the only idea here is to help
you read the examples, but you can read the full documentation at: http://msdn2.microsoft.com/
en-us/library/6a71f45d(VS.71).aspx
Automatic conversion
Many times, the easiest way to understand a code snippet in C# is just to use an automatic
translator to translate it to your language. You can find many online translators just by searching,
but we will mention one of them here: For VB.NET: http://www.developerfusion.co.uk/utilities/
convertcsharptovb.aspx
You can find it in the ASP.NET demos, but just in case you need a quick reference, here is the
code to stream an Excel file:
TFileFormats fileFormat;
if (IsXls) fileFormat = TFileFormats.Xls;
else fileFormat = TFileFormats.Xlsx;
xls.Save(ms, fileFormat);
ms.Position = 0;
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment;" + "filename=
" + fileName);
Response.AddHeader("Content-Length", ms.Length.ToString());
Response.BinaryWrite(ms.ToArray());
Response.End();
}
}
For PDF files, you would use similar code, but using StandardMimeType.Pdf instead.
WARNING
Make sure to use ms.ToArray() and not ms.GetBuffer(), since GetBuffer is not guaranteed to
return everything.
This will happen always that you save a file with formulas. We choose this as the default mode
because we can’t know which formulas your file has, and while we support over 300 Excel
functions, you might be using something we don’t support, or some VBA macro function that
you didn’t re-implement in FlexCel, or some linked files without setting up FlexCel to recalculate
linked files. So the safest default is to leave Excel to recalculate files on open, and this has the
side effect of this dialog popping up when you try to close the file.
If you know the formulas you are using are all supported, and you would like to get rid of this
dialog, you can tell FlexCel to identify the file as having been saved by a specific Excel version.
xls.ExcelFile.RecalcVersion = TRecalcVersion.Excel2016;
or if using reports:
FlexCelReport.RecalcVersion = TRecalcVersion.Excel2016;
Once you do this, any Excel version equal or older than Excel 2016 will not recalculate the file on
open, trusting the recalculation FlexCel
Newer Excel versions will still recalculate and ask for save on close.
NOTE
If you want to use the always use the latest version supported by FlexCel, you can specify
TRecalcVersion.LatestKnownExcelVersion. This value is set to the latest Excel version FlexCel
knows about, so when you update FlexCel it will update automatically without you having to
modify your code.
The other special value is TRecalcVersion.SameAsInputFile. This value means to use the value
in the file you are using as a template. Let's imagine that you have an old FlexCel version
which supports up to Excel 2013, and you had a file saved in Excel 2016. With this value,
FlexCel will identify the file as saved as Excel 2016, even if it doesn't know about it. It will just
copy the value from the input file which was in Excel 2016.
Closing Words
We hope that after reading this document you got a better idea on the basic concepts of using
the FlexCel API. Concepts mentioned here (like XF format indexes) are basic to use FlexCel, so it
is important that you get them right.
And one last thing. Remember that FlexCel API's main strength is that it modifies existing files; it
doesn't use the traditional approach of one API for reading the file and another for writing. In
fact, FlexCel doesn't even know how to create an empty file. When you call ExcelFile.NewFile, you
are really reading an empty xls file embedded as a resource. You are always modifying things.
Take advantage of this. For example, let's say you want to save a macro on your final file. There is
no support on FlexCel for writing macros. But you can create the macro on Excel and save it to a
template file, and then open the template with FlexCel instead of creating a new file with
NewFile.
Use Excel and not FlexCel to create the basic skeleton for your file. Once you have all of this on
place, modify it with FlexCel to add and delete the things you need. This is what FlexCel does
best. And of course, remember that you can use FlexCelReport for creating files, designing your
files in Excel in a visual way.
Introduction
This document is a part of a 2-part description in how to create Excel files by “reporting” instead
of by code. In this part we will look at how to set up the coded needed to create the files, and in
the next part FlexCel Reports Designer Guide we will look at how to modify the Excel file used as
a template to create the reports.
Each method has its good and bad things, and it is good to know the advantages and drawbacks
of each.
Creating a report using XlsFile is a low level approach. As with most lower level approaches, it
will be very fast to run (if coded right!), and you will have access to the entire API, so you can do
whatever you can do with FlexCelReport and more. After all, FlexCelReport uses XlsFile internally
to work its magic.
But you need to take a lot of care when coding directly in the API: XlsFile reports can be a
nightmare to maintain if not coded correctly. When you reach enough number of lines like:
xls.SetCellValue(3, 4, "Title");
var fmt = xls.GetFormat(xls.GetCellFormat(3, 4));
fmt.Font.Name = "Helvetica";
fmt.Font.Size20 = 14 * 20;
int XF = xls.AddFormat(fmt);
xls.SetCellFormat(3, 4, XF);
changing the report can become quite difficult. Imagine the user wants to insert a column with
the expenses (and don't ask why, users always want to insert a column).
xls.SetCellFormat(3, 4, XF);
to
xls.SetCellFormat(3, 5, XF);
But wait! You need to change also all references to column 5 to 6, from 6 to 7... If the report is
complex enough, (for example you have a master detail) this will be no fun at all.
But there is something much worse with using XlsFile directly. And this is that only the author
can change the report. If your user wants to change the logo to a new one, or maybe make
column C a little wider, he needs to call you and you need to recompile the application and send
him a new executable.
FlexCelReport is a higher level approach. The design is cleanly separated on three different
layers, data layer, interface layer and presentation layer, and most of the work is done on the
presentation layer, with Excel. You design the report visually on Excel, and you mess as little as
possible with the data layer. If the user wants to change the logo, he can just open the template
and change the logo. If he wants to insert a column, he can just open the template and insert a
column. And the application does not even need to be recompiled.
As with any higher level approach, it is slower than using the API directly, and there are some
things where it is more limited. But all in all, reports are really fast too and the things you cannot
do are in many cases not worth doing anyway.
So, the option is yours. For normal reports we recommend that you use FlexCelReport. Even
when it can take a little more time at the beginning that just hurry and start coding, it pays on
the long run.
Data Layer
This is the layer that contains the data model of the information we want to send out. The data
might be stored at a database, or in lists of objects in memory.
Interface Layer
This layer works as glue between the data and the presentation layers. It has to prepare the data
in a way that the presentation layer can easily consume.
Presentation Layer
This is the most complex and changing layer of the three. Here is where you design all the visual
aspects of the report, like data position, fonts, colors, etc.
The big advantage of this “layering” is that they are somehow independent, so you can work on
them at the same time. Also, Data and Interface layers are small and do not change much on the
lifetime of the application. Presentation does change a lot, but it is done completely on Excel, so
there is no need to recompile the application each time a cell color or a position changes.
NOTE
The data flow goes from the Presentation layer to the Data layer and back. It is the
presentation that asks FlexCel for the data it needs, and FlexCel that in turn asks for the data. It
is not the application that tells FlexCel the data it needs (As in SetCellValue(xx) ), but FlexCel
that will ask for the data to the application when it needs it.
On this document we are going to speak about Data and Interface layers. The Presentation
layer is discussed on a different document, FlexCel Reports Designer Guide because it is complex
enough to deserve it, and because it might be changed by someone who only understands Excel,
not .NET. And we don't want to force him to read this document too.
Data Layer
The objective of the data Layer is to have all data ready for the report when it needs it. Currently,
there are six ways to provide the data:
2. Via User defined functions: User defined functions are classes you define on your code
that allow for specific needs on the presentation layer that it can't handle alone. For
example, you might define a user function named “NumberToString(int number) that will
return “One” when number=1, “Two” when number =2 and so on. On the presentation
layer you would write <#NumberToString(2)> to obtain the string “Two”
3. Via DataSets: .NET DataSets are a collection of memory tables that can be sorted, filtered
or manipulated. Whatever format your data is, you can use it to fill a DataTable and then
use that data on FlexCel.
4. Via IEnumerable: If you have business objects that implement IEnumerable (or even
better IQueryable) and you are in .NET 3.5 or up, you can use them directly with FlexCel.
Just call FlexCelReport.AddTable(“name”, YourCollection) and you are ready to go.
5. Via Direct SQL in the template. For maximum flexibility, you can set up the data layer on
the template too. On this case, you need to add a database connection to FlexCel, and all
the rest is done on the template. This allows the users to completely edit the reports, even
the data layer from the template, but also it allows users to do thing they might not be
intended to. Use with care.
6. Via Virtual Datasets: If your business objects don’t implement IQueryable, and the
overhead of copying your existing objects to datasets is too much, you can implement
your own wrapper objects to provide this data to FlexCel without copying it. But please
take this option with care, as it might be simpler and more performing to just dump your
objects into a DataSet and use that. FlexCel has an optimized implementation of a virtual
dataset for DataSets, and if you create your own virtual datasets you will have to
implement a similar level of performance (and functionality). Please read the Appendix II
for a more detailed description of virtual datasets.
To use Linq, you just need a collection of objects. For every object in the collection, you can use
all the public properties as fields in the report. So if for example you have the following class:
class MyObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
You can add this collection as a datasource for the report with the line: FlexCelReport.AddTable
("MyName", Objs)
You can also use Entity Framework collections or any other of collection that implements
IEnumerable as datasource.
NOTE
Record count: When FlexCel runs a report from an IEnumerable datasource, it needs to know
the count of objects in advance, so it can insert the rows before filling the data. For normal
collections like List<Object> this isn’t a problem since they provide a “Count” method that will
be used by Linq and it is very fast.
For Entity Framework collections, two different SQLs will be sent to the server, once to get the
record count and the second to actually fetch the records. Other collections could have no way
to know the record count in advance, and in those cases Linq will loop over all objects in the
collection to get the count. In this third case, it will be faster if you provide the record count
yourself. Look at the Performance guide to see how you can supply the record count.
NOTE
Transactions: When you are running a report that access the database (like a report from
Entity Framework objects) you must be sure that the database isn’t updated while the report is
running.
As said in the note above, in database reports FlexCel sends two SQLs to the server for every
table, the first to get the count and the second to get the data. If when you run the second
SQL the number of records changed, you will see weird things in the report as the number of
rows inserted won’t be the same as the number of records written to the report. Also for
master detail and even a single table, you must make sure that data doesn’t change while it is
being fetched. To ensure this, the report should be run inside a “Snapshot” or “Serializable”
transaction.
Snapshot transactions are preferred if supported because they don’t block other users from
modifying the tables while the report runs. Please take a look at the Performance guide and
the Entity Framework demo for more information about transactions.
The reason why it uses more memory is because you have to fill the dataset before running the
report, loading all the data into memory. The reason it can be faster many times is because you
load all the data just once from the database, and then work from memory, avoiding further
access to the database which can be slow.
To use a DataSet in a report, just add the dataset or the tables with:
FlexCelReport.AddTable(DataSet);
As with LINQ, if there is a chance that the data can be modified while you are filling the datasets,
those datasets must be filled inside a “Snapshot” or “Serializable” transaction. An advantage of
datasets here is that those transactions will complete faster. Once the data is loaded in the
dataset, you can run the report without worries about other users changing the data, as it is all
loaded in memory.
Data Relationships
FlexCel supports Master-Detail reports, where you have a “master” datasource and a “detail”
datasource where for every master record you have different detail records. You could have an
“employee” table and for every employee, the orders they sold.
For these kind of reports, you must tell FlexCel which is the master and which is the detail. Also,
you must tell FlexCel how those two tables are related; for example, both tables could be related
by an “EmployeeId” field that is used to know which orders correspond to which employee.
Implicit Relationships
When the individual objects inside a collection of objects contain a public property that is itself a
collection of objects, the second collection is implicitly a detail of the first. For example, if you
have the class:
class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Order> Orders { get; set; }
}
Then “Orders” is an implicit detail of “Employee” and when you create a Master-Detail report,
FlexCel will output the correct orders for every employee.
Explicit Relationships
Sometimes (as it happens with DataSets) you don’t have implicit relationships in your data. You
might have two different Lists of objects:
List<Employee2> Employees;
List<Order2> Orders;
class Employee2
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Order2
{
public int EmployeeId { get; set; }
public int OrderId { get; set; }
public string OrderName { get; set; }
}
And related by an Id (in this case EmployeeId). If you were to create a report out of those two
lists, you would get the full list of orders for every Employee. You need to tell FlexCel that the
“Employees” collection is related to the “Orders” collection by the EmployeeId.
FlexCelReport.AddRelationship(...)
But you will almost never need to do so. FlexCel automatically loads all existing Data
Relationships in datasets, so if the tables on your dataset are related, then you don’t need to do
anything else.
For other objects different from datasets you will probably want to use implicit relationships. But
there might be cases where you need explicit ones. Explicit relationships are more powerful than
implicit, in the sense that you can have the same tables related in different ways. For example,
The “Orders” collection could have also an explicit relationship with a “Date” table. If you run the
report “Orders by Date” then that relation will be used. If you run the report “Orders by
Employee” then the employee relation will be used. If you had implicit relationships, then both
“Date” and “Employee” collections would need an “Orders” field, and that could lead to repeated
data.
Explicit data relationships can be defined with any objects, even between DataSets and and LINQ
collections. Whenever FlexCel finds an explicit Data Relationship, it will filter the detail table to
show the correct records for the master.
TIP
You might also define the relationships directly in the template instead of by code. This is
useful when using Direct SQL, since the tables are defined directly in the Excel template and
you can’t set up the relationships in code. Also, you can add custom relationships not related
with datasets, when using virtual datasets.
Interface Layer
This is the simplest of the three, as all work is done internally. To setup the interface layer, you
only need to tell FlexCel which datasets, tables and user functions it has available from the data
layer. For this, you use “SetValue”, “SetUserFunction”, “AddTable” and “AddConnection” methods
on FlexCelReport. Once you have told FlexCel what it can use, just call FlexCelReport.Run and this
is all.
NOTE
It is worth spending some time thinking about what tables, functions and variables from the
data layer you want to make available for the final user. Limiting it too much might mean
cutting the power of the user to customize his report. And just adding everything might be too
inefficient, since you are loading thousands of rows the user is not going to use, and might
also have some security issues, as the user might get access to tables he is not expected to.
You can also use SQL on the templates to allow maximum flexibility, but then you need to be
extra careful about security.
As explained earlier, the easiest way to provide data to FlexCel is via IQueryable collections or
DataSets. Both methods are fast, optimized, with lots of added functionality, like data
relationships, filtering or sorting, and if you are experiencing issues with them, you are probably
better using them.
But you might want to use your own objects directly, and they might not be stored in any of
those containers.
You can do this by writing wrapper objects around your data, implementing the abstract classes
**** and ****. In fact, standard IQueryable collections and Datasets also interoperate with FlexCel
by implementing those classes.
NOTE
If you are curious and have FlexCel source code, you can search for TLinqDataTable,
TLinqDataTableState, TAdoDotNetDataTable and TAdoDotNetDataTableState to see how
they are implemented.
1. On one side we have . It is a “stateless” container, much like a dataset. Each virtual dataset
corresponds with a table on the data layer that you would add with “
FlexCelReport.AddTable”, or create by filtering existing datasets on the config sheet.
2. On the other side we have . Each VirtualDataTableState corresponds with a band on the
presentation layer, and if 2 bands share the same data source they will have 2 different
VirtualDataTableState objects associated (but a single shared VirtualDataTable).
This is probably easier to visualize with an example. Let's imagine we have the following report:
FlexCelReport.AddTable(Customers);
FlexCelReport.AddTable(Company);
FlexCelReport.Run();
There are two VirtualDataTables here (Company and Customers), and three
VirtualDataTableStates (One for the Company Band, one for the first Customers Band and one
for the second Customers Band)
WARNING
Take note that the same VirtualDataTable is used by two different VirtualDataTableStates, and
might be used by other VirtualDataTableStates in other threads.
This is why you cannot save any “state” information on the VirtualDataTable, and if you write
to any private variable inside of it (for example to keep a cache) you should use locks to avoid
threading issues.
Always assume that some other class might be reading your data.
VirtualDataTableState on the other hand is a simple class that will not be accessed by more than
one class at the time, and you can do whatever you want inside it without worries of other
threads trying to access it.
Those methods define the “columns” of your dataset, and the fields you can write on the
<#dataset.field> tags on the template.
You need to implement this method if you want to provide the user the ability to create
new datasets by filtering existing ones on the config sheet. If you do not implement it, any
attempt to create a filtered dataset on the config sheet will raise an exception.
Also when FlexCel needs to create a dataset that is a copy of the existing one (for example
for sorting it) it will call FilterData with rowFilter null. So even if you don’t implement
filtering, it is normally a good idea to at least implement the cas for “rowFilter” = null and
return a clone of the datatable.
Note that this only applies to standard filters. For <#Distinct()> or <#Split> filters you do
not need to implement this.
Implement this method if you want to let your user write <#Distinct()> filters on the
config sheet.
• LookUp: Will look for a record on the dataset, and return the corresponding value. This
method allows the user to use <#Lookup()> tags on their reports.
• GetRelationWith: Use this method to return implicit relationships between your data
tables. For example the VirtualDataTable implementation of Datasets uses this method to
return the ADO.NET DataRelations between datasets. The Linq implementation returns as
related any nested dataset.
• GetValue: Here you will finally tell FlexCel what is the value of the data at a given row and
column. You can know the row by reading the “Position” property, and the column is a
parameter to the method.
As with RowCount, this method should return the records on a specific state, not all the
records on the datatable. If you are in a master-detail relationship and only two records of
detail correspond the master position, GetValue(position = 0) should return the first
record, and GetValue(Position = 1) should return the second. It doesn't matter if the total
number of records is 5000.
You should probably create indexes on the data on the constructor of this class, so
MoveMasterRecord can be fast finding the information it needs.
You do not need to implement this method if the VirtualDataTable is not going to be used
on Master-Detail relationships or Split relationships.
• FilteredRowCount: This method returns the total count of records for the current state
(similar to RowCount), but, without considering Split conditions.
If the dataset has 200 records, of which only 20 apply for the current master record, and
you have a Split of 5 records, RowCount will return 5 and FilteredRowCount will return 20.
This method is used by the Master Split table to know how much records it should have.
In the last example, with a FilteredRowCount of 20 and a split every 5 records, you need 4
master records. You do not need to implement this method if you do not want to provide
“Split” functionality.
EOF: Return true if the dataset is at the last record. If you don’t implement this method, the
default implementation is to check Position == RecordCount. But RecordCount might be
expensive to calculate, and in this case, if you explicitly implement this mehtod, it will work faster.
Finally
When you have defined both classes, you need to create instances of your VirtualDataTable, and
add them to FlexCel with FlexCelReport.AddTable.
For examples of how the whole process is done, please take a look at the Virtual DataSet demo.
Introduction
This document has two different target audiences, developers using FlexCel and final power
users that want to customize their reports. It covers how to design an Excel template, but not the
code parts needed to run a report. Read the Reports Developer Guide for the code part.
Report Elements
There are three concepts you should understand to create or modify a report: Tags, Named
ranges and the Configuration sheet. We will be covering all of them in the sections below.
Tags
A tag is text that you write in a cell and that will be replaced by a different value on the
generated report. All tags are on the form <#TagName> when they don't have any parameters,
and <#TagName(param1;param2...)> when they have parameters.
NOTE
Notice that the parameter separator is “;” not “,” as it is on expressions. This was done so it is
simpler to mix formulas in tags.
Tags are case insensitive, so you can write <#tag>, <#TAG> or <#Tag> as you prefer. The
convention we usually use is all lowercase, but it is up to you.
You can write multiple tags on the same place, and the result will be the concatenated string.
You may also apply different format to different tags. For example, writing “<#value1> and
<#value2>” inside a cell will be replaced by something similar to “1 and 2”
Tags will be replaced on Cells, Comments, Sheet names, Images, Hyperlinks, AutoShapes,
Headers and Footers.
Tag Reference
The complete list of tags you can use and their descriptions is on the Reports Tag Reference.
Evaluating Expressions
Expressions can be used inside <#If> and <#Evaluate> tags. They behave like standard Excel
formulas, and you can use any formula that FlexCel can calculate. But, different from formulas,
you can also enter tags inside expressions.
NOTE
As you might recall from the previous note, the parameter separator inside tags is ";". But, the
parameter separator in Expressions is “,” , not “;” This is to keep the expressions syntactically
compatible with Excel, and also to allow you to mix tag parameters with expression parameters
without issues.
The supported list of things you can write inside an expression is detailed on the following table:
Named Ranges
While Tags allow you to replace complex expressions inside a sheet, with them alone we can only
create “Fill in the blanks” type reports. That is, reports that are static, like a form, and where cells
with tags will be replaced with their corresponding values.
Now we are going to introduce the concept of “Band” A Band is just a range of cells that is
repeated for each record of a table. Imagine that you define a Band on the cell range A2:C2, and
associate it with the table “Customer”. Then, on cells A2:C2 you will have the first customer, on
cells A3:C3 the second and so on. All cells that were previously on A3:C3 will be moved down
after the last record of the dataset.
If table customer has six registers, and you have a template as follows:
After running the report you will get something like this:
On FlexCel we use Named Ranges to indicate the bands. If you are not used to Excel Named
Ranges, take some time to familiarize with them, as they are one of the things that can be a little
confusing when starting with FlexCel. Different from tags, that you can immediately see when
you open a workbook, named ranges are a little more hidden.
To create a Band on A1:C1, we would go to the “Formulas” tab, then choose “Name Manager”:
Once there, we can define a Band __Customer__ on cells A1:C1. And once the name is defined,
we can easily see it on the Names combo:
Note the “__” at the beginning and at the end of the range name. We use this to indicate FlexCel
that this is a horizontal range that inserts full rows down.
The rest of the name (Customer) should be the name of an existing data source (table, array of
data, etc), or a custom table defined on the config sheet.
Range Types
You can define four different kinds of bands on FlexCel:
• “__” Range: This range moves down and inserts full rows. For example, to define a band
that inserts full rows down for each record of the dataset “Customer” you would use a
name __Customer__
• “_” Range: This range is similar to “__” but cells outside of the range won't move down. To
define a band that inserts only a range of cells down for each record of the dataset
“Customer” you would use a name _Customer_
• “II_” Range: This range moves to the right and inserts full columns. Note that the first
character is the letter i, not a pipe (|). To define a band that inserts full columns for each
record of the dataset “Customer” you would use a name II_Customer_II
• “I_” Range: This range is similar to “II_” but cells outside of the range won't move right. To
define a band that inserts cells to the right for each record of the dataset “Customer” you
would use a name I_Customer_I
A “__” range is the same as a “_” range defined on the full rows, and the same is valid for “II_” and
“I_” ranges.
On the following example, if you name A1:D7 as “_Customer_” Cell E8 won't move when inserting
down. If you name it as “__Customer__” Cell E8 will move to the last inserted cell, because a
“__Customer__” range is equivalent to a “_Customer_” range on A1:XFD7.
Master detail
Named ranges can be placed inside others, and a master detail relationship will be automatically
created. For example, if you define a range “__Customer__” and inside it a range “__Orders__” and
there is a relationship created on the application between “Customer” and “Order” tables, it will
automatically group your orders by customer.
On the following example, the yellow cells are the range “__Customer__” and the blue ones are
the range “__Orders__”
After running this report, you will get something similar to:
As you can see, Orders are filtered for each customer, based on the Data Relationship defined
on the application, and on the nesting on the ranges. In general, any range that is inside another
is filtered by all of its parents. You can have as many levels of master-detail relationships as you
wish, and each master band filters all of its children.
For example, if we wanted to group the customers by country we could define a __Countries__
named range on A1:F6, and it would automatically filter the data on its child and grand-child.
(Customer and Order)
There is a special Table that if present filters all the others on the sheet, acting as a parent of all
the named ranges on the sheet. This is the table that you define on the name of the sheet, when
doing a multiple sheet report. You can see the multiple sheet report demo for more information:
there every table on each sheet is filtered by category.
Bidirectional Bands
As a general rule, bands in FlexCel can’t intersect. You can have separated bands:
And they will each expand separately. You can also have one band completely inside another:
And the inside band will be a detail of the outside band. But if the bands intersect:
It is not really possible to do a report with this data. There is no master and no detail here, and if
we run those two bands separately, one band would overwrite the results of the other. It is not
possible to know if cell C3 should have data from Band 1 or Band 2.
So in general, FlexCel will raise an error if you try to do a report with bands that intersect: This is
most likely a mistake in the template.
But there is one special case where FlexCel allows you to have two bands that intersect: If the
bands form a cross, and the vertical leg of the cross is a column range, while the horizontal leg is
a row range:
This will create a bidirectional band, that grows both to the right and to the bottom at the same
time. The conditions for creating a bidirectional band are:
2. The bands must form a cross. This means that the top of Band 2 must be smaller or equal
than the top of Band 1, the bottom of Band 2 must be bigger or equal than the bottom of
Band 1, the left of Band 1 must be smaller or equal than the left of Band 2, and the right of
Band 1 must be bigger or equal than the right of Band 2. You will normally make Band 1 a
full row range and Band 2 a full column range.
If both conditions are met, FlexCel will create a master detail report where Band 1 is the master
and Band 2 the detail. For bidirectional bands, FlexCel will always make the horizontal band
the master and the vertical band the detail.
As the vertical band is the detail, you can have multiple vertical bands for the same horizontal
band master. That is, you can have something like this:
And both Band 2 and Band 3 will be details of the master Band 1.
What you can’t currently have is two row bands intersecting the same column band:
In this case, as FlexCel will always make the vertical bands the detail, Band 2 should be the detail
of both Band 1 and Band 3. And while a master band can have many details, a detail band can
have only one master.
A workaround if you really need such a thing would be to use <#Includes>. You could have two
subreports: one with Band2 and Band1, and the other with Band3 and Band2. Then in your
master report, you <#Include> both subreports one below the other. The effect will be as if you
had run multiple horizontal bands with Band2, but there won’t be multiple masters.
For more information on Bidirectional Reports, you can look at the “Bidirectional Reports”
example.
“X” ranges
One issue that might appear when defining named ranges is how formulas on other ranges
change when inserting the new cells.
Let's imagine we want to make a simple report on a list of items and their prices.
So, we create a new template, define a title row, and insert a named range on A2:B2 to
propagate the data. But we also want to know all the total cost of all items, so we add a formula
on B3:
When you run this report, rows will be inserted between row 2 and 3, but the formula Sum
(B2:B2) won't change. Nothing has been inserted between B2 and B2, so the sum range will
remain constant.
So, we need to have a Sum range that can expand. We will define:
Now, when rows are inserted between row 2 and 3, the formula will be updated to reflect the
new rows.
On this particular case, this solution might be enough. Just leave an empty row after the range
so formulas expand, and then you can hide the extra row or just leave it there.
But, if we were for example creating a chart, this extra row will be on the chart too. If you are an
old FlexCel VCL user, you know about the “...delete row...” thing just to avoid those cases.
Well, delete row does not work anymore on FlexCel 5.0 and up, because the row would be
deleted before the range is expanded, and the formula will then point to B2:B2 again.
This is why we introduced the “X” ranges. X ranges are normal named ranges with an “X” at the
end. On this case, instead of “__Item__”, we would call the range “__Item__X” It will behave
exactly the same as a normal range, but once it is expanded it will erase the last row (or column
if it is a column range). So if we try the last example with “__Item__X”, row 5 on the last
screenshot will be deleted, and the formula would be “=SUM(B2:B4)”. Just what we were looking
for. See the chart demo for more information on using X ranges.
Fixed Bands
By default, FlexCel will always insert cells when expanding ranges, and this is what you would
normally want. If you have a template:
A1:Title
A2:<#data>
A3:Footer
You would expect that the generated report will have the Footer for example on cell A33 (if we
had 30 data records), but not on A3.
But there is a situation where this is not what you expect, and this is on Fixed Form reports. Let's
imagine that you want to fill out a form with FlexCel. Most fields will be just simple expressions,
not related to datasets, but we might have a table too:
Here, no matter if the dataset has 1 record, two or 10 (it should not have more than 10) you want
the “Total” line to be at row 23. You cannot do this with normal ranges, since you would be
inserting rows. For this you can define a “__TopOrders__FIXED” named range, which will not
insert any records. See the “Fixed Forms with Datasets” demo for more information.
You can get this by using a “FixedN” range, where “N” is the number of rows or columns you
want fixed. For example “__data__FIXED2__” will overwrite the first 2 rows, and insert
(data.RecordCount – 2) rows for the rest.
You can see an example of FixedN bands at the “Balanced Columns” demo.
Balanced Columns
Sometimes you might want to create a report that once generated looks like the following:
But if you try to do this by creating a _Block1_ and _Block2_ parallel ranges, you will actually end
up with something like this:
Because when _Block1_ and _Block2_ grow down, the cells are inserted only in the columns that
are used by those ranges.
But there is a case where FlexCel doesn’t work this way, and that is in master-detail. When you
are doing a detail, cells are inserted in all the columns so they end at the same place, and the
next master record won’t be broken. So the solution in this case is to create a dummy “master”
dataset with a single row, and use it as a master for both _Block1_ and _Block2_ ranges.
The last cell in every column of the “master” dataset will be copied down so all columns inside
the master insert the same number of cells.
You can use the “ROWS” function in the config sheet to create a single row datasource.
Please take a look at the “Balanced Columns” demo for more information on how this is done.
FlexCel will try to keep rows or columns on those ranges together when printing by inserting
page breaks at the needed places.
KEEPROWS_<Level>_<Whatever>
Where <Level> is the level of “keep together” of the group, and “Whatever” is anything, you
can use it to have more than one “keeptogether” range in the same sheet.
KeepRows_1_customers
and
KeepRows_1_orders
in the same sheet, to tell FlexCel to group the rows in both ranges together when printing. You
might also have different levels of “Keep together”, and you will normally use higher levels for
details in master-detail reports.
Once you have created the ranges, you need to write an <#auto page breaks> tag somewhere in
the sheet, and FlexCel will add page breaks when it ends the report trying to keep those ranges
together. You can customize the <#auto page breaks> to influence the way page breaks are
created.
We are not covering in detail Intelligent page breaks here, since they are described in the API
guide, and it makes no sense to repeat that information here. We only cover here what is
different for reports, and this is the “KeepTogether” ranges.
Make sure you read the section Preparing for Printing in the API guide, since most of the
concepts apply also to reports. And of course take a look at the Intelligent Page Breaks in
Reports demo.
The most important difference is that the final pagination must be done in the master report, not
in the included one. Let's look at an example:
In this example, we can have “KeepRows_” and “KeepColumns_” ranges in the included report,
but we should not create the actual page breaks in it. If we did it, those page breaks would be in
the wrong position when inserted in the master. (unless the report is included at cell A1). As you
can see in the drawing, the included report is copied into the master at a lower location. So if the
report goes down, the page breaks will go down too, ending up at the wrong places. Excel needs
to insert its own page break (marked with a red dotted line) in order to have the master page no
bigger than the paper size, and the old page break will create a small page that we don't want.
So, in order to have intelligent page breaks in included report, you must follow the next simple
rules:
• Create the “KeepRows_” and “KeepColumns_” ranges in the included report, as you would
normally do it.
• Do not write an <#auto page breaks> tag in the included report. This will ensure
FlexCel does not add the page breaks in the included report before copying it to the
master.
• Make sure you include full rows (by using “__” or “II_” as parameter in the include tag). This
way, the KeepRows and KeepColumns ranges will be copied to the master.
• Write an <auto page breaks> tag into the master. This way, when the master report is
finished, FlexCel will paginate the master, keeping together the rows you marked in the
included report. Pagination must be done on the master, never in the included report.
Configuration Sheet
The configuration sheet is a repository where you can configure different things on your report,
define common used expressions and so on. It is not required, you can create a report without a
configuration sheet, but it is recommended that you have one except for very simple reports.
NOTE
If there are any macros assigned to the configuration sheet, FlexCel will be unable to delete it
and will just clear and “Very hide” the sheet. (Very hidden sheets are similar to hidden sheets
but the user needs to write a macro to see them, they are not listed when you select to unhide
a sheet from Excel). If you get a macro by mistake on the config sheet and you are unable to
delete it in Excel, use the “MacroCleaner” tool included on the “Tools” folder.
The layout of the config sheet is free as long as you keep the positions, you can write the
captions you want or change any format you need. Just one advice: keep it simple. The config
sheet will be deleted from the final report, so the final user won't see it. And deleting a sheet
with comments, images formulas and so on is slower than deleting a simple sheet.
2. Cells A10 to C10 and all rows below (A11:C11... etc) are used to define new custom tables.
You use a master table defined by the application (B10), and define a new name for it
(A10). Then you can write a filter (Look at the “Filtering Data” section in this document).
And also sort the table by some columns. Different search columns are separated by “,”,
and you can also define an “ASC(ending)” or “DESC(ending)” order. For example: “State,
ZipCode DESC” Again, any Sort expression valid for a DataView is valid here. On some
places you can use tags. For example, if you write on C10 “<#mytag>” instead of
“ProductId=7” the value of mytag wil be used as a filter.
3. You can leave empty rows. For example, you can define a new table on row 10 and
another on row 12 without writing anything on row 11. This allows you to better separate
your tables.
4. On columns H and I you define custom formats. The name of the format goes on column
H, and the definition on column I. Then you can use those cell formats on <#format> tags.
5. You can write whatever you want on column I, the text is not important and you can use it
to know how the format will look like.
6. Column K is used to list the report variables. It is not used at all by FlexCel and you don't
really need to fill it, but it is used by FlexCel designer to list the available report variables. If
you do not write them here, FlexCel designer will not show them.
7. Columns M and N are probably the most important on the config sheet and are used to
create reusable expressions.
About Expressions
It is recommended that whenever you have complex chains of tags, instead of writing them
directly on the cell, you create an expression and then refer to it.
On the screenshot, we defined a <#order> tag that itself calls other report expressions to
calculate its value. Then, if you want to write the result of order on cell A1, instead of writing the
full chain of tags, you just write <#order> On some places, like for example the name of the
sheet or an image name this might be the only choice, as the name length is limited to 32
characters. You can use also parameters on Expressions, and for Example define <#order(row)>
For more details on how to do this, see the Expressions with parameters demo.
For example, you might format I10 with blue background, name it “Header” in cell H10, and then
use the format inside a <#format cell(Header)> in the template. But this will apply the full
format in cell H10 to the new cell. This means that besides the blue background, all other
properties will be copied too. The new cell will also have the same font, same alignment, same
numeric format than H10.
Now let's imagine we have a full row that we want to be blue if some condition applies. If we
write <#format row(header)> in a cell on the template, then all other attributes besides the
background will be applied to the row. This might mean that all the cells will be aligned to the
left (if cell I10 was aligned to the left), and this is not what we want. We want to apply only the
background color on I10, keeping everything else as it is defined on the template.
To do this, you can define partial formats. You define a partial format by adding one or more
attributes to the name of the format. For example, if you name the format Header
(background;font.color) only the font color and the background color of cell I10 will be
applied, not the full format.
You can add as many properties as you want to be applied for the format separating them by
semicolons (“;”). Also, you can use negative properties by preceding them with a “-” sign. For
example, the following definition: Header(All; -Border; -Font) will apply all attributes in cell I10
except the border and the font.
Name Description
Applies the all the formats in the cell. Defining a format named “header” is
the same as defining one named “header(All)”. <br/ >
All You will normally use the All format when excluding formats. For example, “
header(All, -Border)” will apply all the formats except the border. <br/ >
“header(-Border)” would not apply any format.
Applies the four borders. “header(Border)” is the same as “header
Border
(Border.Left;Border.Right;Border.Top;Border.Bottom)”
Border.Left Applies the left border.
Border.Right Applies the Right border.
Border.Top Applies the Top border.
Border.Bottom Applies the Bottom border.
Name Description
This is a special setting, used to apply the borders only on the outer
bounds of the range. For example, if fmt is defined as “fmt(border;
border.exterior)”, and you write <#format range(a1:c5;fmt)> then the
top row of the range (row 1) will be formatted with the top border format
of the cell, the left column or the range (column a) will be formatted with
the left border format of the cell, and so on. Inner cells in the a1:a5 range
will not have any border applied. <br/ >
This tag alone has no effect; you always need to use it together with other
border tag. For example “fmt(border.exterior)” will not do anything, you
Border.Exterior
need to write “fmt(border; border.exterior)”, “fmt(border.top;
border.bottom; border.exterior)” or something similar.
Note that this tag only applies to the <#format range> tag. It makes no
sense in <#format cell>, and you cannot use it in <#format row/column>
since format for columns and rows does not support this.
Applies all the font properties. “header(Font)” is the same as “header
Font
(Font.Family;Font.Size;Font.Color;Font.Style;Font.Underline)”
Font.Family Applies the font name.
Font.Size Applies the font size.
Font.Color Applies the font color.
Applies the font style (bold and italics) Underline in not included in
Font.Style
Font.Style.
Font.Underline Applies the underline.
NumericFormat Applies the numeric format of the cell.
Applies the background color of the cell. “header(Background)” is the
Background
same as “header(Background.Pattern;Background.Color)”
Background.PatternApplies the fill pattern for the cell.
Background.Color Applies the fill color for the cell.
Applies the horizontal and vertical alignment in the cell. “header
TextAlign
(TextAlign)” is the same as “header(TextAlign.Horiz;TextAlign.Vert)”
TextAlign.Horiz Applies the horizontal alignment in the cell.
TextAlign.Vert Applies the vertical alignment in the cell.
Locked Applies the “Locked” attribute in the cell.
Hidden Applies the “Hidden” attribute in the cell.
Name Description
TextWrap Applies the Word Wrap setting for the cell.
ShrinkToFit Applies the Shrink to fit setting.
Rotation Applies the Rotation.
TextIndent Applies the indent.
IMPORTANT
You need to specify what the format applies to in the format definition, but not when calling
the format. You might define a format as header(border), but you need to call it with
<#Format Cell(header)>, not <#Format Cell(header(border))> The second way will not work.
With partial formats you can apply the different formats as “layers” in your document, one
independent from the other. You can apply as many <#format cell> and <#format range> tags
as you want in a cell, as long as the applied formats are different all of them will be applied.
WARNING
A last word of caution: As powerful as custom formats are, remember that they are only
useful if you want to conditionally format cells. If the format is static, just format the cell as you
want it. We have seen too many customers “over engineering” reports with lots of format cell
tags, when the simplest solution would be to format the cells directly in the template with
Excel and not use any tag.
Also remember that you can use Excel's conditional formatting in almost every case you
could need custom formats. But Excel's conditional formats are much more simpler to use an
maintain. So for example, if you wanted to paint a cell red when it is bigger than 100, you
could define a custom format Warning(Background) and then use a <#if(<#cell> >
100;<#format cell(Warning)>)> or you could just set a conditional format in the cell in the
template making it red when it is bigger than 100.
FlexCel fully supports Excel conditional formatting, and you should always prefer to use that if
possible.
You can see an example on how to use custom formats to have alternating rows in the “Mutiple
Sheet Report” demo.
NOTE
As an alternative of defining the formats in the config sheet, you could also define them as
User Defined Formats and add them with FlexCelReport.SetUserFormat. For an example of
using user defined formats, look at the "User Defined Formats" demo.
Filtering Data
You can filter the data you are going to use directly in the template by writing a filter in the
config sheet:
Here you must be aware that generally filtering is done locally: that is, we fetch the full table
from the database, and then apply the filter to discard the unwanted data. This makes Filters
inefficient except for small tables. In general, you will want to filter in the SQL, not in the config
filter column as a way to avoid fetching all records from the database. But sometimes when you
can’t modify the data, and there aren’t too many rows, filtering in the config sheet can be an
option.
NOTE
When using LINQ to SQL as datasources then filtering is done in the server, not in the client.
The data fetched from the server will only be the data we need, not the full dataset. This is
because LINQ delays sending the SQL to the server to after the filters have been applied, and
so it sends the correct filtered SQL.
This makes filtering with LINQ to SQL very efficient, and not different from filtering directly in
the server by changing the SQL.
Filter Syntax
The exact syntax of the expression you have to write in the “Filter” cell depends on the
technology you are using:
1. DataSets: You can write any filter expression available on a DataView, for more info search
at the DataView.Filter documentation on the .NET framework documentation.
2. LINQ: When using LINQ, FlexCel provides two different syntaxes for the filters:
◦ Simple built-in parser: By default, any expression you write in the “filter” cell will be
parsed by a simple built-in parser in FlexCel. The syntax of the built in parser is
described in the section Built-in parser syntax below.
◦ Linq “Where” filter: If you are using Entity Framework or any other technology that
includes “Where(string)” extension methods that allow to send direct SQL “where”
strings to the server, you can write those strings in the filter, by adding an “@”
before the string. For example: “@it.Field1 like '%test%'” When using this filter
style, you need to prefix all fields with the table name. By default, Entity Framework
uses “it” for all table names, and that’s why we used “it.Field1” in the example.
• The operators you can use in a simple filter are, by order of precedence (lower to greater):
◦ FieldName, or [Field Name]: Use the square brackets when there are spaces in the
name.
◦ ‘Strings’: Use single quotes for strings. To enter a single quote inside the string,
repeat it twice, as in “Field1 = 'I''am me'”
◦ Numbers: You should use “.” for floating point numbers, no matter what your
locale is. For example: “Field1 >= 1.5”
◦ SQL92 Date Literals: You can use either DATE 'yyyy-MM-dd', TIME 'HH:mm:ss',
TIMESTAMP 'yyyy-MM-dd HH:mm:ss'. For example, Birthday = DATE '1972-09-08'
◦ OLE Date Literals: Ole date literals use the syntax: {d 'yyyy-MM-dd'}, {t 'HH:mm:ss'},
{ts 'yyyy-MM-dd HH:mm:ss'}. For example, Birthday = {d '1972-09-08'}
◦ Booleans: You can use the constants “true” and “false”, as in “Married = true”
◦ null: This is used for null values, and it uses the .NET definition of a null (where null
== null), not the SQL definition (where null <> null). This means that different from
SQL, you can use this filter: “Field <> null” and it will work as expected.
◦ YEAR: Returns the year of a date. For example you could have a filter: Year(field1) =
1999
Grouping Tables
Sometimes you might have the data for a master detail report into one table, and you want to
create two different tables based on the value of a key field.
You can use the “DISTINCT” filter on the config sheet together with the “RELATIONSHIP” tag to
get this effect. Look at the “Master detail on one table” demo for more information on how this
is done.
NOTE
Whenever possible, do not use this grouping as the normal way to get data. While it simplifies
the data layer, it also fetches a lot of repeated information from the database.
key field-from-table1field-from-table2field-from-table2
table1.value1table1.value2 table2.value1 table2.value2
table1.value1table1.value2 table2.value3 table2.value4
table1.value1table1.value2 table2.value5 table2.value6
On this simple case, you fetched 12 values from the database. If you had made 2 different
selects, you would have fetched only 8 values (table1.value1 and table1.value2 for the first table,
and table2.valueN for the second). Depending on the amount of data, there might be a lot of
repeated fields on the join.
Splitting Tables
The same way sometimes you might want to group tables, other times you might want to split
them in groups of n records. For example, you might want to create a 5 column report, and you
need to split the master dataset in groups of 5 records in order to fill the columns.
This is where the Split(source, number of records) tag can be useful. In short, you write this tag
on the “Source Name” column in the config sheet, specifying the table to split on the “source”
parameter, and how many records you want on each group on the “number of records”
parameter. This will create a new table that you will name on the “Table Name” column of the
config sheet. You can then use this new table as master on a master-detail relationship with the
original table. The generated master has no columns, but you can use the pseudocolumns
(#RowPos or #RowCount) just fine. Each record of the master is related to “number of records”
records on the detail.
Note that the generated master table is a “pseudo table” n the sense that it has no columns or
data, but it has (DetailRecords.Count – 1) / NumberOfRecords + 1 rows. Also the relationship
between the master and the detail is not on real columns, since there are no columns on the
master. This creates a limitation on how you can use those tables, and it is that the master
should have the detail as a direct child. You cannot have other __ range__ between them, or
FlexCel will complain.
WARNING
Be careful with this tag. If your table has 10,000 records and you only need 10, fetching them
all from the db in order to use only 10 is not a smart idea. Take a look at the Fixed Forms With
Datasets demo for more information.
You might do this with the AtLeast(source, minimum number of records, [default value]) tag.
In a way, this is the opposite of the ]TOP(…) tag discussed above.
For example, you might define the following in the config sheet:
In this example, the table detail_1 will have at least 1 record. If detail is empty, as we didn’t
specify a default value, it will return a record with all null values. The table detail_5 will have at
least 5 records, and if detail has less, all records after the detail record count will return "---" in all
their fields.
NOTE
You can’t specify a default value for every field; it will be the same for all of them. But you can
use some <#if(detail_5.field="---";something;else)> tag in the template to provide different
values.
Joining Tables
When you have a “full row” report like the one in the image below, we insert a row for each
record in the table.
As you would expect, if you have a merged cell (in green) below the range being inserted (in
blue), the merged cell will move down too:
And the same will happens with ranges of cells like Print_Area or others. We are inserting full
rows, so everything moves down.
But now, let’s imagine we have 2 independent ranges we want to grow in parallel. We can do it
with “-“ ranges instead of “__” ranges:
In this case, when the blue range runs, the cells will only be inserted in columns A and B.
But as a side effect of this, the merged cell will be broken (as you can see in the image above).
After the blue range runs, the orange range will run and the cells will move down in column C
too:
And now the merged cell is all green again (as expected), but it isn’t a full merged cell anymore:
It is a merged cell in A:B and a different cell in C. Something similar happens with names, when
the blue band grows it can’t grow a name which goes from column A to C, because not all cells
in that range have moved. And when the orange range grows, the name can’t grow either since
now columns A and B aren’t moving.
There is no real solution to this: Due to the order the bands run, having two parallel _ ranges isn’t
the same of one big __ range. Merged cells might break and ranges might not update if they
span over the two _ ranges running.
One solution is of course not to put any merged cell below the _ ranges which are not fully
inside the columns of one of the _ ranges. And do the same with names.
But there is another solution, which is to “Join” the datasets inside “some range” and “some
other range” so they form a bigger dataset with the columns of both, and then run a single full
row report in the bigger range.
Say for example that you want to run two parallel reports on the tables:
Customer
NameCustomerID
... ...
And
Employee
NameEmployeeID
... ...
In the config sheet, you could define a new table “CustomerAndEmployee” as JOIN
(Customer;Employee)
CustomerAndEmployee
Customer.NameCustomer.CustomerIDEmployee.NameEmployee.EmployeeID
... ... ... ...
And you can use this new CustomerAndEmployee table to run the report.
Things to note:
1. The table “CustomerAndEmployee” has as many rows as the maximum of “Customer” and
“Employee”. Say for example that you have 3 customers and 5 employees, then
CustomerAndEmployee will have 5 records. The last 2 records of CustomerAndEmployee
will have Customer.Name and Customer.CustomerID null.
2. If “Customer” and “Employee” have columns with the same name, you will need to prefix
them with the table name in order to show them. In our example, both Customer and
Employee have a “Name” column. So you can’t just write
“<#CustomerAndEmployee.Name> inside a cell, because you wouldn’t know which Name
it is referring to (Customer.Name or Employee.Name). For this case, you need to write
<#CustomerAndEmployee.Customer.Name> and
<#CustomerAndEmployee.Employee.Name>
3. Even if the prefix is only necessary for the case when there are repeated columns, it might
be good practice to always prefix the table names in joined tables. This way you know you
are always accessing the right column at the right table.
4. JOIN joins unrelated tables adding the columns of each one after the other, but it doesn’t
do any master detail or relationships. It will just show record1 or table1 together with
record1 of table2, and record2 of table1 with record2 of table2 and so on. When one of
the tables runs out of records, the next records for the columns in that table will be null.
5. While this command doesn’t use any extra memory (joined tables aren’t copied in
memory) and has no performance penalty, if you are doing SQL it will be simpler to just
use SQL to construct the joined table.
Union of Tables
While “JOIN” in the section above creates a table with the columns of a list of datasets, UNION
creates a table with the rows of a list of datasets. It works in a similar way as the union command
in SQL.
In general union is not really needed: You could just write the 2 ranges one after the other for
the two tables. But conceptually, sometimes it feels better to join the data of similar tables into a
big one and have a single named range for that table.
NOTE
The “Union” table will have all the columns of all the tables used to create it. Normally you will
use tables with the same columns, but if a column is present in one table but not in another,
union will show the values of the column for the table that has it, and null for the other.
You can look at the “Join and Union” example for an example on how to use JOIN and UNION
tags.
IMPORTANT
Be aware that allowing your users to directly write the SQL commands can mean a big
security risk.
For example, a user could use the connection you give him to execute the SQL: “drop table
users” instead of a normal select. While FlexCel does a little validation on the SQL written by
the user (for example, it cannot contain “;” or “--”, it has to start with “Select” etc) SQL is a very
powerful language and there can always be a way to execute a command on the server. And,
even if the user does not manage to execute a command, he might always do a “Select user,
password from users” or similar command, and get dbadmin access to the database.
You must supply a readonly connection, and with rights limited to the tables you want the
user to see.
2. On the config sheet, “Source Name” column, add a string like “SQL(Select * from clients”).
Give this table a name, and you can use it as any other table on the report.
Note that also for security reasons, you can't replace expressions inside the SQL string. For
example, you can't write “SQL(select * from customers where cust_id = <#custId>)” This would
open another security hole and allow for SQL injection attacks.
SQL Parameters
You need to specify database parameters to be able to actually pass information to the SQL.
.NET can use both positional (“?”) and named (“@name” or “:name”) parameters, and some data
providers will accept one or the other. As we want to keep the template database independent
(so you can replace the db backend without changing templates) all parameters on the
template are named with a preceding “@” (“@name”). When using a db that needs positional
parameters (like ADODB) or has another syntax for named parameters (like oracle that uses
“:name”) the SQL will be automatically converted by FlexCel into the needed one before sending
it to the db. FlexCel includes support for the most common cases, and when it does not know
which db it is, it will “guess” that the db uses named “@” parameters.
When you use a db FlexCel does not recognize, you need to let FlexCel know which kind of
parameters your db needs.
You can see a demo on how to do it on the “Direct SQL” example; more information is also
available there.
User table(Params) is a very simple tag, but it allows a lot of things. You write it on the Source
Table column in the config sheet, and you can add an additional parameter on the Table Name
column. You can leave the “Sort” and “Filter” columns empty or with values, their values do not
matter.
For every “User Table(Params)” on the template, the event UserTable will be called on the
report. Anything you write in “Params” will be passed as arguments to the event, without any
further processing by FlexCel. Also, the value you write on the Table Name column will be passed
to the event. You will normally want to use this second parameter to tell FlexCel how you want to
name the table you are creating. Note that the name you write on the “Table name” column is
not guaranteed to be created; this all depends on what you write on the event handler. Also,
note that you must write something on the tablename column even if you will not use it on the
event, or the row will be ignored.
So, how do you use it? Imagine you write a tag: User Table(CUSTOMER), and define the event
UserTable so each time it gets the parameter “Customer” it loads the customer table from the
database and adds it to the report. This way you are actually telling FlexCel which tables it needs
to use on the template, allowing you to add new ones (from a list of available tables on the
application) or remove existing tables without changing the code.
Other way to use this tag could be if you have your own API to access the database, with your
defined commands, permissions and validations. You could pass the API command as parameter
on the <#User Table> tag, and use this parameter inside the event to execute the API and add
the generated table to the report. You would write something like <#User Table(get table
customer on customerId < 100)> on the template, and on the UserTable event on the report,
execute the parameter against your API, and add the resulting dataset.
WARNING
When using the “User table” tag to pass arbitrary API commands to the application, please
remember to validate permissions on the UserTable event to see if the user is allowed or not
to run the query. Forgetting to do so could generate a big security hole on your application,
the same way as the SQL tag could.
For more information on this tag, see the User Tables demo.
Debugging Reports
Introduction
Whenever you hit enough complexity in your reports, you will get expressions that do not
behave as you expect, and you will need tools to investigate what is really happening under the
hood. This chapter speaks about those tools.
FlexCel Reports are declarative instead of imperative. This means you describe what you want
using report tags, but you do not write code to tell the computer how to do it. In a way, FlexCel
Reports are similar to SQL, and different from normal code.
An “imperative” report could be done with XlsFile, with some code like this:
A “declarative” report would be a template with the text “Table” in cell A1 and the tag
<#if(Table.field =;error;<#Table.field>)>
in cell A2.
Declarative reports can sometimes be more “resistant” to bugs because they are simpler and so
it is easier to see what is wrong. But on the other hand, they are also more difficult to debug
when there is a bug, because there is no code you can step into with a debugger.
In the XlsFile example above, you could set a breakpoint in the last line and evaluate the values
of the variables before execution. In the FlexCel Report there is no way to set a breakpoint, since
there is no code to execute. But you can still find out what is going on, and this is what we will
explain here.
There are two main causes for errors in FlexCel reports; syntax errors and logical errors. They are
covered below.
If for example you write “<#tag” in a cell, you will get an error telling you that there is a closing
tag “>” marker missing.
NOTE
When dealing with syntax errors, FlexCel leans over the "pedantic" side. This means that it will
not let pass anything that is ambiguous or not syntactically correct and report every little error.
We believe that by checking the errors when compiling the templates as much as possible, we
minimize the runtime errors that are harder to catch and can have worse consequences.
We also made a big effort to try to have all error messages as helpful as possible: For example
instead of a generic invalid parameter error, you should get a longer sentence indicating
what parameter is invalid and why.
But this is not always possible. Sometimes FlexCel just forwards the error of a lower layer, and
if the lower layer doesn't provide more information, neither can FlexCel. In this particular
example, some ADO database providers might return invalid parameter when they fail to
establish a connection, and in this case FlexCel will just forward that exception to you.
Now, in some situations it might be useful to see all syntax errors at once, and for this FlexCel
offers a “ErrorsInResultFile” mode. In this mode, all errors related to tags will be written to the
cell where the tag is, instead of raising exceptions, and the report will continue to generate.
Errors will have a yellow background and red text.
And "Invallid" is not defined, then instead of an exception when running the report it will
complete successfully. In the result file you will see:
This mode does not cover all errors (for example a named range for a non existing table will still
raise an error), but it covers the most common issues. The others still need to raise an exception,
since if for example you have a range named “__table__” and no “Table” in the report, there is no
place where FlexCel could write this error inside the xls file. The error must be in a cell for this
mode to work.
There are two ways to enter ErrorsInResultFile mode. The first one is to change it in the code
before running the report, by changing the FlexCelReport.ErrorsInResultFile property in
FlexCelReport.
For example:
The second way is to write it directly in the template, inside the config sheet.
For example:
The <#ErrorsInResultFile> tag can be anywhere in the Expressions column, and you do not need
to write anything in the Expression definition.
This second way to enter ErrorsInResultFile mode is better when you are editing a template and
want to do a debug without modifying the code, while the first way is better if you are
automating testing and do not want to modify the templates.
WARNING
Remember that this mode is a debugging mode, and you should turn it off for production.
You do not want to ship a file containing error messages in cells to your customers.
<#if(<#tagval>=<#refval>;OK;Error)>
This is a valid tag, and the report will compile and run without issues. Let's imagine now that
tagval is the number 1, and refval is “l” (lowercase L). So, when tagval is 1, the condition will
evaluate to false, and you will get the “Error” label instead of “OK”.
With this font in particular this can be a hard to spot problem, because as we said before, you
cannot really put a breakpoint in the expression and see what is inside tagval or refval.
As with the “ErrorsInResultFile” mode, there are two ways to activate debug mode. The first is
by code, by setting the “FlexCelReport.DebugExpressions” property in the FlexCelReport to
true. Again, setting it in code is useful when automating tests because you do not have to
change the template.
And the second way is to write “<#Debug>” in the configuration sheet, the same way as in
ErrorsInResultFile. This second way is preferred in most cases, f.i. if you do not want to modify
the code, for example when testing a template.
The same remarks mentioned for the <#ErrorsInResultFile> apply to the <#Debug> tag.
In this mode, tags will not write their value to the cell, but they will rather write the whole chain
of calculations made to arrive to the value.
<#if(<#tagval>=<#refval>;OK;Error)>
Each line has an expression in bold and a value in italics. Under that line and indented to the
right we find the subexpressions used in the main expression and their values.
In this case, we can easily see that <#tagval> is “1”, <#refval> is “1”, and <#tagval> = <#refval>
evaluates to false, so there is something wrong in the test. Having the full stack and all the
intermediate values used to compute the main expression can be a valuable tool to find out
what is going wrong, by allowing us to easily locate the exact point where the expression is not
evaluating as it should.
NOTE
In order to correctly visualize the stack in the cell, you will have to set “Wrap Text” to true in
the cell properties. If Wrap text is false, all lines will show in one line and it will be more
difficult to read.
Understanding the stack can be a little difficult at first time, but once you get used to it, it can be
a great help. To end up this chapter, we will present a more complex expression and its
corresponding stack explained. You can see it yourself at the “Debugging Reports” demo.
And the result we get when running this template in “debug” mode is:
You can see we have 4 main expressions here (they show without indentation). The first one is
the constant text “Test value is”, the second is the tag “<#test>” that evaluates to 3, the third is
other constant text “and here we will format it: ” and the fourth is an expression “Format
(<#test>;<#round(1)>)” that evaluates to 3. So the result in “normal” mode of this cell will be
“Test value is 3 and here we will format it: 3”. As there is a “format cell(red)” tag in the evaluation
stack, the result will be formatted in red format.
All tags shown below the “format” line are subexpressions used to compute it.
Constant
Syntax
Any text
Description
Any text that is not inside <#... > symbols.
Example
On the string “<#tag1>Hello<#tag2>” “Hello” is a constant.
Report Variable
Syntax
1. <#Value>
2. <#Value;default>
Description
A variable set in code before running the report. You can specify a default value (second syntax)
that will be used if the variable does not exist. If no default value is provided and the variable
does not exist, an exception will be raised.
Example
If you have a line of code FlexCelReport.SetValue("Date", DateTime.Now) before
FlexCelReport.Run, the tag <#Date> will be replaced by the current date. If you have <#Date>
in the template but do not do the SetValue command, a runtime error will happen. If you write
<#Date;No date supplied> in the template, this tag will be replaced with the date if you set it
on code with SetValue, or with the string "No date supplied" if not. There will never be a
runtime error.
NOTE
Report Variables, User defined Expressions and User defined Functions use the same syntax, so
it is impossible to differentiate between them. If you define a Report Variable with the same
name as a User defined Expression and/or User defined function, they will be used on the
following order: 1) User defined Expression. 2) Report Variable. 3)User defined function.
Description
Text on the Column “User defined Expressions” on the config sheet. When using parameters, you
can use the <#param1> name inside the expression definition.
Example
If you define an Expression CompleteName = <#LastName>, <#FirstName> on the config
sheet, the tag <#CompleteName> will be replaced by the expression.
NOTE
Report Variables, User defined Expressions and User defined Functions use the same syntax, so
it is impossible to differentiate between them. If you define a Report Variable with the same
name as a User defined Expression and/or User defined function, they will be used on the
following order: 1) User defined Expression. 2) Report Variable. 3)User defined function.
Description
An user defined function on the code. See the User Defined demo for details.
Example
If you define a function Proper(name) on the report code, <#Proper(test)> on the template
will be replaced by the results of the function.
NOTE
Report Variables, User defined Expressions and User defined Functions use the same syntax, so
it is impossible to differentiate between them. If you define a Report Variable with the same
name as a User defined Expression and/or User defined function, they will be used on the
following order: 1) User defined Expression. 2) Report Variable. 3)User defined function.
Dataset
Syntax
1. <#DataSet.Field>
2. <#DataSet.Field;default>
Description
The value of Field on the current row of Dataset. If a default value is provided, it will be used
when the field does not exist in the table or the table does not exits. You will normally want to
provide default values when using metatemplates, together with the defined and preprocess tag.
If you do not provide a default value and the field does not exist, a runtime error will happen.
There are two defined “pseudocolumns” that you can use on any dataset:
NOTE
Normally you can write any character in the column name, including a column named similar
to "Data(Max)". But in some cases, like when for example there is a simple parenthesis, or if
you have the character ">" inside the column name, you might need to quote the name. For
example, if your column name is "a(" you will have to write the tag as <#"DataSet.a(">.
When quoting a name, you can include a quote inside by writing 2 quotes. For example, the
column a"b can be written as <#"DataSet.a""b">
Examples
If you have a table "Customers" with a column named "LastName", <#Customers.LastName>
will replace the value on the report.
<#Customers.LastName;No customer> will enter the value of the field LastName if such field
exists in the database, or the string "No customer" otherwise.
<#Customers.LastName;>will enter an empty value if the field LastName does not exist.
Full Dataset
Syntax
<#Dataset.*>
Description
The whole current row of the dataset. Cells to the right will be overwritten. You can write other
<#Dataset.*> and <#Dataset.**> tags inside the same cell, and they will have the value of the
first one.
NOTE
It is recommended that you don't use <#column width(autofit)> in a <#Dataset.*> tag, since
the <#autofit> tag will be copied to every row and applied for every one. If you want to use it,
it is best to put the autofit tag in the Full dataset captions tag (<#Dataset.**>)
Example
If the cell A1=”<#DataSet.*>, after the report column A will have the first column of dataset,
Column B the second column, etc. Any text previously on Column B will be overwritten. If you
write "<#DataSet.*> <#if(<#DataSet.**>="Date";<#Format Cell(blue)>;>) in a cell, all
columns written will have autofit, and when the column is named "Date" it will be formatted in
blue
Description
The whole row of column captions for a dataset. You normally use this tag before a <#dataset.*>
and outside any named range.
Example
If you write <#Dataset.**> on cell A1, cell A1 will have the first column name on dataset, B1, the
second, etc. Text previously on B1 will be overwritten. If you write "<#Dataset.**>
<#ColumnWidth(autofit)>" in a cell, it will autofit all the columns in the range
DbValue
Syntax
1. <#dbvalue(table;row expression; column expression)>
2. <#dbvalue(table;row expression; column expression; default value)>
Parameters
• table: Datatable with the data, without quotes
• row expression: An excel expression (any valid excel formula that can include other
<#tags>) that must return a number or be empty. If empty, the record number will the
current record. If a number, then that number is the record you want to retrieve. (starting
at record 0)
• column expression: An excel expression (any valid excel formula that can include other
<#tags>) that must return a number or a string. If a number, this means the position of
the column in the datatable (with the first column being column 0). If a string, this is the
name of a column in the table.
NOTE
As this is an expression, not a string, if you want to enter a constant string here (like
"customer") it must be between double quotes. The expression ="customer" with quotes
evaluates to customer (without quotes). If you write just customer, it will be an invalid
expression.
• default value: A default value that will be used if the row or column doesn't exist. If you
don't specify a default value and try to access an invalid row or column, an exception will
be thrown.
Description
Returns any value of a table, letting you specify the record and column for the value. Note: This
is an advanced tag, and you most likely don't want to use it. The most common application for it
is when you want to do something depending on the value of the previous record (For example
merge the cell if the value is the same as previous). Don't use it as a general tool. <#db.field>
tags, ranges and aggregates should be enough for most needs, and they are a much cleaner and
"functional" abstraction.
Examples
<#dbvalue(customers;<#customers.#rowpos> - 1;"customerId";)> will return the value of
the previous value in customers.customerid. Note that "CustomerId" is in quotes as it should be
an expression that returns a string, not a string. Also note that we defined an empty default
value, so no exception is thrown when we are at the beginning of the table.
Aggregate
Syntax
1. <#aggregate(agg function; dataset name and column)>
2. <#aggregate(agg function; dataset name; agg expression; filter)>
Parameters
• agg function: It might be SUM for adding the values, AVG for finding the average, MAX
to find the maximum value and MIN to find the minimum value.
• dataset name (and column): Name of the dataset in which we want to aggregate the
values. Note that this dataset doesn't need to be inside any named range, since we will
use all of its records anyway. If "agg expression" is present, you don't need to include the
column name, as the columns to aggregate will be taken from the expression. If not
present, you need to include the column in which you want to aggregate.
• agg expression: This parameter is optional. An expression that will be applied to every
record in the dataset.(any excel function is valid here, and you can use any combination of
Excel functions) Null values will be ignored, but will count to the total records when
calculating the average. If not present, the values of the column specified in "dataset name
and column" will be used
• filter: This parameter is optional. If present, it should be an expression that returns true or
false. Again, any excel formula is valid here. Only those records where the filter value is
true will be used in the aggregate. When calculating the average, filtered records will not
be used in the count.
Description
Aggregates a dataset and returns an unique value for all its records. You can use this tag to find
for example the sum on a column in a dataset. Note that this tag can have bad performance, as
you need to load all data in memory in order to calculate the aggregate. If possible, it is
preferred to do the aggregate directly in the database, for example using a "Group by" clause in
the select SQL. This tag can be of use when you can't modify the datasets and you already have
the data loaded, so you need to do the aggregation from the template.
Examples
<#Aggregate(sum;orders.orderid)> will sum all the values in the column order id of the table
orders
List
Syntax
1. <#List(dataset name and column)>
2. <#List(dataset name; list separator; agg expression; filter)>
Parameters
• dataset name (and column): Name of the dataset in which we want to get the values as a
list. Note that this dataset doesn't need to be inside any named range, since we will use all
of its records anyway. If "agg expression" is present, you don't need to include the column
name, as the columns to aggregate will be taken from the expression. If not present, you
need to include the column in which you want to aggregate.
• list separator: This parameter is optional. If not present it will default to a single space.
This is the character that will separate the elements in the list. Note that if you want to us
a semicolon here (;) you will have to write it in quotes (";") so it is not considered a
parameter separator
• agg expression: This parameter is optional. An expression that will be applied to every
record in the dataset.(any excel function is valid here, and you can use any combination of
Excel functions) Null values will be ignored and not added to the list. If not present, the
values of the column specified in "dataset name and column" will be used
• filter: This parameter is optional. If present, it should be an expression that returns true or
false. Again, any excel formula is valid here. Only those records where the filter value is
true will be used in the aggregate.
Description
Returns a string with all the values of a table one after the other, and separated by a delimiter. If
the table has only one record, you can use <#List(table.field)> to get the value of the only
record without having to define any __table__ named range.
Examples
<#List(Employees.Lastname)> will return a string like "Smith Brown Perez". As we didn't specify
a separator, a single space will be used. If you know Employees has only one record, you could
have used this to avoid defining a __employees__ named range.
If
Syntax
<#if(Condition; IfTrue; IfFalse)>
Description
A conditional statement. When “condition” is true, “IfTrue” expression will be evaluated, if not
“IfFalse” will. For a description of the “Condition” format, see “Evaluating Expressions”
Example
<if(<#value>=1;One;Not One)> will write “One” if the report variable “Value” is 1, and “Not
One” if not.
Ifs
Syntax
<#ifs(Condition1; value1; Condition2; value2...)>
Description
An "if-chain" of different conditions. If condition1 is true then this tag will return value1, if it is
false it will evaluate condition2 and if condition3 is true return value2, and so on. If no condition
evaluates to true, then this tag will return #N/A!. If you want to provide a default condition, make
sure that the last condition is the value "true"
NOTE
If the condition always compares against the same value, you might use <#switch> instead.
The examples below are simpler using <#switch>
Examples
<#ifs(<#value>=1;One;<#value>=2;Two)> will write “One” if the report variable “Value” is 1,
"Two" if value is 2, and #N/A! if the value isn't 1 or 2
Switch
Syntax
<#switch(SwitchValue; value1; result1; value2; result2...[default])>
Description
This tag will compare "SwitchValue" against value1, value2, etc in order. If SwitchValue is equal to
any of the value_n, then result_n will be returned. You can provide a default value as the last
parameter. If no value matches SwitchValue and you have a default parameter, then the default
will be returned. The default is inferred from the number of arguments: An odd number of
arguments (3, 5, 7...) don't have default value. If the number of arguments is even, then the last
parameter is the default.
Examples
<#switch(<#value>;1;One;2;Two)> will write “One” if the report variable “Value” is 1, "Two" if
value is 2, and #N/A! if the value isn't 1 or 2
Evaluate
Syntax
<#evaluate(expression)>
Parameters
expression: An expression to evaluate. For a list of possible expressions, see the section
“Evaluating Expressions”.
Description
This tag will evaluate an expression and output the final result. You can see it as a “static”
formula. Different than <#=()> tag, expression will be evaluated each time a value is needed, so
you can have relative addresses.
Example
<#evaluate(A1+$A$2*2 & left(a3,2))> will output a string consisting on A1+A2*2
concatenated with the 2 first characters of a3.
Equal
Syntax
<#=(“Cell”)>
Description
Replaces the tag with the referred cell content. “Cell” might be a reference to another sheet.
Note that “Cell” will be evaluated at compile time, so it won't change when you copy the range.
(It behaves always as an absolute reference, on the style $A$1). If you want to have a cell
reference that is dynamically evaluated at fill time, use <evaluate(“Cell”)> tag. Note: Almost the
only place where this tag makes sense is on sheet names. For other expressions it is better to
define an expression on the config sheet and use it instead of a cell reference. When using a
sheet name you can not always know which one is the config sheet, so it is not safe to use
expressions, and you need the “=” tag.
Example
If you name a sheet <#=(Sheet2!A1)> the sheet name will be replaced by whatever you write
on cell A1 on Sheet2. On cells, define a named expression on the config sheet and use it instead
of this tag.
Include
Syntax
1. <include(file; named range; shift type)>
2. <include(file; named range; shift type;static/dynamic)>
3. <include(file; named range; shift type;static/dynamic;CopyRowsOrCols)>
Parameters
• file: Filename to include. The path is relative to where the current template is. If you are
inserting from a stream (for example from a database) you need to assign the GetInclude
event. See Templates On The Exe demo for more info. named range: Named range on the
included file that determines which cells will be included. If you leave this parameter
empty, the full used range in the active sheet will be included.
• shift type: How the existing cells will be shifted to insert the new ones. There are four
possibilities: “__”, “_”, “I_” and “II_”, to move existing cells the full row down, only down, full
column right and only right respectively. static/dynamic: This can be the string "Dynamic"
or "Static" (without quotes), or omitted, in which case it is assumed to be "Dynamic".
Dynamic includes will run inside the main report when inserted; this is the normal
behavior. Static includes will just insert the child file inside the main report without
running it. Static can be used if you want to include a previously generated report, to
make sure FlexCel does not try to run it again.
• CopyRowsOrCols: this parameter can be "R", "C" or "RC" (without quotes). By default,
included reports will get the column widths and row heights of the parent report (unless
you are inserting a full column or row). If you specify "R" here, row heights and format
from the included report will be copied to the parent, modifying the parent rows. If you
specify "C", column widths will be copied, modifying the parent columns. If you specify
"RC", both columns and rows will be copied.
Description
Includes a sub report inside the current one. An included file can include other files itself. The
subreport is precompiled and runs on its own sandbox, so it cannot access cells on the parent.
For example, a <#delete range> tag will never erase something outside the include. The
subreport does have access to all report variables, expressions and user defined functions of the
parents, as it has to the parent databases.
Example
On the following include, cells will be moved the whole row down.
Note how cells C2 and D2 are overwritten on the final report. As a general rule, do not write
anything at the right of the file being included, except if you know for sure the include won't
overwrite the cells. When inserting columns (II_ and I_ ), cells on the same column as the include
will be overwritten.
Configuration Sheet
Syntax
<#config>
Description
This tag will identify the current sheet as the configuration sheet. It will only have effect when
written on a sheet name.
Example
Just name the sheet <#config>
You can also conditionally define a sheet as the configuration sheet. If you write <#if
(<#value>=1;<#delete sheet>;<#config>)> as the name of one sheet and <#if
(<#value><>1;<#delete sheet>;<#config>)> as the name of another, the configuration sheet
will be the first or the second one depending on the value of <#value>.
Delete Range
Syntax
<#delete range(range address; shift type>
Parameters
• range address: The range of the cells to delete. This might be a string like "A1:B5" or a
named range like "myrange"
NOTE
Whenever possible, use named ranges instead of strings in the range definitions. If you define
a named range "myrange" in cells A2:A3 and use it as a parameter for this tag, when you insert
a row in A1 the range will move to A3:A4. If you had written the string "A2:A3" as the
parameter for this tag, it will still point to A2:A3 after inserting the row.
• shift type: How the existing cells will be shifted when deleting. There are four possibilities:
“__”, “_”, “I_” and “II_”, to move existing cells the full row up, only up, full column left and
only left respectively.
Description
Use it to delete a range of cells. The range will be deleted after all the cells on the band have
been replaced.
Example
<#if(<#value>=1;<#delete range(a1:a5;__)>;)> will delete the first five rows on the band
when <#value>=1 and shift rows up.
Delete Row
Syntax
1. <#delete row>
2. <#delete row(full)>
3. <#delete row(relative)>
Parameters
• no parameters: When called with no parameters, the full row will be deleted.
• full: This is the same as calling it without parameters, the full row will be deleted
• relative: Only the cells inside the range being processed will be deleted, not the full row.
Use this call if for example you have 2 side by side reports, and wish to delete the row in
one of the reports but not in the other. Older FlexCel versions used this mode by default
Description
Use it to delete the current row. If you are a FlexCel 2.x user, note that <#delete row> behaves
different than the old ...delete row... Now <#delete row> is processed at the same time as the
ranges, so you can't use it to expand ranges. Use “X” ranges instead.
Example
<#if(<#value>=””;<#delete row>;)> will delete the current row when value is empty.
Delete Column
Syntax
<#delete column>
Parameters
• no parameters: When called with no parameters, the full column will be deleted.
• full: This is the same as calling it without parameters, the full column will be deleted
• relative: Only the cells inside the range being processed will be deleted, not the full
column. Use this call if for example you have 2 reports one above the other, and wish to
delete the column in one of the reports but not in the other. Older FlexCel versions used
this mode by default
Description
Use it to delete the current column.
Example
<#if(<#value>=””;<#delete column>;)> will delete the current column when value is empty.
Delete Sheet
Syntax
<#delete sheet>
Description
This tag will delete the current sheet. It will only have effect when written on a sheet name.
Example
If you write <#if(<#value>=1;<#delete sheet>;Food)> as the name of a sheet, this sheet will
have the name Food when the report variable value is not one, and will be deleted when
value=1
NOTE
As them maximum sheet name size is 31 characters, you will probably need to write the
expression including the <#delete sheet> tag on the config sheet, and name the sheet as
<#=(<#Config>!A1)> or similar, as shown in the picture:
Format Cell
Syntax
<#format cell(format name; [user format parameter 1; user format parameter 2;...])>
Parameters
• format name: The name of a format defined on the config sheet, or an User Defined
Format defined with FlexCelReport.SetUserFormat.
• user format parameters: This is a list of optional parameters and they are only used if
format name is an User Fefined format. It is a list of parameters that will be passed to the
used defined format function.
Description
Use it to format a cell with a defined format. You define all format settings (fonts, borders,
patterns, etc) on the config sheet, and then you can freely use them. Note that you can define
"partial formats" that will only apply part of the format. (for example the cell background, but
keeping the font of the destination cell). Look at the Reports Designer Guide for more
information on partial formats
Examples
<#if(mod(row(a1),2)=0;<#format cell(Yellow)>;)> will format the cell as yellow for odd rows.
NOTE
You could also use an User Defined Format added with FlexCelReport.SetUserFormat. For
example: <#format cell(MyUserFunction;<#category.color>)> will call a function registered as
MyUserFunction with FlexCelReport.SetUserFormat and pass the value of <#category.color> to
the function. The function will return the format for the cell.
Format Row
Syntax
<#format row(format name; [user format parameter 1; user format parameter 2;...])>
Parameters
• format name: The name of a format defined on the config sheet, or an User Defined
Format defined with FlexCelReport.SetUserFormat.
• user format parameters: This is a list of optional parameters and they are only used if
format name is an User Fefined format. It is a list of parameters that will be passed to the
used defined format function.
Description
Use format row to format the current row with a defined format. Note that the order on that the
<#format> tags will be applied is:
So, if you format row 1 as “Red”, and cell A1 as “Blue”, the cell format will have priority over the
row format and A1 will be Blue. B1:XFD1 will be Red.
Example
<#format row(blue)> will format the current row with the user defined format “blue”.
Format Column
Syntax
<#format column(format name; [user format parameter 1; user format parameter 2;...])>
Parameters
• format name: The name of a format defined on the config sheet, or an User Defined
Format defined with FlexCelReport.SetUserFormat.
• user format parameters: This is a list of optional parameters and they are only used if
format name is an User Fefined format. It is a list of parameters that will be passed to the
used defined format function.
Description
Use format column to format the current column with a defined format. Note that the order on
that the <#format> tags will be applied is:
So, if you format column A as “Red”, and cell A1 as “Blue”, the cell format will have priority over
the column format and A1 will be Blue. A2:A1048576 will be Red.
Example
<#format column(blue)> will format the current column with the user defined format “blue”.
Format Range
Syntax
<#format range(range address; format name; [user format parameter 1; user format parameter
2;...])>
Parameters
• range address: The range of the cells to format. This might be a string like "A1:B5" or a
named range like "myrange"
NOTE
Whenever possible, use named ranges instead of strings in the range definitions. If you
define a named range "myrange" in cells A2:A3 and use it as a parameter for this tag,
when you insert a row in A1 the range will move to A3:A4. If you had written the string
"A2:A3" as the parameter for this tag, it will still point to A2:A3 after inserting the row.
• format name: The name of a format defined on the config sheet, or an User Defined
Format defined with FlexCelReport.SetUserFormat.
• user format parameters: This is a list of optional parameters and they are only used if
format name is an User Fefined format. It is a list of parameters that will be passed to the
used defined format function.
Description
Use format range to format a range of cells with a defined format. Note that the order on that
the <#format> tags will be applied is:
So, if you format range A1:B2 as “Red”, and cell A1 as “Blue”, the cell format will have priority
over the range format and A1 will be Blue. All other cells on A1:B2 will be Red.
Example
<#format range(a1:b2;blue)> will format the range a1:b2 with the user defined format “blue”.
<#format range(myrange;blue)> will format the named range "myrange" with the user defined
format “blue”.
Merge Range
Syntax
<#merge range(range address)>
Parameters
• range address: The range of the cells to merge. This might be a string like "A1:B5" or a
named range like "myrange"
NOTE
Whenever possible, use named ranges instead of strings in the range definitions. If you define
a named range "myrange" in cells A2:A3 and use it as a parameter for this tag, when you insert
a row in A1 the range will move to A3:A4. If you had written the string "A2:A3" as the
parameter for this tag, it will still point to A2:A3 after inserting the row.
Description
Use merge range to dynamically merge a range of cells when generating the report. The range
will grow/shrink when copying the tag, depending on the count of records on the current band.
IMPORTANT
This tag is only for when you need to merge cells depending on a condition. For normal
merged cells, just merge them in the template.
Example
<#merge range(a1:a2)> when written inside a band on A1:Z2 will merge the cells on column A
once per band.
Description
<#page break> tags are useful for inserting page breaks on the report. “Normal” page breaks
are fixed, they won't be copied each time a band is expanded. So you need to add this tag to get
a page break for each value of the band. Note that manual page breaks on a sheet have a
maximum of 1026, so any page break above this will not be inserted and will be silently ignored.
There is a property FlexCelReport.ErrorActions that you can set to throw an exception instead on
ignoring them when the limit is reached.
Example
<#page break> will insert a page break on the current row.
Description
<#column page break> tags are useful for inserting page breaks on the report. “Normal” page
breaks are fixed, they won't be copied each time a band is expanded. So you need to add this
tag to get a page break for each value of the band. Note that manual page breaks on a sheet
have a maximum of 1026, so any page break above this limit will not be inserted and will be
silently ignored. There is a property FlexCelReport.ErrorActions that you can set to throw an
exception instead on ignoring them when the limit is reached.
Example
<#column page break> will insert a page break on the current column.
Parameters
• PercentOfPageUsed: This value must be between 0 and 100 and specifies the minimum
percent of the sheet that can be empty when adding the page breaks.
• PageScale: This parameter must be between 50 and 100, and it specifies how smaller to
consider the sheet when calculating the page break, in order to avoid rounding errors.
Calling this tag without parameters is equivalent to calling <#auto page breaks(20;95)>
Description
When you write an <#auto page breaks> tag in a sheet, FlexCel will try to keep together all
named ranges starting with "keeprows_" and "keepcolumns_". For an in depth explanation on
how this works, take a look at the Reports Designer Guide and API Developer Guide
Example
<#auto page breaks> will tell FlexCel to add manual page breaks in all the sheet so all "keep..."
ranges are kept together.
Row Height
Syntax
1. <#Row Height(Value)>
2. <#Row Height(show)>
3. <#Row Height(hide)>
4. <#Row Height(autofit; Adjustment;AdjustmentFixed;MinHeight;MaxHeight)>
Parameters
• Value: If it is a number, it means the height of the row. If it is “show” or “hide” means to
show or hide the row. If it is “autofit”, it means to autofit the row to the cell contents.
• Adjustment: This value is optional and only has meaning if “value” is autofit. It is a
percent to make the row higher.
• AdjustmentFixed: This value is optional and only has meaning if "value" is autofit. It is a
fixed amount to make the row bigger than the calculated value. The final height of the row
might be calculated as: FinalHeight = CalculatedHeight * Adjustment + AdjustmentFixed
• MinHeight: This value is optional and only has meaning if value is autofit. It might be:
◦ Dont Shrink: Means autofit, but never make the row smaller than the original size.
◦ Dont Grow: Means autofit, but never make the row bigger than the original size.
• MaxHeight: This value is optional and only has meaning if value is autofit. It might be:
◦ Dont Shrink: Means autofit, but never make the row smaller than the original size.
◦ Dont Grow: Means autofit, but never make the row bigger than the original size.
Description
Use this tag to change the heights of rows. See the “Autofit” demo for more information.
Example
<#Row Height(30)> will set the wor height to 30.
<#Row Height(Autofit;100;0;dont shrink)> will mark the row to be autofitted by FlexCel, with
standard adjustment, and with a row size of at least the original row size.
Column Width
Syntax
1. <#Column Width(Value)>
2. <#Column Width(show)>
3. <#Column Width(hide)>
4. <#Column Width(autofit; Adjustment;AdjustmentFixed;MinWidth;MaxWidth)>
Parameters
• Value: If it is a number, it means the width of the column. If it is “show” or “hide” means to
show or hide the column. If it is “autofit”, it means to autofit the column to the cell
contents.
• Adjustment: This value is optional and only has meaning if “value” is autofit. It is a
percent to make the column wider.
• AdjustmentFixed: This value is optional and only has meaning if "value" is autofit. It is a
fixed amount to make the column bigger than the calculated value. The final width of the
column will be calculated as: FinalWidth = CalculatedWidth * Adjustment +
AdjustmentFixed
• MinWidth: This value is optional and only has meaning if value is autofit. It might be:
◦ Dont Shrink: Means autofit, but never make the column smaller than the original
size.
◦ Dont Grow: Means autofit, but never make the column bigger than the original size.
• MaxWidth: This value is optional and only has meaning if value is autofit. It might be:
◦ Dont Shrink: Means autofit, but never make the column smaller than the original
size.
◦ Dont Grow: Means autofit, but never make the column bigger than the original size.
Description
Use this tag to change the widths of columns. See the “Autofit” demo for more information.
Example
<#Column Width(Autofit)> will mark the column to be autofitted by FlexCel.
Autofit Settings
Syntax
<#Autofit Settings(Global; KeepAutofit; Adjustment; AdjustmentFixed; MergedCellsMode)>
Parameters
• Global: It can be either the string All or Selected (without quotes). All means
automatically autofit every row on the workbook, regardless of if the row has been
marked for autofit (with <#row height(autofit)>) or not. Selected means only autofit
rows that are marked. The default is Selected and we recommend this setting, since
autofitting all the rows on the sheet could change heights of rows you do not want to
change. If you want to use All, make sure you make rows that you do not want to chage
of fixed height on the Excel template.
• KeepAutofit: Might be keep or fixed. If keep, rows that were marked as autofit on the
original template will be kept autofit. This means when you open the file in Excel it will
recalculate the row heights and they might change a little, but you will never get croped
text. The dafault is true. If this setting is fixed, row height will be fixed at the size
calculated by FlexCel, and Excel will not recalculate them. While this will make both Excel
and FlexCel look the same, when seing the file in Excel it might crop some text. If you want
to use this option, we recommend you ser Adjustment of about 150 to avoid text crop.
• Adjustment: It is a percent to make the columns wider or rows higher on all the sheets.
The default is 100, but you can enter a bigger number here. Also, you can override global
adjustments with the <#row height(autofit, localadjustment)> and <column width(autofit,
localadjustment)>. If you do not specify localadjustment on those tags, the value specified
here will be used.
• AdjustmentFixed: It is a fixed ammount to make the columns wider or rows higher on all
the sheets. The default is 0, but you can enter a bigger number here.
• MergedCellsMode: Sets how to fit a cell that spans over multiple rows when autofitting
rows, or a cell that spans multiple columns when autofitting a column. The possible values
for this parameter are None, First, Last and Balanced (without quotes).
• First means that the first row/column of the merged cell will be changed to autofit the full
cell.
• Last means that it will change the last row/column of the cell.
• Balanced means that it will increase the height of all the rows in the merged cell by the
same amount.
First might be followed by a "+" and a number between 0 and 4. For example, "First+1"
means use the second row of the merged cell to do the autofit. Similary, "Last" might be
followed by a "-" and a number between 0 and 4. For example "Last-2" means use the row
that is 2 rows before the last.
Description
This tag commands the autofit settings on a sheet. You need to have only one of those tags in
each sheet, and it will affect the autofit of all rows and columns. If you do not specify this tag on
a sheet, the default used is <#Autofit Settings(Selected;keep;100;0)>
Example
<#Autofit Settings(All, keep, 100)> will autofit all non fixed rows on the sheet, and you will
not need to specify individual rows to autofit with <#row height> tag.
<#Autofit Settings(Selected; Fixed; ; ;Last-1)> will autofit only rows that have an autofit tag,
and keep the size fixed. The adjustments are left to the default, and in case of merged cells, the
row or column before the last in the merged cell will be used. Note that when autofitting merged
cells, you might want to keep autofit "Fixed", because Excel doesn't autofit merged cells, so when
you open the file, it will revert to a single line.
Comment
Syntax
<#//(...)>
Description
Everything inside a // tag will be ignored. You can use it to temporary disable tags.
Example
<#//(This is a comment)> will not do anything and is equivalent to an empty string.
Image Size
Syntax
1. <#imgsize>
2. <#imgsize(zoom; aspect ratio)>
Parameters
When called with no parameters, this tag will resize the image to "Best Fit" inside the original
image template rectangle maintaining the aspect ratio. That is, if the image in the template is
50px wide x 40px tall, the new image will be resized to be either 40 px tall or 50 px wide, in a way
the aspect ratio is mainatined and the new image is no bigger than the image in the template.
• zoom: Percent of zoom to resize the image. 0 means leave size untouched.
• aspect ratio: Aspect ratio of the image. 0 means leave size untouched. Negative values
mean keep height fixed and resize width to match, and positive values mean keep width
fixed and resize height to match.
Description
This tag will only work when written on the name of an image. You shouldn't use both
parameters at the same time, always leave zoom = 0 or aspect ratio = 0.
Example
The most common way to use this tag is just to name an image <#Data><#imgsize>. If you do
so, the inserted image will be as big as possible without being bigger than the original, and
maintaining the aspect ratio. If you name an image <#Data><#imgsize(0;-1)> the image will
retain its designed height and resize its width so it is not distorted. You can see a lot of different
uses of this tag on the Images demo.
Image Position
Syntax
<#imgpos(RowAlign;ColAlign;RowOffset;ColOffset)>
Parameters
All parameters are optional.
• RowOffset: It is a number specifying how many pixels from the calculated position the
image will be moved down. If negative, image will be moved up from the calculated
position. If omitted it is assumed to be 0.
• ColOffset: It is a number specifying how many pixels from the calculated position the
image will be moved right. If negative, image will be moved left from the calculated
position. If omitted it is assumed to be 0.
Description
This tag must be written as part of an image name, not in a cell. Use this tag to dynamically
move an image. You will normally need to use it when dealing with image of different sizes.
Example
If you name an image <#Data><#imgpos(center;center;-10)> the image will be centered in
the column, and 10 pixels to the right of being centered in the row.
Image Fit
Syntax
<#imgfit(FitInRows;FitInCols;RowMargin;ColMargin)>
Parameters
All paramters are optional.
• FitInRows: It might be "InRow", "Dont Shrink", "Dont Grow" (all without quotes) or
omitted. If omitted the row will not change. If you use InRow, the row size will always
change to fit the image. Dont Shrink and Dont Grow work the same as InRow, but row
size will only change if the new height is larger/smaller than the current size.
• FitInCols: It might be "InColumn", "Dont Shrink", "Dont Grow" or omitted. If omitted the
column will not change. If you use InCol, the column size will always change to fit the
image. Dont Shrink and Dont Grow work the same as InColumn, but column size will
only change if the new width is larger/smaller than the current size.
• RowMargin: It is a number specifying how many pixels to add to the row as a margin
around the image.
• ColMargin: It is a number specifying how many pixels to add to the column as a margin
around the image.
Description
This tag must be written as part of an image name, not in a cell. Use this tag to resize a row or a
column so they are big enough to hold an image. You will normally want to use this tag when
image size changes dynamically, so the images fit inside their cells.
Example
If you name an image <#Data><#imgfit(inrow;;-10)> the row will be made as big as the
image plus 10 pixels.
Image Delete
Syntax
<#imgdelete>
Description
This tag must be written as part of an image name, not in a cell. Use this tag to delete an image.
Example
If you name an image <#Data><#if(<#Data="";<#imgdelete>;)> image will be deleted when
there is no data.
Lookup
Syntax
<#lookup(table name; search key names; search key values ;result field)>
Parameters
• table name: Master table where we will look for the value.
• search key names: A list of columns containing the search key on the master table. It will
normally be just one column, but if you need to search by more than one, you can
separate column names with a comma (“,”)
• search key values: A list of values containing the search values on the master table. The
number of search key values should match the number of search key names. If you have
more than one search key value, you need to use an <#array> tag.
Description
Use <#lookup> to search for a field description on another table.
Example
If you keep an CustomerId on table Orders and the Customer data on a table Customers, to
output the real customer name for an order you can use:
<#lookup(Customers;CustomerId;<#Orders.CustomerId>;CustomerName)>
For more examples on the use of lookup, see the lookup demo.
Array
Syntax
<#array(value_1; value_2;.... ;value_n)>
Description
Use <#array> to output an array of values. Currently, the only use of <#array> tag is to provide
an array of search keys for the <#lookup> tag, but it could have more independent uses on the
future.
Example
To lookup a field by two different keys, you should use:
<#lookup(Table;Column1,Column2;<#array(value1;value2)>;result column)>
Regular Expressions
Syntax
<#regex(IgnoreCase; Expression; Match; [Replace])>
Parameters
• IgnoreCase: 0 to do a case-sensitive search, 1 to do a case-insensitive search.
• Replace: This is an optional parameter, and if present, it is the string that we will replace
into the matching parts of Match.
Description
There are 2 ways to use this tag, depending if you include the Replace parameter or not. If you
don't include it, this function will return the parts of the string Match that match the regular
expression. When you specify a Replace parameter, this function will return the original “Match”
string with the parts that match the expression replaced by the Replace parameter.
Examples
<#regex(0;x.*e;flexcel)> will return xce
Formula
Syntax
<#Formula>
Description
You can use this tag to make FlexCel enter the text on the cell as a formula instead of a string.
Note that the text on the cell must be a valid formula, and start with an “=” sign. If the
expression is not a valid Excel formula, an Exception will be raised. You only need to enter this
tag once in a cell, normally at the beginning. Note that for entering cell references, you will need
to use the <#ref> tag.
Example
If you enter on a cell:
when the report is generated, on the cell you will have formulas like:
B5: “=A5 + 4”
B6: “=A6 + 3”
etc.
We used the <#ref> tag here to make the reference “A5” grow down when the cell is copied.
Also, using <#ref> instead of writing the cell reference directly, allows you to insert for example
a row at the beginning of the template, and not break the report.
WARNING
In the real world, there is little use for the <#formula> tag, to the point that is difficult to find
examples where it can be useful. Of course those cases exist, but if you are thinking in using
this tag, first think if a simple Excel formula without any tags wouldn't be better.
In the example above, you could write <#Db.Field> in column C, and have a simple = A5 +
C5 formula in B5. Whenever possible, prefer simple Excel formulas instead of using this tag.
Ref
Syntax
1. <#Ref(NamedRange)>
2. <#Ref(RowOffset; ColOffset)>
3. <#Ref(NamedRange; RowAbsolute; ColAbsolute)>
Parameters
• NamedRange: The name of a named range with the cell address you want to use.
• RowOffset: How many rows below or above this cell is the refrence. Use negative values
to indicate rows above the cell.
• ColOffset: How many columns at the left or the right of this cell is the refrence. Use
negative values to indicate rows at the left of the cells.
• RowAbsolute: If true, the row will not move down when copying. This is analog to a A$1
reference.
• ColAbsolute: If true, the column will not move to the right when copying. This is analog to
a $A1 reference.
Description
This tag will normally be used together with a <#formula> tag, in order to add relative
references to a hand-written formula. Even if the values are absolute, it is a good idea to always
use <#ref> tags on formulas, since if you don't, whenever you insert rows on the sheet the
references will not be updated
Example
<#ref(-1;-2)> means the cell that is 1 row above and 2 columns to the left
<#ref(Potatoes;true;true)> means a reference to the name "potatoes" on the sheet that will
not move when copying cells.
HTML
Syntax
<#HTML(Enable)>
Parameters
• Enable: Enable can be “TRUE” or “FALSE”. When true, the text on the cell will be entered as
HTML, when false it will be entered as normal text. For more information about HTML tags
supported, see the FlexCelReport.HtmlMode property.
Description
This tag overrides the global property FlexCelReport.HtmlMode on a cell by cell basis. If you set
HtmlMode = true on a report, you can exclude individual cells of being HTML formatted with the
tag <#HTML(false)>. Similarly, when HtmlMode = false, you can make individual cells HTML
formatted with the tag <#HTML(true)>. You only need to write one HTML tag into a cell, and its
position does not matter.
Example
<#HTML(true)><#Text> will enter the value of <#Text> as an Html string when HtmlMode =
false.
Preprocess
Syntax
<#Preprocess>
Description
The preprocess tag enters a "preprocessor" mode where you can modify the template before
actually running the report. You only need to write one Preprocess tag into a cell, and its
position does not matter.
When this tag is present in any cell of the template, FlexCel will make 2 passes on it. On the first
pass, FlexCel will process all the cells with "Preprocess" tag, and in the second it will load the
modified template. You can use the first pass to delete rows and columns, and customize the
final template before the report.
You can get dynamic templates this way, that are customized depending on the data.
Example
<#Preprocess><#if(<#defined(customer.date)>;;<#delete column>)> will delete the
column from the template before running the report when customer.date is no defined.
Defined
Syntax
1. <#defined(field_or_variable)>
2. <#defined(field_or_variable;global>
3. <#defined(field_or_variable;local>
Parameters
• field_or_variable: Field we want to find out if is defined. "Defined" will return if the
variable or database field exists in a global scope.
• local: When the second parameter is the string "local", "defined" will return true only if the
field is accessible to the current range. For example, if you have a master range __master__
and included inside a detail range __detail__; defined(detail.field;local) will return true only
if the cell is inside the __detail__ range, but not if is inside the __master__ range. defined
(detail.field;global) or simply defined(detail.field) will return true no matter the cell where
the expression is in.
Description
Use this tag to know if a field variable is defined or not. This is normally useful when doing
metatemplates (see meta templates demo) together with the Preprocess tag. This way you can
have dynamic SQLs, and delete columns from the report if those columns where not selected in
the SQL.
Example
<#Preprocess><#if(<#defined(customer.date)>;;<#delete column>)> will delete the
column from the template if the field "date" does not exists in the table customers
NOTE
When using the "defined" tag, you will probably need to use default values in the database
fields too. For example, the expression "<#if(<#defined(db.field)>;<#db.field>;no data)>"
will raise an error if db.field does not exits. This is because FlexCel precompiles the whole
expression before evaluating it, and it can't compile it if <#db.field> does not exist. The
defined tag will be evaluated later, (many times, this is why FlexCel precompiles the
expression), but at precompile time this expression will raise an error.
In general, when using fields that might be defined or not, you should always specify a default
value for them.
Defined Format
Syntax
<#defined format(expression)>
Parameters
• expression: An expression that should resolve to a string
Description
Use this tag to know if a custom format is defined in the config sheet.
Example
<#if(<#defined format(<#fmt>)>;<#format cell(<#fmt>)>;)> will format the cell with the
style specified by the variable <#fmt> if it is defined, or do nothing otherwise.
Introduction
Performance is a complex but very important characteristic of any application. One of our design
goals with FlexCel is to be as fast as possible, and we are always finding ways to improve a little
the performance from version to version. The idea is that for most of the cases, FlexCel should be
“fast enough” and you shouldn’t have to care about performance when using it, as we took care
of it for you. But for cases where you need the absolute maximum of performance, you can help
FlexCel perform better by writing code that uses it in an optimal way. To know how to code for
maximum performance, you need to understand how FlexCel works from a performance
viewpoint, and that’s what this document is about.
WARNING
Before doing anything else, let us make this clear: Don’t over optimize. In many cases code
clarity and performance are not compatible goals and the more performing the code, the
more difficult it is to maintain, change, or adapt. In most cases FlexCel is incredibly fast, and
you shouldn’t have to depend on dirty tricks to create or read files instantly.
Memory
If there is one thing that can make your applications slower, that thing is using too much
memory and starting paging to disk. And sadly, because of the way Excel files are created, and
also because of how a spreadsheet works, FlexCel needs to keep the full spreadsheet loaded in
memory.
Even when in many cases we use Excel files as databases they aren’t. It is impossible to randomly
read or write cells from an xls/xlsx file, and due to the way formulas work, a single change in a
cell might affect the full file. For example, imagine that we have a spreadsheet with cell A1 = 1,
and in the second sheet, Sheet2!E4 has the formula “=Sheet1!A1 + 1” When we change the value
at cell A1 from 1 to 2 we need to change the formula result in Sheet2!E4 from 2 to 3. Also, if we
insert a row before A1, we will need to change the cell Sheet2!E4 from “= Sheet1!A1 + 1” to
“=Sheet1!A2 + 1”. This makes it very difficult to work with just a part of the file in memory, and
neither we nor Excel do it. We both load the full file into memory and do all of our work there.
The main issue with running out of memory is that performance degradation is not linear. That
is, you might use 1 second to write a million cells, but 1 minute to write 2 million, and 1 hour to
write 3 million. This is one of the reasons we don’t normally quote silly benchmark numbers like
“n cells written per second”; they are meaningless. If you take n seconds to write m cells, it
doesn’t mean that to write 2*m cells you will need 2*n seconds. Performance degrades
exponentially when you run out of memory.
So we make a lot of effort to try to not use too much memory, for example, repeated strings in
cells will be stored only once in memory, or cell styles will be shared between many cells. But no
matter what, we are a fully managed .NET library, and we can’t escape the limitations of the
platform. .NET applications do use a lot of memory, and the Garbage Collector might not release
everything you need either. This is not necessarily a bad thing, using the memory you have is
good not bad, but for huge files it can become problematic.
There are 2 ways you can get around the memory problem: using FlexCel in “Virtual Mode”, or
going to 64 bits. We will expand on this in the sections below.
WARNING
Before continuing, we would like to give one last warning about memory.
Measuring memory consumption can be very complex, especially in .NET. You need to
measure how many objects you have in Generations 0, 1 and 2, you need to distinguish
between private and public bytes, and you need a memory measurement tool. Task manager
isn’t such tool.
Virtual Mode
Sometimes you don’t really need all the complexity that a spreadsheet allows; you just want to
read values from cells or dump a big database into a file. In those cases, it might not make sense
to have the full file into memory, you could read a cell value, load it in your application, and
discard the value as the file is being loaded. There is no need to wait until the entire file has been
read to discard all the cells.
For this, FlexCel provides what we know as “Virtual Mode”, a special mode where cells are read
on demand. Not all features are supported in this mode, but normally for the huge files where
Virtual mode makes sense, those features aren’t needed either.
You call ExcelFile.Open, and FlexCel reads the file and loads it into memory.
You call ExcelFile.Open, and the file is read. But for every cell read, the VirtualCellRead event is
called, and the cell is not loaded into the memory model. You will end up with an ExcelFile object
that has no cells (but has charts, drawings, comments, etc.).Cells can be used by your application
as they are being read. You can get the rest of the things (comments, merged cells, etc.) from the
memory model after the file has been opened.
64 bits
Sometimes, if you have a memory problem and the application runs server-side, the cheapest
solution might be to install more memory in the server. But in 32 bit mode, no matter how much
memory you have, FlexCel will never use more than 2 Gb.
If you want to use more memory than that you should use FlexCel in 64 bit mode. Note that
FlexCel.dll is compiled as “Any CPU”, and that means that it will work in 32 bit mode if your
application is compiled as a 32 bit application and 64 bit mode if it is compiled as 64 bit. You
don’t need a different assembly.
WARNING
Going 64 bits might not only not improve performance, it might decrease it. In our tests
in a 4Gb machine, 64bit version is consistently slower than the 32bit one (both versions
running in a 64 bit Operating System).
It kind of makes sense, since by going 64 bits now every pointer is 8 bytes instead of 4, so there
is a lot of memory used in those bigger pointers, that are mostly 0 anyway. For 64 bit to improve
things, you need a lot of memory installed.
By the way, .NET 4.0 is much better than 3.5 in our tests for 64 bits, getting almost the speed of
32 bits, while in 3.5 64bit is really bad. So if you are thinking in going 64bit, consider at least
.NET 4.0.
Loops
This is because in OLE Automation, one of the “performance tips” is to do exactly that. Set the
cell values into an array, and then copy that array into Excel. But this is because in OLE
Automation method calls are very expensive.
And this concept is worth expanding: Excel itself isn’t slow; it is coded in optimized C/C++ by
some of the best coders in the world. So how is it possible that third party libraries written in
high level languages like FlexCel can be so much faster? This is in part because Excel is optimized
for interactive use, but more important, because while Excel itself is fast, calling Excel from OLE
Automation is not.
So any good OLE Automation programmer knows that to maximize the speed, he needs to
minimize the Excel calls. The more you can do in a single call the better, and if you can set a
thousand cells in one call that is much better than doing a thousand calls setting each value
individually.
But FlexCel is not OLE Automation and the rules that apply are different. In our case, if you
were to fill an array with values so you can pass them to FlexCel, it would actually be slower. You
would use the double of memory since you need to have the cells both in the array and in
FlexCel, you would lose time filling the array, and when you finally call the imaginary
“ExcelFile.SetRangeOfValues(array)” method it would loop over all the array and cells to copy
them from the Array to FlexCel since that is the only way to do it.
So just lopping and filling in FlexCel directly is faster; filling an array before setting the values
only would add overhead.
This can be from a non-issue when VeryExpensiveFunction isn’t too expensive (for example it is
List.Count) to a disaster if VeryExpensiveFunction is actually expensive and the iteration runs for
millions of times (as it can be the case when using “ExcelFile.ColCount”).
1. If the order in which the loop executes doesn’t matter, you might simply reverse the loop:
This way VeryExpensiveFunction will be called only once at the start, and the comparison is
against “ i >= 0” which is very fast.
NOTE
Note that RowCount is very fast, the issue is with ColCount, because FlexCel stores cells
in row collections.
Let’s study this by our rules. First of all, let’s see the ending conditions of the loops. As explained,
xls.RowCount and xls.ColCount are called for every iteration in the loop. This is not too bad for
RowCount since RowCount is fast and it is the outer loop, but it is a huge problem for
Xls.ColCount, which is not only slow as mentioned earlier, but also runs in the inner loop.
So if the file has 50,000 rows and the maximum used column is column 30, you are iterating
50,000 x 30 = 1,500,000 times. xls.RowCount is called 50,000 times (since it is in the outer loop)
and xls.ColCount is called 1,500,000 times. And every time ColCount is called, FlexCel needs to
loop over all the 50,000 rows to find out which row has the biggest column.
So, how do we improve it? The first way could be, as explained, to cache the results of RowCount
and ColCount:
But while this was a huge improvement, it is not enough. By our second rule, xls.ColCount is
slow, and we are calling it 50,000 times anyway. This is better than calling it 1,500,000 times, but
still 50,000 times worse than what it could be. The column count is not going to change while we
loop, so we can cache it outside the row loop:
So, for now we have fixed this code to rules 1) and 2). We cache the end conditions in the loop,
and we avoid calling ColCount much. (A single call to ColCount isn’t a problem). But still, this
code can be incredibly inefficient, and this is where rule 3) comes to play.
In this example, where our spreadsheet has 50,000 rows and 30 columns, we are looping
1,500,000 times. But do we really have 1,500,000 cells? In most cases, we don’t. Remember that
ColCount returns the maximum used column in the sheet. So, if we have 5 columns, but we
also have some helper cells at column 30 (AD), ColCount will return 30:
But the loop will run as if every row had 30 columns, looping through millions of empty cells. If
as in this example we only have 5 columns except for row 1, then we have 5*50,000 + 1 cells =
250,001 cells. But we are looping 1,500,000 times. Whenever you use ColCount for the limits of
the loop, you are doing a square with all the rows multiplied by all the used columns, and this
will include a lot of empty cells.
Note that even when ColCountInRow(r) is fast, we still cache it in the ColCountInRowVariable, as
it doesn’t make sense to call it in every column loop just because. And as the used columns in
every row will be different, we need to call it inside the row loop.
This last version will run millions of ways faster than the first, naïve version.
Reading Files
If you don’t care about the formula texts (“=A1 + 5”), but only about the formula results (“7”),
and you are reading huge files with thousands of formulas, setting:
ExcelFile.IgnoreFormulaText = true
before starting to read the file can improve the performance a little.
NOTE
This particular tip won’t improve performance a lot, and it can make the things more complex
if you ever start needing the formula texts. Make sure that the speed increase you get from it
is worth it.
Reports
Reports are by design a little slower than the API, because they must read a template before,
parse it and compile before filling the cells. So if you need ultimate performance, don’t use
reports, use the API directly. But remember; this advice is similar to “If you need absolute
performance use assembler and not C#”. Yes, you will get the most performance in assembler or
the API than in C# or Reports. But it might not be worth what you lose in flexibility and ease of
use.
The problem here is that FlexCel works by inserting ranges of cells. If you have a template with a
single range, it will insert the rows needed for every record in the database at the beginning, and
then fill those rows. A single insert of n rows, this is very fast.
But if you have a master detail report, it will first insert all the rows needed for the master, and
then, for every record in the master, it will insert all the rows needed for the corresponding
detail. If that detail itself has other detail then the inserts grow a lot.
Normally, for more than 3 levels of master-detail, you shouldn’t have very big reports. Most of
the times this is ok, since when you have huge reports they are normally just the records of a
database. Those will be used for analyzing the data because nobody is going to print or read a
million row report anyway. Complex reports designed to be printed or read normally are smaller,
and for small reports you don’t really need to care about performance.
As always, the best way to know what is faster in your cases might be testing. You might expect
LINQ datatables to be a little faster with huge datasets with no master-details, and datasets to be
faster in small reports with lots of nested master detail levels.
FlexCelReport.AddTable(LoadTable(table1));
FlexCelReport.AddTable(LoadTable(table2));
FlexCelReport.Run();
This is ok if you are going to need both table1 and table2 in the report, but in more complicated
cases where you don’t know in advance which tables might be used it can be a waste of
resources. You might be loading table2 from the database, and only using table1 because the
user checked a checkbox in the application for a “short” report that doesn’t need table2.
If you are using LINQ this isn’t a real problem; you can AddTable() as many tables as you think
you might need, and the data will be fetched from the database only when that data is actually
requested. But with datasets, all data is preloaded before running, so all tables will be queried
from the database.
To solve this problem, FlexCel offers “on demand” loading of datasets through the LoadTable
event. Instead of using AddTable, you can assign the “LoadTable” event in the report and only
load the tables when they are actually requested by the report. For example you could call:
FlexCelReport fr = NewFlexCelReport();
fr.LoadTable += new LoadTableEventHandler(fr_LoadTable);
fr.Run(...);
You could also use DirectSQL or User Tables to avoid preloading the datasets.
WARNING
Let us repeat: This only applies to datasets. When using LINQ, data is always fetched from
the server when (and if) it is needed.
If you are using SQL Server, a simple way to check this is to run the SQL profiler in the server
while running the report.
You should see two queries for every band, the first to query for the number of records in the
band, and the second to retrieve the values. FlexCel needs the first query to know how many
rows to insert in advance, since inserting rows in Excel is a costly operation.
You can see the “Select count” statement highlighted in the screenshot above.
Make sure that the SQL sent to the server make sense. For some tables that have the same data
repeated over and over again it might make sense to cache the results.
For datasets this isn’t an issue, as the dataset has all data in memory: the row count is a very fast
operation just returning the length of the internal array that holds the data in memory. But when
using LINQ datasets, this is not always the case. It is ok for List<…> collections since again
“Count” will be called and that is fast. But in Entity Framework an SQL “Select count * from…” will
be sent to the server, and in the worst case LINQ might need to iterate through all values in the
IEnumerable to get the count.
If the collection that holds your objects is slow to compute the count, and you know the count
anyway, you can tell FlexCel the record count by having a field “__FlexCelCount” in your business
object. For example:
If FlexCel finds a column named “__FlexCelCount” in your collection, it will assume this is the
value of the row count, and it won’t call “Count()” in the collection.
Please note that this is a last resort optimization. Count should be fast anyway for most cases,
and by adding a “__FlexCelCount” field to your business objects, you will probably just making
them more complex without any real gain. We provide this for really special situations, but this
optimization isn’t something that should normally be done.
distributed transactions, as the connection will be opened and closed many times while running
the report. When the connection is opened a second time inside the transaction, it will be
promoted to distributed.
See
http://www.digitallycreated.net/Blog/48/entity-framework-transactionscope-and-msdtc
If your database supports snapshot transactions, and your reports take a while to run, and other
users might want to modify the data while the report is running, snapshot transactions are a
better choice.
Introduction
FlexCel comes with a full PDF writer that allows you to natively export Excel files to PDF, without
needing to have Acrobat or Excel installed. While the output will not be exactly the same as
Excel, a lot of effort has been done to make it as similar as possible.
Using PdfWriter
PdfWriter is a lower level option, and it was really not designed to be used directly, but to
provide the methods FlexCelPdfExport needs to do its job.
But it can be used standalone to create a PDF file for scratch, or most likely to modify the
output from FlexCelPdfExport using one of the FlexCelPdfExport.BeforeGeneratePage or
FlexCelPdfExport.AfterGeneratePage events.
We will not cover it in detail here since methods are documented in PdfWriter, but it is worth
mentioning that there is also an example to get you started in the API Demos.
Using FlexCelPdfExport
This is a higher level option, and the one you would normally use. The simplest skeleton to
export a file to pdf would be:
While the above snippet of code should be enough in many cases, you can add to it in the
following ways:
1. You can set the FlexCelPdfExport properties. You will find there are not a lot of properties
(things like margins, printing gridlines or not, etc) and this is because all this information is
read from the Excel file. If you need to change them, change the associated properties on
the attached XlsFile object.
For example, to change the page margins you can use the ExcelFile.SetPrintMargins method.
TIP
As always, you can use ApiMate to find out how to change the printer settings in the XlsFile
object.
But while all the document-specific settings are stored in the XlsFile object, that doesn't mean
there aren't properties specific to the PDF output. You can change things like the PdfType or
Kerning used in the PDF export. Make sure to take a look at the available properties in the
documentation for FlexCelPdfExport
1. You can export multiple Excel files to the same PDF file. To do that, you use
FlexCelPdfExport.BeginExport, then FlexCelPdfExport.ExportSheet or
FlexCelPdfExport.ExportAllVisibleSheets, and finish the exporting by calling
FlexCelPdfExport.EndExport
Font Management
The font handling when creating pdf files can be problematic, so here we discuss some concepts
related to them.
1. PDF internal fonts. PDF defines 14 standard fonts that must be available to any PDF
viewer, so you don't need to embed the fonts on the PDF document. They will always be
supported by the PDF viewer.
Those fonts include a Serif (Times New Roman-like), a Monospace (Courier-like) and a
Sans Serif (Arial-like) alternatives, on four variants each (regular, bold, italic and bold
italic) and two Symbol fonts.
What those fonts don't include is unicode characters outside the ASCII range, so if you are
using those, you can't use internal fonts. FlexCel will automatically change the fonts to
be true type if your document contains characters outside the ASCII range.
When exporting to PDF, you can choose between three different ways to handle fonts,
depending on the value you set on the FlexCelPdfExport.FontMapping or
PdfWriter.FontMapping property:
1. ReplaceAllFonts. This will replace all fonts on the xls file to the most similar ones on the
14 standard fonts. This way you get the minimum file size and the maximum portability,
but the exported PDF file might not look exactly the same, as you will lose all fancy fonts.
3. DoNotReplaceFonts. This will only use true type fonts. It will be the one that better
matches the xls file, but it will be a lot larger (if you embed the true type fonts) or might
not look good when the user does not have the fonts you used installed (if you don't
embed them)
NOTE
Besides choosing how the fonts will be replaced when creating the PDF, you can also choose
whether to embed the True Type fonts or not. If you embed them, the file will be bigger, but
also will render well when the user does not have the fonts on his machine. To avoid issues, it
is normally recommended that you embed all fonts.
IMPORTANT
If you use Unicode characters, the fonts will always be embedded no matter which
embed option you choose. This is needed to ensure that the Unicode mapping will remain
correct.
NOTE
With the rise of Android and iOS devices you can't just assume that the final user will have
any fonts (not even Arial) installed on the device where he is reading the file.
If you add the fact that the size increase by embedding the fonts isn't that much given the size
of even a modest webpage, we can only recommend you that you just embed all fonts.
FlexCel used to default to not embedding fonts, but since version 6.5 it defaults to embedding
all fonts.
Font Subsetting
When embedding fonts you can choose if to embed the full font, or just the characters that were
actually used in the document. You can control this with the property
FlexCelPdfExport.FontSubset
If you embed just the subset of characters used, the file will be as you might expect smaller. For
big fonts with thousands of characters the subsetting can make a big difference in size.
But on the other hand, if you embed only the subsets, the final users might not be able to edit
the PDF document to add text, since the characters they might want to add might not be in the
embedded subset.
You might see the issue that users won't be able to so easily edit the PDF documents as a
feature, since PDF files are not normally generated for editing. And this is the reason FlexCel
defaults to subsetting all fonts.
But if you care about the users being able to edit the PDF files you create, remember to set
FlexCelPdfExport.FontSubset = true.
1. On Linux (using SKIA, not Linux in Mono), iOS and macOS FlexCel asks the operating
system for the font data directly, and there is nothing that you need to do.
2. On the rest of platforms (Windows , Windows Store and Android) FlexCel looks for the
font folder, and reads the files directly from there.
When reading the ttf files from disk, FlexCel will try to locate the files in the following folders:
• if the OS dafault font folder is empty, then we assume we are running on Linux (under
Mono WinForms), and we will try “/usr/X11R6/lib/X11/fonts/truetype”. Note that when
running in Linux with SKIA we will read the fonts directly from the OS, so we won't be
looking for them in any folder. This applies only for Linux with Mono.
• if the folders above don't exist, FlexCel will search on “<folder where FlexCel.dll is>/
Fonts”.
NOTE
Avoid using the GetFontFolder event if you can, since when you use it your code will not
transparently run on different platforms. A Symbolic link from the FlexCel installation folder to
the fonts should be a more elegant solution.
• Finally, if you have your true type fonts not available as files, but maybe as resources or
entries in a database, you can use the FlexCelPdfExport.GetFontData event to provide
the font data directly instead of a folder where FlexCel will look for the files.
NOTE
If you don't mind using p/invokes, you can use the FlexCelPdfExport.GetFontData event to
call GetFontData on the GDI API, and return the font information to FlexCel. This way you will
avoid scanning the font folder completely, and it can speed up a little PDF export.
While we do not recommend that you go unmanaged for this since scanning the font folder is
fast anyway, you have all options. Do what you prefer.
For a demo on using the GetFontData event, see the ExportPdf demo.
Fonts in Android
At the time of this writing, in Android there are only 4 predefined fonts available for every app
(Droid Sans, Droid Serif, Droid Mono and Roboto). This means that unless you want to use the
internal pdf fonts, your application will have to provide its own fonts.
As in Android you will normally deploy your fonts as assets, FlexCel comes with prebuilt
functionality for handling Assets. You can read more about it at the Fonts section of the Android
Guide
1. You can copy the Excel 2007 fonts to the server, and make sure you embed the fonts
in the PDF file. Note that if you do not embed the fonts, any user who does not have
office installed might not be able to see your PDF file correctly.
2. If you want maximum portability, make sure you change all fonts to Arial or Times new
Roman in your template before exporting.
2. In Excel 2007 or newer: In the home tab in the Ribbon, select Cell Styles, right click in “
Normal” and choose "Modify". Note that depending on your screen resolution, “Cell Styles”
might show as a button or just display the “Normal” box directly in the ribbon.
Make sure the normal style uses a font you have in your server.
The “Normal” style is used not only for empty cells, but to set the column widths. For example,
this is how an empty sheet looks with “Normal” style using Calibri with 11 points:
And this is how it looks using everybody's favorite font, Comic Sans with 28 points:
As you can see, the font used in the “Normal” style is used to draw the headings “A”, “B”, “1”, etc.,
and even more important, it is used to calculate the column width. Column with is measured as a
percentage of the “0” character width in the normal font. If you change the normal font, column
widths will change.
If you do not have the “Normal” font installed in your server, Windows will replace it with a
substitute, and it will probably have a different width for the “0” character, leading to a wrong
column width. So it is important that you have the normal font installed in your server.
You can control what to do when any of these errors happen by hooking an event to the
FlexCelTrace static class. From this event, you could write to a log file when any of these errors
happen, warn the user, or just raise an exception if you want to abort file generation.
This might not be an issue if there are any fonts in the system that are similar to the one being
replaced, but it can be a big issue with Calibri, since that font has very different metrics from the
font it gets replaced (Arial). As an example, here you can see an Excel file using Calibri exported
to PDF in a machine that has Calibri installed an in other that doesn't:
As you can see in the images, Calibri is much narrower than Arial, so the text in cell B2 “This Text
is in Calibri” is cut in the second screenshot. If you are seeing lots of cut text in the server while
the files are exported fine in your development machines, this is probably the cause.
The solution for this problem is easy; make sure you have all the fonts you use installed in your
system. If you want to get notified whenever this automatic font replace happens, you can catch
the FlexCelError.PdfFontNotFound errors in FlexCelTrace, and use it to notify the user he
should install the missing fonts.
“⽇本 に⾏きたい。”
inside a cell and keep the font “Arial”, you will see the correct characters in Excel, but when
exporting you might see blank squares like this:
□□ □□□□□□
The reason for this is that “Arial” doesn't actually contain Japanese characters, and Excel is “under
the hood” using other font (normally MS Mincho) to display the characters. To emulate this
behavior, FlexCel provides a FlexCelPdfExport.FallbackFonts property, where you can enter a
list of fonts to try if the font that was supposed to be used doesn't have the character. If no font
in the FallbackFont chain contains the glyph, you will see a blank square.
The solution in this case is to use fonts that actually have the characters you want to display, or
ensure that some fonts in the FallbackFonts properties have them. By default FlexCel uses “Arial
Unicode MS;Segoe UI Symbol;Yu Mincho;Yu Gothic;Ms Mincho;Ms Gothic” as fallback fonts, but
you can add as many others as you need.
TIP
Windows 10 changed which fonts are available in a default Windows installation. So for
example while in Windows 8 or older Arial Unicode would be a font installed by default, in
Windows 10 it isn't.
It also replaced "Ms Mincho" by "Yu Mincho" and "Ms Gothic" by "Yu gothic".
This is the reason the default Fallbacl fonts in FlexCel include so many fonts: We can't know in
which OS you are going to run it, so we try both the default fonts for Windows 10 and for
older.
If you want to get notified when this happens so you can warn the user to change the fonts, you
can catch the FlexCelError.PdfGlyphNotInFont and FlexCelError.PdfUsedFallbackFont errors
in FlexCelTrace.
That is, a different file for each font variant. If the font comes with only one “Normal” file, the
italics can be faked by the system by slanting the characters, and bold can be faked by adding
some weight to the characters. But of course this leads to low quality results, and should be
avoided whenever possible.
There is other problem with “fake” Italics and Bold, and that is that Acrobat will not show them
when you embed the fonts.
The solution to this problem is to use fonts that include the variants you need.
Normally this is not a problem, since all quality fonts designed to be used with Italics and Bold
already come with files for those variants. But if absolutely you need to use fonts that don't come
with the variants, you might at least not embed the fonts, so the “fake” style will show.
To be notified whenever FlexCel finds a “fake” style, you can use the
FlexCelError.PdfFauxBoldOrItalics notifications in FlexCelTrace.
To set up the language in FlexCel to for example Spanish, use code like this:
pdf.Properties.Language = "es-ES";
As it is an important accessibility feature, since FlexCel 6.5 files are tagged by default. You must
explicitly turn tagging off with FlexCelPdfExport.TagMode in order to get untagged pdfs.
IMPORTANT
Tagged pdfs can be much bigger than normal ones, so this might be a reason to turn tagging
off. If your files are big, try saving with and without tagging to compare the sizes; then decide
if tagging is worth. For small documents, tagging should be kept on.
If you need to choose a version, we would recommend PDF/A2 or PDF/A3. PDF/A1 is a little too
restrictive, and lacks some features that FlexCel could use to generate better files: It doesn’t
support transparency and it doesn’t allow compressing the tags in the document. Due to the lack
of transparency, if you have any transparent image in your file it might look wrong. Due to the
lack of tag compression, files will be much bigger than PDF/A2.
In order to create PDFA files, you need to set PdfWriter or FlexCelPdfExport property
FlexCelPdfExport.PdfType to the correct version. For example:
pdf.PdfType = TPdfType.PDFA1
Then you need to choose if you want to generate “a” (PDF/A1a, PDF/A2a, PDF/A3a) or “b” (PDF/
A1b, PDF/A2b, PDF/A3b) files. “a” files are the most complete, and they require you to tag the
file. "b" files don't require tagging, so they can be smaller if the documents have a lot of pages.
When using FlexCelPdfExport, you would just set the correct option by changing the
FlexCelPdfExport.TagMode property:
When using PdfWriter, you need to manually tag the files, as FlexCel can’t know the structure
from the drawing commands. You need to use the methods PdfWriter.TagContentBegin and
PdfWriter.TagContentEnd to specify the blocks of text you want to tag, and then set the
TagActions property to specify how that tagged content relates to the structure of the file.
Tagging in PdfWriter is an advanced topic outside the scope of this document. Due to the way
PdfWriter is designed, it won’t keep tags in memory and you need to write them directly to the
file as you are creating it.
IMPORTANT
FlexCel signing algorithm is supported only in Acrobat 7 or newer, it will not work in Acrobat 6
or 5 since those versions do not have the required support. Furthermore, SHA512 is only
supported in Acrobat 8 or newer, and SHA1 is known to have vulnerabilities and it is not
recommended to use. Because of these reasons, the header in the generated PDF file will
automatically switch to say that the file is “Adobe 8 or newer” compatible when you include a
signature in your files. If there are no signatures, the default header specifies the file is
“Acrobat 5 or newer” compatible.
It is also worth noting that users will still be able to see the generated files in Acrobat 5, 6 or 7,
but they will get a warning when opening them and the signature will not validate.
Concepts of signing are outside the scope of this document, but you can find a lot of
information in signing in the Acrobat documentation or just in Internet. A good place to start
might be:
http://msdn.microsoft.com/msdnmag/issues/07/03/NETSecurity
The built-in engine is not optimal. It uses the standard .NET PKCS classes, and those classes do
not allow for incremental signing. This means that the whole PDF document must be kept in
memory so we can pass all the contents as a byte array to the signing method in .NET. If you
have a library that supports incremental signing, that is, each time a small group of bytes is
written you recalculate the hash instead of calculating the whole hash at the end, you can save
memory by creating your own engine. Just make sure you need the extra performance before
doing so, because normally the built in implementation is good enough, and has the advantage
of being 100% managed and built-in in the framework.
If you decide to create your own Signer class, you need to implement two simple abstract
classes:
1. TPdfSigner
This is the class that implements the signing. You need to override three methods:
2. TPdfSignerFactory
This class is really simple, and it just should return an instance of the particular TPdfSigner child
you created in 1).
When creating your own classes, it might be helpful to look at the TBuiltInSigner implementation
on file Signatures.cs. Also look at PdfExport demo.
FlexCel implements over 300 Excel functions, and most used formulas are there so you should
not experience big issues. But you need to make sure you do not use any not implemented
function to get a properly recalculated sheet.
Other reason why recalculation might now work is because you are using User defined functions
and you haven't defined those functions in FlexCel.
And the last common cause why FlexCel could fail to recalculate is if you have linked files and
you haven't set a TWorkspace object to calculate those links.
FlexCel comes with a little utility, the demo “Validate Recalc” that will allow you to check if all
the formulas on an Excel file are ok to use. And of course you can use the code on this demo
inside your own application to tell your users when they use a not supported formula.
Introduction
One of the things FlexCel allows you to do is to export your xls/x files to HTML. We do our best
to create the HTML files as closely as possible to the original xls/x file, but there are some
restrictions in HTML that make it not as good as a PDF export. HTML is a “Flow” format, opposed
to PDF (which is a “Fixed Page” format), and this means the browser can resize and reposition
elements depending on the output medium.
With this limitation in mind, we will study now how this is done.
This is a component that allows for “drag and drop” display on a web page of an Excel file,
using ASP.NET Webforms. Internally it uses FlexCelHtmlExport, and provides some extra
functionality to avoid repetitive tasks.
As always, we will not cover those methods or the properties on FlexCelHtmlExport in detail,
since they are described in the reference and it makes no sense to repeat the information here.
This document is about the conceptual part of creating HTML files.
Exporting “workbook.xlsx” to PDF will return in just one file: “workbook.pdf”, but it might result in
3 files when exporting to HTML: “workbook.htm”, “workbook_image1.png” and “workbook.css”
So we need a way to name the different generated files. By default FlexCelHtmlExport will take
the name of the main HTML file as a parameter to the export, and generate the name of the
images with the following pattern:
<ImagesFolder>\<htmlfilenamewithoutextension>_image<imagenumber>.<imageext>
You can change this pattern, but all in all it gives a good default for images being created. You
might use a GUID too as name for the generated images, by changing the
FlexCelHtmlExport.ImageNaming property.
Now, you might want or need more control than this over the filenames created, and
FlexCelHtmlExport gives you that with the GetImageInformation event, where you can specify
exactly which filename you want for each image or even a stream for each one if you are saving
for example the file to a database and not to a file system.
Remember that GetImageInformation will be called for every image in the Excel file, even if they
are not real images. For example, a chart will be rendered as an image, and so it will throw an
OnGetImageInformation event. Also, if you have vertical text and the
FlexCelHtmlExport.VerticalTextAsImages property is true, then this event will also be called when
creating the images with the vertical text.
TIP
When exporting to HTML5, you might choose to embed the images inside the files so there
are no extra files generated.
This is not a good idea for big images, but it can help avoiding the naming issues if your
images are light.
TIP
If you prefer not to use HTML 5 but still would like to embed the images, for example to send
the file by email, FlexCel can also export to MHTML which is a format that supports
embedding the images.
CSS files are other place where you might create extra files. The HTML standard allows
embedding the CSS files inside the generated HTML or to use an external stylesheet, and so
does FlexCelHtmlExport. If you decide to go for an external stylesheet, you need to tell
FlexCelHtmlExport where to place it, by providing a TCssInformation class or a CSS filename to
the export methods.
With FlexCelHtmlExport, you can use the PartialExportAdd method and the
TPartialExportState class for this.
You start by creating a new TPartialExportState instance, where FlexCel will store all the
information it needs to create your files.
Then, you call FlexCelHtmlExport.PartialExportAdd for each sheet or file you want to add to the
HTML file, using the same TPartialExportState instance as parameter.
Once you have added all the sheets and files you want, you can use the methods in
TPartialExportState to get the individual parts of the HTML file.
While normally really straightforward to use (just drop it and set its properties), there is one
thing to take in account, and this is images.
FlexCelAspViewer needs a way to feed the images back to the browser. While it can send the
HTML text back to the main stream when the browser requests an ASP.NET page, it has no way
to send the images to the browser, when the browser requests them after reading the HTML file.
Remember the browser will make many requests, the first for an aspx file, and then for the
individual png/jpg/gif files, as shown below:
In this example we need a way to provide “image1.png” from the ASP.NET server. On the request
for the page (test.aspx) we can create the report and send back the HTML, but we need to persist
all images here, so when the browser asks for them we know how to get them.
We provide three operating modes for images so they can be sent to the browser, and they are
controlled by the ImageExportMode property:
TImageExportMode.TemporaryFiles
This is the simplest mode, and when using it, FlexCel will output all the images to a temporary
folder when the browser asks for the page. The image links in the main HTML file will link to this
folder, and so the browser will be able to get them when it needs them.
Images will be named with a GUID, so even if two different browsers ask for the same report at
the same time, they will get different images.
When working in this mode you will need to implement a “Garbage collection” of images older
than a given timespan, in order to avoid infinite grow of temporary images. You can do this with
some scripts on the server, or use the ImageTimeout property in FlexCelAspViewer.
TImageExportMode.UniqueTemporaryFiles
This mode is not actually suited for wide use, but can be used in controlled environments, where
you can test that the browsers work fine. Not all the browsers will work in this mode. Older
browsers might ask for the image twice, and will not get it the second time
On this mode, as in the first one, images will be saved to a temporary folder when the main page
is requested by the browser. But, instead of links to the image, the generated HTML will contain
links to HTTPHandlers, as shown below:
The advantage over the first method is that images will be deleted by the HTTP handler once
they are served. This provides a more scalable solution, since you do not rely so heavily in the
garbage collection, but on the other hand if a browser re-asks for the same image it will not find
it.
IMPORTANT
It is very important to realize that while this method minimizes the need for garbage collection
on temporary images, it does not eliminate it. You will still need to periodically delete the
images from the images folder!
The reason why there could still be images left even when they are deleted by the HTTP handler
is simple, and it is that the HTTP handler might never be called. Let's imagine the following
“broken flow”:
As in the picture above, the browser requests an html file from the ASP.NET server. And as
above, the ASP.NET server returns the html file with links to the handlers for the images.
But before the browser actually requests the handlers in the HTML file, the user loads other web
page. In this case, the “flexcelviewer.ashx” will never be called, and the image never be deleted.
Now let's imagine a malicious user, trying a “denial of service” attack to your server. He could
make a script that asks for a page in your server, but never calls the handlers, creating lots of
temporary files that will never be deleted. In fact, just refreshing his browser manually hundreds
of times will get this effect. In each refresh a new html page is generated (along with all the
corresponding temporary images), but as he presses refresh again before the page is loaded, the
image handlers will not be called and the images will not be deleted.
So, while this method provides a better mode for normal use (where the user requests the page
and waits for the images), it is no better to prevent malicious attacks. FlexCelAspViewer provides
a property, MaxTemporaryImages, that can be used to mitigate this effect, by deleting older files
once you have too much temporary files in the disk. It is recommended that you set
MaxTemporaryImages to a reasonable value.
TImageExportMode.CustomStorage
This is a more advanced method that allows you complete control over how the images will be
delivered to the browser. It works by creating links to ASP.NET HttpHandlers and allowing you to
customize those HttpHandlers to your needs.
It is in some ways similar to the UniqueTemporaryFiles method in that the generated HTML file
will have links to HTTP Handlers instead of images, but it is your job now to implement it.
1. You need to specify the image links for the HTML file, providing the parameters you need
in order to get the images when the http handler is called.
2. You need to implement the HTTP handler that will return the images to the browser. It
could use temporary files as the second mode, or it could cache them in memory, a
database or wherever you want.
In order to specify the image links you need to assign the event ImageLink in the
FlexCelAspViewer component.
You have complete freedom in how to implement your HttpHandler as long as you implement
the IhttpHandler interface, but you can derive your class from FlexCelHtmlImageHandler to avoid
writing the basic code. Code in FlexCelHtmlImageHandler has been adapted from the article:
http://www.hanselman.com/blog/PermaLink,guid,5c59d662-b250-4eb2-96e4-
f274295bd52e.aspx
and it provides the basic boilerplate common to all Http Handlers. We would like to thank Scott
Hanselman for it.
When implementing the handler, you will need to use the SaveImage event in FlexCelAspViewer
in order to get the images you will need to serve later.
IMPORTANT
Even when it is mentioned in the FlexCel reference, we think this is important enough to
repeat here: Take a lot of care when creating your own custom handler. A naive
implementation might provide a malicious user access to any file in the server, if you fail to
validate the parameters correctly.
And manually place logo.gif in the images folder, so it does not have to be generated each time.
You can do even more things. If you set the ImageFile parameter to an absolute value (for
example “c:\www\images\logo.png”), and the link to point there (for example “/images/
logo.png”) each time you create a file those images will be recreated so you do not have to care
about manually updating them.
The only issue here is that when two users ask for the same image at the same time, one will get
a sharing violation because the image is being created by the other thread.
FlexCelHtmlExport has a property “IgnoreSharingViolations” that is true by default and fixes this
issue. When a file is locked by another thread, FlexCelHtmlExport will assume this is because
there was other thread writing the same image, so it will just not write it.
In the example, if two threads try to write to “c:\www\images\logo.png” at the same time, the
first one will succeed and the other will silently fail and continue with the rest of the file. When
the browsers then ask for “/images/logo.png” both browsers will be server with the same image
that was written by the first thread.
General Customization
Customization is a very important part of creating HTML files, since differently from xls/x or pdf
files, HTML files normally have to be integrated with an existing site. This means the style of the
generated pages must match the general look and feel of the whole website, and we tried not to
cut any corners in letting you do so.
1. You can get basic customization of colors and properties by changing the
TStandardSheetSelector.CssTags property in the TStandardSheetSelector class.
2. If the basic customization is not enough, you can completely redefine the css properties in
a TStandardSheetSelector class. There is a general property for the styles that apply to all
the position in the selector (TStandardSheetSelector.CssGeneral) and then customized
properties for all the other positions.
For example, you could set border black for all TStandardSheetSelector.CssTags with
CssGeneral style, and then redefine the border as blue when the tab is on the left by using
the TStandardSheetSelector.CssWhenLeft property.
You can use the built-in macros when defining your CSS properties and you can even
define your own. For example, you could define:
As explained, you can even define your own macros. For example you could use
And then add “floatpos” to the CssTags array. This way you will be later be able to change
your own styles by replacing the macro values. The syntax for using a macro in a CSS
definition is “<#macroname>”, and then you need to add it to the CssTags array.
3. Normally methods 1. and 2. should be enough to handle most needs, but if you need a
completely new way to display your tags, you can just define your own SheetSelector by
deriving it from the class TSheetSelector. You will need to override the abstract methods
in this class to provide the behavior you need. In fact, this is the way
TStandardSheetSelector is defined, and you can look at its code when creating your own
SheetSelector class.
So it is recommended that you stick to standard fonts, like Arial or Times New Roman on the xls/
x files you want to export. But if the files already exist and have non standard fonts, you can use
the FlexCelHtmlExport.HtmlFont event to convert the fonts to standard typefaces. You can search
for an example on this in the Export HTML demo.
IMPORTANT
The quote character FlexCel uses in style tags is the single quote ('). So, if you need to quote
your font names because they have spaces, use a double quote (") so the style declaration is
not affected.
For example, a style declaration in FlexCel might be: style = 'font-decoration: "my font"; '
You need to use double quotes around "my font" so it does not end the style declaration.
You can do this using the FlexCelHtmlExport.BaseUrl property. If for example you
define”BaseUrl” to be "http://www.tmssoftware.com/", whenever a link starts with that text, the
text will be removed. In our example, the link:
"site/flexcelnet.asp"
SVG is well understood by all modern browsers, so it might be a good idea to turn SVG
exporting of images on. To do it, change the FlexCelHtmlExport.SavedImagesFormat property to
THtmlImageFormat.Svg.
Introduction
The FlexCel code you have to write for iOS itself is very similar to normal FlexCel for Windows
code.
And this is the code needed to create the same file in iOS:
There are really not much differences, and this applies to most FlexCel code you can write: It is
exactly the same. So why are we covering iOS in a separate document? What else can we say
that is not covered in the other documents?
Well, while most FlexCel code will be the same, there is a fundamental difference between iOS
and Windows: Files in iOS are in a sandbox. You can’t just open a file in “My Documents” and
save it in another place in the disk. Actually, you can’t access any file outside the folder where
your application is installed. How to deal with the file sandbox is what we are going to cover on
the rest of this file.
For this reason, in iOS your application can only read and write to the folder where it is installed
or its subfolders. This gives you the added advantage that when you uninstall the app it is gone
completely, as it can’t leave garbage all over your hard disk. But on the other side, how do you
work with a restriction like this? How do you create a file in Excel, open it with FlexCel, modify its
values and give it back to Excel, if FlexCel and Excel can’t see each other at all?
The first way to share things is for special files: Apps can access certain other files such as
address book data and photos, but only through APIs specifically designed for that purpose. But
this isn’t a general solution, and while it might work for images, it won’t work for xls/x or PDF
files.
1. In order to do anything useful with the xls/x, pdf, or html files FlexCel can generate, you
need to Export them to other apps.
2. To be able to read files from other apps like dropbox or the email, you need to Import the
files from the other apps.
With this Import/Export system, your application can’t open any file that wasn’t given to it. In
order to open a file, the user needs to export it to your app.
• <Application_Home>/Documents/ This is where you normally will put your files. Backed
up by iTunes.
• <Application_Home>/Documents/Inbox This is where other apps will put the files they
want to share when exporting to your app. Read only. Backed up by iTunes.
• <Application_Home>/Library/ This is for the files that ship with your app, but not for
user files. You could for example put xls/x templates here.
• <Application_Home>/tmp/ The files you write here might be deleted when your app is
not running. Not backed up by iTunes.
Those are at a glance the most important folders you need to know about. You can get a more
complete description of the available folders in the Apple documentation.
For handling xls or xlsx files, you would need to add the following to your Info.plist:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Excel xlsx document</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>xlsx</string>
<key>public.mime-type</key>
<string>application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.tms.flexcel.xlsx</string>
</dict>
</array>
</dict>
</plist>
You can either do this manually by entering the xml in a code editor (info.plist is just an xml file),
or add this from Visual Studio, by clicking in info.plist:
You can find step-by-step information on how to register your app in the iOS tutorial.
When you configure everything, your app will appear in the “Open in” dialog from other
applications on your phone:
After that iOS will start your app, and send it an application:OpenUrl: message so you can
actually open the file. So in order to do something useful, you will need to listen to
application:OpenUrl: event.
Luckily this is straightforward; just open the file AppDelegate.cs in your project and add the
following lines:
NOTE
OpenURL would get the URL of the file (something like “file://localhost/folder...”) instead of a
filename. The FlexCelView example shows how to convert the URL into a path, and how to
handle an OpenUrl event.
And the “Print” button will appear among the other options inside the share sheet.
Backing up files
A note about the files you use with FlexCel. Not all of them might need to be backed up, and
Apple considers it a reason for App Store rejection if your application is backing up static files
(as this will increase backup times and sizes for all users).
If you are using xls or xlsx files as templates for your app, but they aren’t actual data and
shouldn’t be backed up, you should use the NSURLIsExcludedFromBackupKey or
kCFURLIsExcludedFromBackupKey properties to exclude them from backup.
You can find more information about this topic at: https://developer.apple.com/library/ios/#qa/
qa1719/_index.html
<key>UIFileSharingEnabled</key>
<true/>
To your Info.plist file. Once you add this entry, your app will appear in iTunes and the user will be
able to read and write documents from the “Documents” folder of it. The interface is kind of
primitive, but it gets the work done.
NOTE
Again as in the case of registering the application to consume some file types, the Delphi IDE
doesn’t allow you to do it directly. You need to add a Boolean key, and Delphi will only add
string keys. So, again you will have to create a different Info.plist and merge it, as we did in “
Registering your app with iOS”. If you are already doing so to registering files to import, then
you can use that same file to add this entry.
WARNING
If you decide to enable iTunes sharing for your app, make sure that the documents in the
“Documents” folder are actual documents the user cares about, and put the others somewhere
else, like in “Library”. Failing to do so can result in a rejection from the App store. (as you can
see here: http://stackoverflow.com/questions/10767517/rejected-by-uifilesharingenabled-key-
set-to-true )
Note that you can only put files inside your app bundle, you can’t put a file directly in the
“Documents” folder. Because you will not be uploading the Documents folder to the app store;
you will upload only the app bundle.
If your application is called “MyApp” and the folders look something like this:
Then you can only put files in the green folders in the diagram. This is because “MyApp.app” is
what will be distributed to the App store, and what your users will download when they
download your app.
So, how do we put a file on the blue folders in our distribution package? The usual technique is
iOS is to copy them to some folder inside MyApp.app, and on startup of your application, copy
those files to “/Documents” or “/Library”
FlexCel will work in most cases with the reduced number of encodings, but there are some rare
cases where we need the full list of encodings. An example is when reading an Excel 95 file, and
there are a couple of other cases more.
So in order to not have problems with non existing encodings, it might be a good idea to click in
your project properties and add "west" encodings.
• In iOS: Go to Build->iOS Build and select the "Advanced" tab. There in the
"Internationalization" section choose "west".
• In Android: Go to Build->Android Build and select the "Linker" tab. There in the
"Internationalization" section choose "west".
If you are worried about the extra size you can skip this step. FlexCel will still work in most of the
cases without the extra encodings.
Introduction
As it is the case with iOS and most of the platforms we support, the FlexCel code you have to
write for Android itself is very similar to the code for normal "Full-Framework" .NET apps in
Windows. We’ve reused most of the code from FlexCel in our Android implementation, and you
can still use APIMate, the Demos, everything that you could in Windows when coding for
Android.
Fonts
The biggest difference with Windows apps is that Android by default has a single font: “Roboto”.
When exporting documents to PDF the lack of extra fonts can be a problem.
1. If you are only using ANSI Windows-1252 characters and a single font, and you don’t
care about the exact font used, you can use the code:
FlexCelPdfExport.FontMapping = TFontMapping.ReplaceAllFonts;
A PDF viewer is required to understand 3 basic fonts: A Sans-Serif (Arial or Helvetica like),
a Serif (Times or Times New Roman like), and a proportional font (Courier or Courier New
like).
So if you don’t embed any font and are fine with using the internal fonts, you can get a
smaller file and forget about other issues by replacing the fonts by internal ones. But note
that this will only work if you are not using characters outside the ANSI
Windows-1252 range: The internal fonts don’t support Unicode, so if you have Unicode
characters, you need to provide a font that has the needed characters.
2. You can provide FlexCel the fonts you need in the place where FlexCel expects them. The
simplest way to do that is to deploy the font files needed as internal assets in your
application.
To do it, in the project manager of Visual Studio, right click in the Assets folder and create
a “fonts” folder behind it:
WARNING
The path is case sensitive: “Fonts” instead of “fonts” will not work.
IMPORTANT
At the time of this writing there is a bug in Visual Studio 2017 which will raise an error *
MSB3025: The source file Assets/fonts is actually a directory* when you add a folder to the
assets.
If you are getting this error, you need to manually edit the csproj after adding the fonts folder
to the Assets, and remove the line:
Also note that all assets should have their “Build action” set to “AndroidAsset”:
IMPORTANT
Make sure you have the rights to distribute the fonts that you deploy.
1. If you deploy your fonts to a “fonts” folder inside your app assets as explained in the point
above, then you have nothing more to do.
But if your app already deploys the fonts to some other folder, you can use the
FlexCelPdfExport.GetFontFolder event to change where FlexCel will look for the fonts.
For example, if your fonts happen to be inside a folder in your assets named Shared Fonts,
you could use the following code:
pdf.Export("result.pdf");
}
Take a look at the “@” sign the code above. To use fonts that you included as assets, you need
to return a string starting with a “@” sign in the GetFontFolder event.
So for example to specify that the fonts are in the "fonts" folder asset, you would return "@fonts"
on the OnGetFontFolder event. (“@fonts” is the default used by FlexCel and covered in point 2 of
this document).
TIP
Of course you can specify any folder in the Android device, not only assets. If the fonts are not
in assets, just specify the full folder without a starting @.
FlexCel will work in most cases with the reduced number of encodings, but there are some rare
cases where we need the full list of encodings. An example is when reading an Excel 95 file, and
there are a couple of other cases more.
So in order to not have problems with non existing encodings, it might be a good idea to click in
your project properties and add "west" encodings.
• In iOS: Go to Build->iOS Build and select the "Advanced" tab. There in the
"Internationalization" section choose "west".
• In Android: Go to Build->Android Build and select the "Linker" tab. There in the
"Internationalization" section choose "west".
If you are worried about the extra size you can skip this step. FlexCel will still work in most of the
cases without the extra encodings.
1. Using the library at the folder lib\winrt81. This library is very different from the rest of
FlexCel (more on that below), and it is not recommended to use. In fact, it might be
deprecated in the near future. But currently it is the only way to natively support Windows
8.1, so if you need Windows 8.1 support this might be your choice.
2. Using the nuget package or the library at lib\uwp10. Different from the first option, this
requires Windows 10 and doesn't support Windows 8.1. But on the other side, it has a
shared API with the rest of FlexCel platforms which makes it much simpler to share code.
3. Using .NET Core.
Same as with iOS and Android, the code needed to use FlexCel in Windows Phone or Universal
Windows Platform (UWP) apps is very similar to the code needed to use FlexCel in Desktop
.NET. But due to the differences in the underlying framework, the first option (FlexCel for
Windows Universal 8.1) is particularly different. We will cover the options more in detail below.
This option is deprecated because it requires an asynchronous API different from all the other
platforms. The only reason to use it is if you need Windows 8.1 support besides Windows 10
support.
FlexCel for Windows Phone and UWP comes as a Portable Class Library, so you can use the same
dll in both Windows Phone and Windows Store. Due to limitations in the graphics support, we
don’t support this Portable class library for other platforms. For iOS and Android we provide a
specialized iOS dll which directly calls CoreGraphics for the graphic calls, and a specialized
Android dll which calls Skia instead.
Requirements
FlexCel for Windows Phone and Store requires at least Windows Phone 8.1 or Windows 8.1
For development, you need Visual Studio 2013 or newer. For Visual Studio 2013, you also need
to install Update 2. This is because we use the new Portable class libraries that are available in
Update 2. The older ones were too limited for proper xlsx support.
The .NET framework for WinRT and Windows Phone don’t include a FileStream class. Instead you
have to use the StorageFile class for accessing files. IO is also asynchronous, which means that
you need to await the calls.
Going Async
While in Desktop .NET you would use the following code to open a file:
xls.Open(FileName);
In Windows Phone/Store, you will need to use something like the following:
await xls.OpenAsync(f);
Something similar happens for saving. As StorageFile isn’t supported in Desktop .NET, we can’t
support the same call for both.
Saving and loading files are the two most important differences, due to the differences in the
filsystems, and the async nature of WinRT/Phone IO API.
Limitations
The Graphics stack in portable libraries is very limited, and we couldn’t implement all the
functionality that is available in other platforms. For starters, graphic code needs to run in the UI
thread, and it also lacks a lot of functionality. The good news is that FlexCel doesn’t rely much in
graphic calls, so this limitation doesn’t affect most of the FlexCel codebase. But things like
exporting to images are limited. When exporting to html, we recommend exporting the images
to SVG for this reason. SVG exporting doesn’t use many graphic calls and mostly outputs xml,
while exporting to png or jpg is much more demanding.
There isn't much to add about FlexCel for UWP 10. It works the same as always and you can
reuse the code you wrote for other platforms.
Encodings
Depending in the exact platform you are using, you might get an error of missing encoding
when calling FlexCel.
With the FlexCel NuGet package encodings are automatically added through NuGet, so you
should see no issues.
If you are manually referencing the portable dll you should do the following to avoid Encoding
issues:
Installing
For developing for Linux under MonoDevelop, you will have to manually copy FlexCel.dll to your
development machine and add a reference to the assembly. Or if you prefer to develop in Visual
Studio, then you don’t need to do anything. Just compile your app in Windows under Visual
Studio, and copy the files to the Linux server. There is no need to recompile under Mono, as
Mono can read the dlls that Visual Studio generates.
Fonts
Probably one of the biggest problems you might get when using FlexCel in anything but
Windows or OSX is the lack of fonts. Most of the default fonts in Excel are only present in
Windows, and when you want to create a PDF file with them you will get a file that looks wrong.
FlexCel needs to read the fonts to know the metrics, where the next character will go, where the
line breaks are, etc.
We discuss how to deal with fonts when exporting in the Font Management section of the Pdf
exporting guide; make sure you read it if you are thinking in deploying to Mono.
Bugs
In a perfect world we wouldn’t have to write this section. But this is not a perfect world, and
while Mono is a high quality piece of work, it has bugs, as does .Net or FlexCel itself. The
problem being, that while .NET has bugs, you have probably tested in .NET, and workarounded
them. But Mono has a different set of bugs, and the only way to know which, if any, affect you, is
to test in Mono. We do regular testing in Mono, and most tests pass ok. But you might find
issues, so test heavily.
.NET Core
.NET Core is positioning more and more as the best way to move your applications to Linux. But
right now it lacks some features you might need in a full featured app, and it is not as mature.
But note that FlexCel for .NET core uses SkiaSharp as its graphics engine, and this has the
following implications:
1. Your app will have a dependency with the SKIA dll in SkiaSharp. This will make your
appliation larger.
2. Your app will use SKIA instead of the native graphic engines in the OS.
◦ For Android this is quite similar since the native graphics engine in Android is SKIA,
but still you won't be using the engine bundled with android, but the one in the
SKIA dll from SkiaSharp.
◦ For iOS and macOS, it is probably better to use the iOS/macOS FlexCel dlls directly,
since they use CoreGraphics which is a very capable framework and there is no
need to use SKIA.
◦ For Windows it is hard to say. The current implementation of FlexCel using
System.Graphics (which uses GDI+) has better support for some things, like right to
left languages. But on the other side, GDI+ is not officially supported in a server
even when it works. So the choice is yours.
◦ For Universal Windows Apps, it is probably better to use SKIA, even if it will make
your applications larger. The graphics stack in UWA lacks a lot of functionality and
the SKIA implemantation is more complete.
3. Your application won't be fully managed since it will be calling the native skia dll. This
might have security implications.
4. Your application will work the same everywhere, since it will use the exact graphics
framework everywhere. The native components might have very small differences in how
they render the text or graphics depending in the underlying graphics engine.
5. At the time of writing you need to manually compile SkiaSharp in Linux, as it isn't available
in the Nuget Package. See https://github.com/mono/SkiaSharp/issues/312 and https://
github.com/mono/SkiaSharp/wiki/Building-on-Linux
Tutorials
In this section there is a list of tutorials to help you get started.
In this section:
Creating an iOS application
iOS Tutorial
Overview
In this tutorial we will create a small xls/x document viewer. The app isn't particularly useful or
flashy, but it is designed to show most concepts you need to know in order to work with files in
iOS
NOTE
The complete source for this tutorial is available as FlexView demo in the FlexCel
distribution.
• Select Installed (1), Templates (2), iOS (3) and then Universal (4)
• Select Single View Application (5)
• Give it a name (6). In this tutorial we will use FlexView.
Then you can go to the application properties, and set icons and the application name.
You might now try running the application, it should show as an empty form in the simulator or
the device.
Double click in the file Main.storyboard. Depending in your version of Xamarin or Visual Studio
and in your preferences, the file might open in XCode or the Xamarin designer. We’ll be showing
the Xamarin Designer here, but the XCode steps are similar.
In the designer, drop a WebView and a Toolbar. Adjust the anchors so the WebView resizes with
the window:
NOTE
If using XCode, create an Outlet for the webview named “Viewer” by ctrl-dragging the
webview to the assistant view.
And finally, set Scales Page To Fit = true in the web properties to enable pinch to zoom:
This can be done directly from the Xamarin Studio Info.plist editor, but for this example we’ll just
open the new generated Info.plist with a text editor, and paste the following text before the last
</dict> entry:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Excel document</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.microsoft.excel.xls</string>
<string>com.tms.flexcel.xlsx</string>
<string>org.openxmlformats.spreadsheetml.sheet</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Excel xlsx document</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>xlsx</string>
<key>public.mime-type</key>
<string>application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.tms.flexcel.xlsx</string>
</dict>
</array>
Once you have done this, if you run the application and have for example an email with an xls or
xlsx file, you should see “FlexView” in the list of possible applications where to send the file when
you press "Share".
What happens when you press the “Open in FlexView” button is that iOS will copy the file in the
“Documents/Inbox” private folder of FlexView, and send an OpenURL event to our app. We need
to handle this event, and use it to load the file in the preview.
TIP
You might just write “override” inside the AppDelegate class, an Visual Studio will show you a
list of possible methods to override.
NOTE
In iOS, we are going to get the URL of the file, not the filename. For example, the URL could
be:
'file://localhost/private/var/mobile/Applications/9D16227A-CB01-465D-B8F4-AC43D70C8461/
Documents/Inbox/test.xlsx'
But while iOS methods can normally use an URL or a path, C# FileStream expects a path. This is
why we need to convert the URL to a path, using the url.Path.
For this, open the file FlexCelViewViewController.cs, and write the following uses at the start:
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using FlexCel.Render;
using FlexCel.XlsAdapter;
using System.IO;
NSUrl XlsUrl;
string PdfPath;
Viewer.LoadRequest(new
NSUrlRequest(NSUrl.FromFilename(PdfPath)));
}
catch (Exception ex)
{
Viewer.LoadHtmlString("<html>Error opening " +
System.Security.SecurityElement.Escape(
Path.GetFileName(XlsUrl.Path))
+ "<br><br>"
+ System.Security.SecurityElement.Escape(
ex.Message)
+ "</html>", null);
return false;
}
return true;
}
If you run the application now and press “Open in FlexView” from another application, FlexView
should start and display the file. You should be able to scroll and pinch to zoom.
But when coding for a phone, things are different. In this case, the memory of the device is
limited, and the flash storage is fast and huge (it has to be able to store music and videos).
So, if memory is our constrain, it makes more sense to create a temporary file and read from
there. While FlexCel will keep the spreadsheet in memory when opening, it won’t keep the pdf
file, which is generated on the fly. So saving this pdf file to a temp place can reduce the memory
usage a lot.
Another thing to notice is that we should not write this temporary file to the /Documents folder,
because this isn’t user data, and it shouldn’t be backed up by iTunes. That’s why we write it to
Library/Caches
And the last thing we are missing here, is to remove the file once the WebView loaded it. While
this isn’t strictly necessary (iOS will remove files in this cache when it needs space), it is a good
practice.
We could remove the file in the Viewer.LoadFinished event, but since we plan to share the file to
other apps in the next steps, we will keep it longer. So we will delete the old file before writing a
new one.
And call RemoveOldPdf() as the first line in the Refresh() method we wrote above. Note that we
could have also saved always to the same filename, so we wouldn’t need to worry about deleting
files at all. But when sharing the file, the filename would be lost.
NOTE
While we won’t cover this here, another advantage of using a temporary file is that if your app
gets killed by the OS, you can restore the state from the temporary file when restarted.
In this particular case, the only difficulty is that we can’t overwrite the original file at Documents/
Inbox. It is readonly. So we will save it to the Caches folder as tmpFlexCel.xls or tmpFlexCel.xlsx
depending on the file format.
Saving it with the same name allows us to not care about deleting the temporary file because it
will always be the same. But on the other hand, we need to store the original name of the file so
the generated pdf file has the right filename.
string XlsPath;
And we’ll replace the old XlsUrl for XlsPath in all places where it isn’t dealing with the pdf file.
Finally, we’ll add a button and write this in the event handler:
xls.Save(XlsPath);
Refresh();
}
Go to the UI designer, locate the “item” button in the toolbar, rename it “Share”, and add the
following code in the button event handler:
This should show a dialog in your app to share the file with other apps. And it might be what we
want in many cases. But this dialog doesn’t include the options to “Print”, or “Mail”, which might
be interesting to show too.
To show the extended options, change the last line in the code above from PresentOpenInMenu
to PresentOptionsMenu:
docController.PresentOptionsMenu(ShareButton, true);
NOTE
Exporting to pdf will show a “Print” option when sharing the file, allowing us to print it.
Modifying the app so it allows you to change sheets isn’t complex, on the FlexCel side you just
need to use ExcelFile.SheetCount and ExcelFile.GetSheetName(sheetIndex) to get an array of the
sheets, then use ExcelFile.ActiveSheetByName to set the sheet you want to display.
But while not complex, there is a lot of plumbing needed for that: we need to define a new view
in the storyboard, we need to populate a table view with the items, and select the correct one
when the user selects a new sheet. Sadly this is a lot of code, and would mean making this
tutorial twice as big with little FlexCel code and a lot of UI code that you can get tutorials
everywhere, and so we won’t be showing how to do it here. It would make the tutorial much
more complex and add very little to it.
We will be adding new tips regularly here, so make sure to check this page from time to time.
In this section:
Embedding Excel files in your application
Don't use APIMate for this.
Using barcodes
Here we discuss the different ways to embed a barcode in your file using FlexCel.
Why are xlsx files generated by FlexCel smaller than the same files
generated by Excel?
You open an existing file with FlexCel and save it. Now the file is some kilobytes smaller. What is
happening?
We also tell you about a simple way to check if all the formulas in a range have consistent values
(so the formula in A1 refers to B1, and the formula in A2 refers to B2 and so on)
One way you might think of to include the xls file is using APIMate to "convert" the file into code,
then paste that code inside your application. But while that would work, it isn't really a nice
solution:
• The code generated by APIMate is very repetitive. So for example if you have the cells
from A1 to A100 with the numbers 1 to 100, APIMate will write 100 calls to
ExcelFile.SetCellValue(1, 1, 1), ExcelFile.SetCellValue(2, 1, 2), and so on, instead of calling
ExcelFile.SetCellValue(row, 1, row) in a loop with row from 1 to 100. The idea of APIMate is
to show you how to do stuff, so for example you want to know how to conditionally
format a cell, you conditionally format it in Excel, then open the file in APIMate and look at
the code APIMate generates. But it is not great to actually convert files to code.
• For the reason above, the code will be big, resulting in a bigger executable than if you
embedded the file directly, and it will be slower to compile too.
• Embedded files as resources will normally open faster than the code that APIMate
generates uses to create the file. Again, the code generated by APIMate is not designed
for this, and it can be inefficient as it sets every cell with a different call.
• Resources are simpler to update. Just edit the file in Excel, save it and rebuild your app.
While if you used APIMate, you would have to relaunch APIMate and recopy-paste the
code every time.
2. In the dialog to select the file set the filter to All Files (*.*) and select the xls/x files you
want to embed.
3. In the solution explorer, right click in the file(s) you added and select "Properties":
Where the name of the resource is "Namespace.NameOfTheFile" (In this case the namespace is
"Resources" so the name of the resource is Resources.test.xlsx)
If you can't figure out the name of the embedded resources, you can use the following method:
This code will return the names of all embedded files in your executable.
This sounds like a nightmare for sharing files: I write a file with my English Excel, using a format
like "yyyy-mm-dd" so the cell looks to me like "2000-01-01", but when I share it with you and
you open it in your Spanish Excel, you will see "yyyy-01-01" in the cells instead of "2000-01-01"
since the file is using "y" for year and it should be using "a" for año.
But luckily it is not that bad. Internally, in the file all formats are stored in English and converted
on the fly by Excel when showing them to you. This means that inside the xls or xlsx file the
format will always be stored as "yyyy-mm-dd". When you open it in a Spanish Excel, Excel will
display it as "aaaa-mm-dd". When you enter "aaaa-mm-dd" as a format in a cell in a Spanish
Excel, it will be saved as "yyyy-mm-dd" inside the file. So the file can actually be shared between
different languages, without issues. Some users will see "yyyy-mm-dd" when they open it and
others will see "aaaa-mm-dd", but the file is the same internally.
This is the reason you always have to enter format strings in English when using FlexCel. It
doesn't matter if you have a Spanish Excel installed and you see the format strings using Spanish
names, what goes on the file is always English format strings. You can use APIMate to get the
English format strings that you need to use in FlexCel even if you have an Spanish or other
language Excel. Just enter the format string in your language in Excel, save the file, and open it
with APIMate. It will show you the English format string that you need to use.
WARNING
While numeric formats in cells will be automatically converted as mentioned above, you have
to be extra careful when using functions like Text
In those cases the format will not be automatically translated. The formula =Text(A1,"yyyy-
mm-dd") will not work correctly when opened in a non-English Excel. Similarly, the formula
=Text(A1,"aaaa-mm-dd") will work in your Spanish Excel, but users with other languages will
see it wrong.
Sadly not always. The most commonly used format strings are not stored as strings inside a file,
but as format ids. This is probably a file size optimization for times where floppy disks had 1.44
mb, but it still is like this today. And as you might guess, those internal formats are also a
problem, because they change with the language of Excel.
So for example, format number 20 is "h:mm" in an Excel from the USA, but "hh:mm" in an Excel
from England. If you set the cell format in USA to be "h:mm" and then send the file to a
customer in England, he will likely see "hh:mm" when he opens your file.
NOTE
The internal formats, except for formats number 14 and 22, are hardcoded into Excel itself,
they don't change with the machine locale. This means that if you buy an USA version of Excel,
it will always read format 20 as "h:mm" even if you are in a machine with a British locale and
your Windows settings are "hh:mm".
Formats 14 and 22 are different, and we cover them in the next section.
This creates a problem for FlexCel. What should we display when we find a format 20 in a file?
There are no "American" and "British" versions of FlexCel, there is a single flexcel.dll file that is
the same for all users over the world. So we can't hardcode "h:mm" for American versions of
flexcel.dll and "hh:mm" for British versions.
What FlexCel does instead is to behave as an American Excel by default, but letting you
customize the built in format to whatever you want. The method you use for that is
XlsFile.SetBuiltInFormat
Please read the link above to see a list with every internal format and how it is interpreted by
FlexCel.
So if you save a file with format "mm/dd/yyy" in an American locale, this format will be saved as
format 14. If you now change the Windows locale so dates are "dd/mm/yyyy", the same Excel
will show the file differently.
Those two internal formats are shown by Excel starting with an "*", and it is explained in the
format dialog that those formats change with the locale.
There are actually four formats that change with the locale. Two of them are for dates (long and
short form) one is for date and time, and the last one is for time. Two of them use an internal
format (14 and 22), the others use a [$-F0000] syntax.
1. Short Date: This is the internal format number 14. To set a cell to this format, set the
format string to be RegionalDateString. RegionalDateString is a FlexCel variable which will
change depending on your ShortDatePattern in your locale settings.
NOTE
This format is not directly the format string in your Windows settings converted to an
Excel formatted string. While it will read the format string from the Windows settings,
only some formats are allowed and extra stuff might be ignored. For example, if the
Windows format string is "mm/dd/yyyy dd/mm" the Excel string will be "mm/dd/yyyy".
2. Long Date: This is not stored as an internal format, but as a format starting with the string
"[$-F800]". What goes after [$-F800] doesn't really matter, but normally it is the format
string for a long date in the language Excel was when creating the file. This allows third
party Excel readers which don't understand the [$-F800] string to still display something.
So for example you could have the format string: "[$-F800]dddd,\ mmmm\ dd,\ yyyy". As
it starts with [$-F800], it will be converted to a regional date by Excel (or FlexCel), and what
is after the [$-F800] will be ignored. But some other third party Excel viewer which doesn't
know about this will ignore the [$-F800] and still display a long date, just not correctly
localized.
3. Date and Time: This is the internal format number 22. To set a cell to this format, set the
format string to be RegionalDateTimeString. RegionalDateTimeString is a FlexCel variable
which will change depending on your locale settings.
4. Time This is not stored as an internal format, but as a format starting with the string "[$-
F400]". What goes after [$-F400] doesn't really matter, but normally it is the format string
for a time in the language Excel was when creating the file. This allows third party Excel
readers which don't understand the [$-F400] string to still display something.
So for example you could have the format string: "[$-F400]h:mm:ss\ AM/PM". As it starts
with [$-F400], it will be converted to a regional time by Excel (or FlexCel), and what is after
the [$-F400] will be ignored. But some other third party Excel viewer which doesn't know
about this will ignore the [$-F800] and still display a time, just not correctly localized.
NOTE
Those formats will change in FlexCel depending in your machine locale, just as they would in
Excel. But you can change what FlexCel uses without needing to change the Windows locale.
See the link about how to change the FlexCel locale
TIP
Besides the formats [$-F800] and [$-F400] you can actually use any locale with the same
syntax. For example, the string: [$-409]m/d/yy h:mm AM/PM;@ is formatted with English locale.
Those formats are what is shown at the bottom of the "Format cell" dialog, and FlexCel also
fully supports them.
But those formats don't change with your machine locale: They show the same everywhere so
they aren't really a problem and this is why we don't discuss them in this tip.
FlexCel behaves the same: If you set the format to be "h:mm", it will realize that it matches
internal format 20, and save the internal format 20 into the file.
So how do you set the format to be "h:mm" for everybody, and not the Excel-Locale-Dependent
format 20?
The bad news is that there is no easy way to do exactly that. Whenever Excel sees "h:mm" it will
be converted to format 20. But the good news is that it is very simple to do something that has
the same effect: **You can set the format to be "h:mm;@" **
As you can see here this is a format string with two parts: "h:mm" for positive numbers and
zeros, and "@" for negative values (Excel doesn't support negative dates, so the value here
doesn't really matter). "h:mm;@" works exactly the same as "h:mm", but due to the extra @ it
won't match the internal format and won't be converted. To match the internal format the string
must be exactly "h:mm".
This is the trick Excel uses when you select a "locale" in the format dialog. Let's for example
select a "Jamaica" locale:
If you then go to "Custom" without closing the dialog, you will see that the format added was
yyyy-mm-dd;@:
The @ at the end makes sure this won't be replaced by an internal format.
But what do you do if you are not creating the files, and your users are sending you files with
internal formats created with their localized Excel versions?
Let's say you live in Spain, and your users are creating files with Spanish Excel with a format "dd/
mm/yyyy". You are opening those files with FlexCel and exporting them to pdf, but in the export
the dates appear as "mm/dd/yyyy" since FlexCel by default is localized as USA, and is behaving
the same as if you had an USA Excel.
You can try to explain all of this to your users, but it is likely that they won't understand: They
saved the files as "dd/mm/yyyy" and they expect the pdf to be "dd/mm/yyyy".
As we have learned in this article, the solution is to use XlsFile.SetBuiltInFormat to make FlexCel
behave as an Spanish Excel. But how do you know which string to use in every one of the
internal formats?
We can't sadly provide every translation from every locale Excel is translated too. And notice that
even US locale is different from UK locale, so it is not just about different languages.
But what we can do is to give you a spreadsheet, which has all the relevant built-in formats in
column B. This file has a user defined function which will calculate the localized format on
column B using the NumberFormatLocal VBA property. Then it shows you the code you need to
write on colums F and G, in C# or Delphi respectively. If you are using a different programming
language, it should be simple to change the formulas in either column F or G to return the calls
in your language.
all-builtin-formats.xlsm
TIP
Remember to enable macros in this spreadsheet so the user defined function will work.
When FlexCel renders a file to pdf or other format, it needs to know how you want those locales
printed. Is it dd/mm/yyyy or mm/dd/yyyy? As you would expect, if you don't do nothing, FlexCel
picks the locale from your machine settings. If you want to change how FlexCel renders those
locale-dependent formats, the simplest way is to just change the machine settings. But
sometimes you can't or don't want to change the machine locale, but still want FlexCel to render
those formats as if the machine had some specific locale.
In .NET it is simple to change the locale, you can just change the thread locale by changing
CurrentCulture and CurrentUICulture
You might change them at the start of your application (or when creating new threads), or you
might change it before working with FlexCel and set them back after the FlexCel work is done, as
in the example below. You might even have different threads working in different locales.
Example:
CultureInfo SaveLocale = System.Threading.Thread.CurrentThread.CurrentCultur
e;
CultureInfo SaveUILocale = System.Threading.Thread.CurrentThread.CurrentUICu
lture;
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-
CN");
try
{
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threadin
g.Thread.CurrentThread.CurrentCulture;
try
{
XlsFile xls = new XlsFile("chinese.xlsx", true);
using (FlexCelPdfExport pdf = new FlexCelPdfExport(xls, true))
{
pdf.Export("chinese.pdf");
}
}
finally
{
System.Threading.Thread.CurrentThread.CurrentUICulture = SaveUILocal
e;
}
}
finally
{
System.Threading.Thread.CurrentThread.CurrentCulture = SaveLocale;
}
Using barcodes.
One question we get regularly is how to embed barcodes using FlexCel. Excel doesn't have any
barcode-specific functionality, so the ways to do that are to either insert an image with a
barcode, or write text with a special font which will render the text as the barcode representation.
Advantages:
1. The font is vectorial, not a bitmap, and fonts are great for scaling. That means that if you
zoom in the page or print with a high resolution printer, you won't see a pixelated image.
It will always be crisp just like any other text in the document, because it is text, just
rendered with a special font.
2. The generated file will probably be smaller. If you have thousands of barcodes, each
barcode will be stored as an ASCII text instead of as an image. It can make a big
difference.
3. The file can be easily edited by another user once you created it. They only need to
change the text and the barcode will update.
4. It is just simpler to write the text in FlexCel to a cell than to generate an image and insert
the image.
Disadvantages:
1. You need to find the special font that renders the barcode and you need and make sure
you are legally allowed to redistribute the font. There are lots of free fonts on the internet,
such as https://www.barcodesinc.com/free-barcode-font/ but you will have to find one
that suits your needs.
2. With images you might get more complex barcodes than with a font. Say for example a
barcode with the shape of your logo, or with annotated text.
3. In Excel you can't embed fonts, so to redistribute an xls or xlsx file using a barcode font,
you need to make sure your users also have the font installed in their machines. If they
don't have the font installed, they will see the text but not the barcodes.
NOTE
When exporting to PDF, not only you can embed the fonts, but also this is what FlexCel does
by default when exporting. So a PDF generated with a barcode font will look great anywhere,
no matter if the user seeing the pdf actually has the font installed on his machine or not.
Using an image
If a barcode font won't work for you, then the other option is to insert an image with the
barcode where you need it. You will still need a separate component for creating the barcode
image, and it is best if you can export it to wmf or emf since those are vectorial formats which
will look better at higher resolutions. If you can only get pngs, make sure they are of a resolution
that is good enough for printing. Try not to use jpegs as they are bad for this kind of images and
can have a lot of artifacts.
Using images is more complex than using a font, files will be bigger, and basically all
"Advantages" in the previous section become the "Disadvantages" of using images. But on the
nice side, the users won't need to have any font installed to see the barcodes. So if you need to
send the xls/x files to random users, an image might be the only sane choice. Once again,
remember that pdf files don't have this problem, so you might want to still use a barcode font,
but redistribute the pdfs instead of the xls/x files.
So hyperlinks are stored in a list, and in FlexCel you have methods to know the number of
hyperlinks (ExcelFile.HyperLinkCount), to retrieve the range of cells one hyperlink in the list
applies to (ExcelFile.GetHyperLinkCellRange) and of course to get the information on the link like
the url where it goes (ExcelFile.GetHyperLink)
If you wanted to know if say cell A3 has an hyperlink, a way to find out would be to loop from 1
to HyperLinkCount and call GetHyperLinkCellRange in each entry of the list. If the range contains
A3, you have found the hyperlink, and you can now call GetHyperLink to get the url of it.
But of course, this can be very slow, if you have thousands of links and you want to get the links
in cell A2, A3, A4... For each one of the cells, you would be looping over all the list to find the
correct links.
What we need here is an indexed search, so you spend O(Ln) instead of O(n) in each search. But
as the search is in 2 dimensions (rows and columns) we need spatial indexing. Luckily, FlexCel
offers a method ExcelFile.LoopHyperLinks that will do just that.
LoopHyperLinks will search for the links that apply to a range of cells, then call an anonymous
method for each one of the links that applies. This will be much more efficient than a dumb loop
over all links, since LoopHyperLinks internally holds an spatial index to locate the links efficiently.
Take a look at ExcelFile.LoopHyperLinks for a code example on how to get the links to a cell.
There can be actually many causes for this, but the main reason is simple: Xlsx files are zip files
with a different extension. In a zip file, you can specify the compression level ranging from
"fastest" (less compression) to "best" (slowest).
Excel is an interactive tool, and it is really important that when an user presses enter the file is
saved as fast as possible and control returned to the user. So Excel uses the "fastest"
compression setting. On the other hand, FlexCel is optimized for server use, and in this case,
smaller sizes win over some milliseconds less to generate the file. So we use the "default"
compression setting which is normally the best compromise between speed and compression.
And this is why the files generated by FlexCel are normally smaller than the same files as
generated by Excel.
NOTE
Comparing file sizes of zip files is not really indicative of anything, but it you want to verify
this, you can easily do the test:
1. Create a file in Excel
2. Open and save it with FlexCel
3. Open the file generated in 2. with Excel again and save it. The file size for this file should
be similar to the file in 1., and bigger than the file in 2.
Finally, note that while "default" compression is normally the best compromise, you can change
the compression of the files generated by FlexCel changing the ExcelFile.XlsxCompressionLevel
property.
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
xls.Save(saveFileDialog1.FileName);
if (MessageBox.Show("Do you want to open the generated file?",
"Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Process.Start(saveFileDialog1.FileName);
}
}
Now, if you have used OLE Automation before FlexCel, you are probably used to a different
pattern: In OLE you can launch Excel, fill it with data using Automation, and then leave the file
open without saving it. And while I personally think saving the file first is nicer, there might be
cases where you want to emulate this behavior of just opening the file without asking the user to
save it first.
The bad news is that it is not technically possible to create a file in FlexCel and have it open in
Excel without saving it somewhere first. On the other side, the good news is that you can
reasonably emulate the behavior and make it look to the user as if the file wasn't saved on disk
first.
This trick uses Excel templates, which have an extension xlt in Excel 2003 or older (equivalent to
xls) and xltx in Excel 2007 or newer. An Excel template is similar to an xls/xlsx file, but it has two
different characteristics:
1. When you open a xltx file in Excel, Excel makes a copy in memory of it, and doesn't lock
the original file. This means that you can remove the template once Excel has opened it
without worrying that Excel will be using it.
2. When an user presses "Save" in Excel, Excel won't save the template but show the "Save
As..." dialog and let the user choose a filename for saving.
So what we are going to do is to create a temporary xltx file instead of a regular xlsx file, open it
in Excel, then wait a little to make sure Excel finished loading it, and then remove the temporary
file. To do so, we can use code like this:
Task.Run(() =>
{
//Wait 30 seconds to delete the file, so Excel has time to open it.
Thread.Sleep(30000);
File.Delete(tmpFileName);
});
NOTE
Once you use Process.Start to open the file, you have to wait until Excel finished loading it. In
the code above we wait for 30 seconds before deleting the file, but if your files are huge or the
machines where Excel is installed are too slow, you might want to increase the time before you
try to delete the file.
NOTE
When saving a file to disk, FlexCel automatically detects from the extension that it should be
saved as a template, so you don't need to do any extra work. Just save the file with an xlt/x
extension and it will be saved as a template. But in other cases like when saving to a stream,
FlexCel can't figure out that you want to save as a template, and you will have to explicitly say
so. You can tell FlexCel that the file is a template by setting the property
ExcelFile.IsXltTemplate to true.
NOTE
When starting the process to open the file, remember that the action needed to open the
template in "template mode" is New, not Open as it is the default in most cases. For example,
when you right click in an xlsx file, "Open" is the default action:
But when you right click in an xltx file, "New" is the default action:
If you opened a template with "Open" instead of "New", it would behave like a normal xlsx file,
being locked by Excel while in use. So in the code above, we used the default action instead of
the default "Open" action. Had we called:
Now the question is: Should I use normal or strict xlsx files? And the short answer is: Use normal
xlsx files unless you have a non negotiable requirement from your customers to use strict xlsx.
The long answer: When Microsoft introduced xlsx back in Excel 2007 they didn't had the time to
completely define all the parts of the new file format, and so for some parts (mostly drawings)
they just reused an older xml file format: Office XML Formats.
Using the existing file formats for the parts that weren't ready did gave them a head start and
allowed them to ship xlsx earlier, but it introduced at least two problems:
1. The style and namespaces for the xml is different. This is mostly a cosmetic issue, but for
example the old xml format uses pascal case for the identifiers (for example
"LegacyDrawing") while the new system uses lower camel case (legacyDrawing). The old
xml also relies more in attributes while the new one relies more in xml children, store
booleans differently and a long list of etc. In general the xml in those legacy parts is very
different from the rest of the newer parts. To read or write to them, they require a lot of
specific code which is different from the code to read or write similar non legacy parts.
2. More importantly the old format wasn't always valid xml. So for example the text of a
button with an enter in the text would be stored as "Some text<br>Second line." This will
crash any compliant xml readers, because xml readers are required to fail when they find
invalid xml and <br> is not valid xml as there is no ending tag.
So in order to fix this and other issues Microsoft has been developing a parallel "Strict xml"
which uses different xml namespaces and has no legacy parts. They renamed the "normal xlsx" to
"transitional xlsx" and added the "strict xlsx" variant.
But the support for strict xlsx in Excel came late, with Excel 2010 being first able to read it, and
Excel 2013 first able to write them. By that time, transitional xlsx was already the standard, and
that hasn't changed up to the day I am writing this hint. And if strict xlsx hasn't gained traction
yet, it is unlikely that it ever will. (even when I would like to be proven wrong as the strict xlsx
format is simpler and free of legacy baggage)
So coming back to the answer: Normal xlsx is the standard, and the format that everyone else
will understand (including old FlexCel versions). So if you have no specific need for the strict xml
parts, just keep using the standard.
The strict xlsx format on the other side should be cleaner, but being "cleaner" is not a property of
a file format that should concern you much. The fact that everyone can read the files you create
should be a bigger concern. And actually, strict xlsx files as saved by Excel aren't cleaner
because they still include the legacy parts inside conditionals so older Excels like Excel 2007 can
still read them. And on the other side, "normal" xlsx files still include the new, non-legacy parts
too for tools that can't understand the legacy parts, so both file formats are basically the same,
except for the different xml namespaces and some syntax differences.
NOTE
As mentioned above, when saving a strict xlsx file with Excel it will still include all the legacy
and potentially invalid xml parts. We believe this defeats the purpose of having a cleaner
format, so when you save with FlexCel, we will only write the non-legacy parts. You will need
to have Excel 2010 so it understands the new non-legacy parts, but you also need Excel 2010
for strict files saved with Excel, since the xml namespaces are different so older tools won't
understand the files. If a xlsx reader knows about strict xml and the strict xml namespaces, it
should be able to handle the non legacy parts too.
A little story
Some time ago I requested some official documents from my government, and they provided
them to me in PDF format. This is all as expected: PDF is the de-facto standard for digital
documents.
But what I didn't expect was that those documents weren't digitally signed. This doesn't make
them completely useless, but the fact that they are not digitally signed diminishes a lot their
value for me. If in the future I need to use those documents myself to prove anything, I can't
really prove that I didn't create those documents myself, or that I modified the data on them.
Can you imagine them giving you any old fashioned paper document that is not signed and/or
rubberstamped?
The government should digitally sign all the digital documents they give us, in the same way
they physically sign their paper documents. And if you are creating documents that can be used
as proof of anything, so should you.
PDF signing is also a proven technology based in solid asymmetric encryption. It is not
something that you (or anyone else) can fake with a 5 minute google search, and neither with
months of hard work. A digital sign is actually much more secure than a physical sign, as long as
you use a secure encryption algorithm.
WARNING
When working in FlexCel .NET, the examples used the default algorithm in a CMSSigner, which
happens to be SHA1. SHA1 is not considered secure anymore, so we have updated the
examples to use SHA512. If you have old code, you might want to update it to use SHA512
too. In Delphi, we already use SHA512 by default.
In practice this means that you can't bundle the certificate with your app, even if you encrypt
the certificate. If your application can read the certificate, a hacker can too. The only secure way
to keep the certificate safe is to sign the PDFs in a server that your users don't have access to.
Should SHA2 or SHA3 become vulnerable in the future, you will need to update the signing
algorithm again.
If you teach your users to trust your self-signed certificates, they might trust other self-signed
certificates too.
Of course nobody can guarantee that any file (not PDF, nor PDF/A) will still be readable in the
future. But PDF/A does have some characteristics that make it likely. For example:
• It is "frozen in time". Different from normal pdf which is constantly evolving, the PDF/A file
format is done. It will never change, and it will be the same 100 years from now. Nobody
knows how the PDF file format might have evolved by then.
• It removes complex and unnecessary stuff which is present in normal PDFs like JavaScript,
Audio and Video.
• It requires that everything is self contained in the document. So for example the fonts that
you use (which might not be available in the future) must be embedded.
• There are multiple independent validators to check if a PDF/A file honors all the requisites.
FlexCel currently uses 3 different validators to ensure that the documents it generates are
valid.
It doesn't mean that there are not drawbacks too: By making your files self contained they
will be larger, and some PDF features you might want to use might not be available. But in
most cases, if your documents are meant to last, you should consider PDF/A.
There are many varieties of PDF/A and all of them are supported by FlexCel. If you are not
sure on what flavor to use, I would recommend PDF/A2, as PDF/A1 is a little too restrictive,
specially because it doesn't support the modern signing algorithms we use in FlexCel. So if
you want to sign and PDF/A your files, you can't use PDFA/1. PDF/A3 is similar to A2, but
it allows attachments. Use A3 only if you want to attach files to your pdf documents.
You can read more on how to create PDF/A files in the PDF guide
NOTE
FlexCel has supported the old conditional formats basically since forever, but it also added
complete support for the new Excel 2007 conditional formats in version 6.9.
This means that in the current FlexCel version you can add, remove and modify any existing
conditional formatting in any file. Every little thing is supported up to the features introduced
in Excel 2016 (the latest version at the time of writing this tip). And FlexCel will also calculate all
of them and export them to PDF, HTML, SVG, etc.
If you weren't using them before because FlexCel didn't support them, make sure to give them
a second look now.
As usual ApiMate will tell you everything about how to enter a conditional format with code, but
conditional formats are specially useful when doing reports.
Let's imagine you want to paint a cell in red if it is bigger than 10. You could do this with tags
like <#Format cell>, but that can get tiring soon and it won't be so simple to maintain. It is much
simpler to just press the "Conditional Formatting" button in the home tab of the ribbon:
Then go to "Highlight cell rules" and select "Greater than" in the cell that you want to format.
Then save the template, and when you run the report, if the value is bigger than 10 it will be red.
No tags or code required!
But this is just scratching the surface. Conditional formats will not only allow you to do stuff that
is possible with normal formatting (with more work), they will also allow you to do stuff that is
just not possible at all without them. Like for example:
Data bars
Color scales
Icon sets
Conditional formats allow a full new world of possibilities to the formatting of your documents,
and better of all, they are fun to use. FlexCel currently supports everything you can throw at it,
up to the latest features in Excel 2016.
NOTE
Not only FlexCel supports all the features in conditional formatting, it sometimes supports
them better than Excel itself!
For example, iconsets in Excel (up to the latest Excel 2016) are bitmaps, which means that they
won't look so good when zoomed in or printed.
This is a screenshot of one of the icons above with Excel at 350% zoom:
To be fair to Excel, they did increase the resolution of those bitmaps in newer versions, they
used to look much more jagged. You might also need a high-dpi monitor to see the
differences better.
On the other hand, FlexCel will draw those icons as vectors and export them as vectors to pdf,
which means that they will look great no matter the resolution or size of the final result.
This is a screenshot of the same icons as exported to pdf by FlexCel, at 400% zoom:
As we value conditional formats so much, we put a lot of effort and months of work to make
sure every little detail is right: From the rendering of icons and databars to the performance
and memory usage with huge files. Take advantage of them.
= A2 * 2
And you want to expand the formula to Columns B to Z. In B1, you will want the formula =B2 *
2, in C1 you will want =C2 * 2 and so on.
This will work the same as in Excel, and the formulas will be adapted when copied. Same as in
Excel absolute references (like for example $B$1) won't be changed, but relative references will
change when copied.
TCellAddress is the record you use in FlexCel to convert cell references from/to numbers to
letters. Here is a little example on how you can get the row and column from the string "B5" and
also how to get the string "B5" from the row and column:
So, for our original example, we could use some code like this:
3) Using R1C1 notation. R1C1 is an alternative notation to the classical A1 notation, to describe
formulas based in their rows and columns, instead of in a letter and a number.
R1C1 is completely equivalent to A1, but has the advantage of always using column numbers,
and that the cells are relative to their position, which is what you normally want.
NOTE
You can find a lot of information in R1C1 cell references internet just by a web search, and I
recommend you to search for R1C1 if you weren't aware that this mode existed. As it makes
no sense to repeat all the information in this doc, here we will focus in how to use R1C1 from
FlexCel.
• ExcelFile.FormulaReferenceStyle = TReferenceStyle.R1C1;
This will affect how you can insert the formulas in FlexCel, but it is independent of how
Excel will show them. To change how Excel displays R1C1 formulas, you need:
• ExcelFile.OptionsR1C1 = true
OptionsR1C1 only affects how Excel shows the formulas, but not how you enter them in
FlexCel, or how FlexCel will return the text of the formulas to you. That is the job of
ExcelFile.FormulaReferenceStyle.
So for our original example, here is the code to do it with R1C1 notation. Note that due to
the fact that R1C1 is relative, the formula is always exactly the same. There is no need to
calculate a formula for each cell as we did in Solution 2; in fact we can move the creation
of the formula outside of the loop to avoid creating temporary objects:
xls.FormulaReferenceStyle = TReferenceStyle.R1C1;
var Formula = new TFormula("= R[1]C * 2");
for (int col = 1; col <= 26; col++)
{
xls.SetCellValue(1, col, Formula);
}
IMPORTANT
While we used R1C1 internally to enter the formulas, in Excel they will show in A1 notation
exactly the same as they do with the other 2 solutions. That is because as explained above
R1C1 support is divided in a property that only affects FlexCel: ExcelFile.FormulaReferenceStyle
and a property that only affects Excel: ExcelFile.OptionsR1C1
So you can work in R1C1 mode in FlexCel while keeping A1 mode in Excel, or vice-versa.
Imagine you have a file with formulas like in our example above, and you want to check that they
are all as they are supposed to be. So for example in Column J, you have =J2 * 2 and not =A2 *
2. Checking this in A1 notation can be very complex, specially if the formulas are not simple.
But retrieve the formulas in R1C1 instead, and all you need to do to check for consistence is to
check that all formulas in A1:Z1 are the same!.
That is, retrieve the formula in A1 (in this case "=R[1]C * 2" ) and then check that all other
formulas in the range have the same as the text in A1. If a formula is different, then it is not
consistent.
and that would be it. But if you try this code and open the generated file in a modern Excel, you
will see that the font didn't actually change. What is going on?
Excel 2007 introduced themes, which are a collection of settings like fonts or colors which you
can change to alter the look of the document all at once.
Themes mostly affect colors and you can find a more in depth explanation of them at the Api
Developer Guide
But themes also affect font names. The theme defines 2 fonts: Major (used in the headings)
and Minor (used in the normal cells)
When you select a font in the font combo-box in Excel, you might have noticed that the first 2
entries are under the category "Theme fonts" and that they have (Headings) and (Body) written
at the right:
The "(Headings)" font corresponds to TFontScheme.Major and the "(Body)" font corresponds to
TFontScheme.Minor.
NOTE
Those 2 fonts in Excel are different from the same fonts without the (Headings) and (Body)
labels at the right.
Imagine that you set cell A1 to Calibri (Body) and cell A2 to just Calibri. And now you change
the spreadsheet theme to a theme where the Body (or minor) font is Arial. When you change
the theme, A1 will change to Arial, but A2 will continue to be Calibri.
So how does this affect FlexCel? Notice that in the code above, we didn't change the font
scheme; only the font name. As the theme takes priority over the font name, a font linked to the
Minor scheme will continue to be whatever font the theme defines for minor, and not what you
specify in the font name.
To make that code work you need to also change the TFlxFont.Scheme to none.
Then type Hello, press Alt-Enter, then type World, and you end up with this:
In the surface, you have just entered text into the cell. But digging a little below the surface, you
have not only entered data, but also changed the format of the cell!
When you entered the text, Excel noticed that it had multiple lines, so it helpfully decided to
change the format of the cell to Wrap text. If you look at the cell format now, you will see that it
has wrap text enabled:
And it wasn't enabled before. If you manually disable it, you will end up with this:
Because the format changed without anybody telling you when you pressed Alt-Enter, you might
not be aware of the fact that in order to display multiline cells you need to set
TFlxFormat.WrapText = true.
WARNING
It doesn't matter if your OS uses \r\n or any other character to separate lines: Excel uses
always the character 10 as line separator.
NOTE
You might wonder why FlexCel isn't as "smart" as Excel, and when it detects that you have
carriage returns in your text, automatically change the format to wrap text.
The reason is simple: Excel and FlexCel have different target users. Excel is an interactive
application, and in those cases every help the application can give you by guessing what you
want to do is welcome. (well not every one, but most. Some guessings can be infuriating even
in interactive mode.)
If Excel guessed wrong, you can see it immediately and just press ctrl-z to undo.
In FlexCel case, you are writing a program to create files, and every "smart" guess just makes
the outcome more unpredictable. You tell FlexCel to do A, but it will end up doing B because it
guessed B is what you really wanted to do.
So FlexCel is dumb on purpose. If you tell it to do A, it will do A and nothing else. If you set a
cell value, it won't change also the cell format because it thinks you might want to do that too.
NOTE
Still here? Ok, what we told you in the last note was only partially true.
For most cases what was said in the last note stands: FlexCel won't do B when you tell it to do
A. That is the only sane way to write a reliable program.
For most rendering stuff, including the PDF API, FlexCel uses points, and those are simple: A
point is 1/72 of an inch. As you can see in the google calculator here.
A point is also normally used to specify font sizes: For example a font with 10 points has a
bounding box of 10/72 inches.
Now, the Excel units are a little more complex. The simplest ones are the y coordinates (rows): A
row is measured in 1/20 of an inch. ExcelFile.GetRowHeight returns the current row height, in
Excel internal units. (1/20th of a point)
But the x coordinates (columns) are way more complex. They measure how many “0”s you can
put in a cell in the font used in the “Normal” style, plus some margins.
For example, the normal style for an Excel 2007 or newer file is “Calibri 11”, so if you can fit 5 “0”s
in a column with that font, (“00000”) then the column width is 5. But not exactly, there are some
margins too, which aren’t correctly documented in Excel docs, and which actually change
depending in the screen resolution. So the width is not going to be 5, but 5.something.
Here you can see how a column autofitted to "00000" looks like:
It is interesting to see what happens when we change the "normal" font of the spreadsheet to
some bigger font. The column width in pixels will get bigger, but the internal units will still be
around 5, because you can still fit 5 "0"s in that column. In fact, even when physically much
bigger, the "size" got a little smaller when selecting a bigger font: from 5.13 to 5.08.
As explained in the help on ExcelFile.GetColWidth you can convert between columns and rows
and pixels using ExcelMetrics.ColMult and FlxConsts.RowMult. And I wish I could tell you this is
an exact science, but sadly Excel isn’t very exact about this. You can see it simply by pressing a
screen preview and measuring the real width of a cell, and compare it with what you see on the
screen, you’ll see it is not exactly the same.
The actual box width for a cell can also change if you use a different screen scaling: They will
look different if you use say 100% or 175% screen scaling. FlexCel can offset this with the
property ExcelFile.ScreenScaling. While by default the screen scaling is 100 (which corresponds
to 100% scaling or 96 dpi), if you are working always in a different setting, changing this
property will make FlexCel work as if it was Excel showing the file at that particular screen scaling.
Changes between resolutions are not much, but they exist so in general it is a bad idea to try to
do “pixel perfect” designs in Excel. You always need to leave some extra room because in
different resolutions the cells might be slightly smaller or bigger.
For more info about how Excel units can change with the resolution, you can also read
Autofitting Rows and Columns in the API Developer Guide
NOTE
As you can see in the images above, Excel will also show you the column size in "Pixels"
besides the internal units.
But sadly those "pixels" aren't actual pixels either. If you took the effort to count them, you
would realize that the pixels in that column are not what Excel says they are. Also, if you
double the resolution of the screen those "pixels" will remain the same, even if the actual
count of pixels doubled with the double resolution.
But the main issue is that those "pixels" are as unreliable as the other units Excel uses. They
share the same resolution problems as the internal Excel units, so they don't really solve any
problem.
And the answer is no, we don’t include such a method because we find it too limiting: Normally
you will have to customize how the cells are written; and you can’t do it with a single method
that does the whole export in one piece. So instead, we provide 2 other different solutions to the
problem.
1. FlexCel has reports, which you can use to dump a dataset “as is”, mostly as a
DumpDataSet method would, but it also lets you do the formatting.
As an added advantage reports allow you to easily modify the resulting sheets without
modifying the code at all, so even the final user can do it.
From reports, the most similar thing to "DumpDataset" is to create an empty spreadsheet,
write "<#mydataset.*>" on it, save it, and then run the report against your dataset. You
can find an example of this in the demos Generic Reports and Generic Reports 2
Of course those 2 demos focus in “generic” datasets which you don’t know beforehand. If
you know what data you are outputting, then you can just write <#dataset.field> in the
cells where you want that field written, and format each field as you want.
Reports are designed specifically to deal with the problem of exporting datasets into Excel,
but allowing formatting.
2. The reports in FlexCel work exclusively by calling the FlexCel API, so anything you can do
in a report, you can do it directly with the API. So if for any reason you can’t or don’t want
to use reports, you can use a method like this:
//Now loop over all records and send them to the file.
for (int row = 0; row < ds.Rows.Count; row++)
{
for (int col = 0; col < ds.Columns.Count; col++)
{
object value = ds.Rows[row][col];
if (value is DateTime)
{
xls.SetCellValue(row + 1, col + 1, value, DateTimeXF);
}
else
{
xls.SetCellValue(row + 1, col + 1, value);
}
}
xls.Save("test.xlsx");
}
}
For those cases, it might make sense not to load the full file in memory, just to read the cell A1
and then close it. And FlexCel has the right tool for the job: Virtual Mode
Virtual mode is described in more detail in the Performance Guide, but in this tip we will just give
you the code to read a file and stop reading after the first cell.
About
This documentation is for FlexCel Studio for the .NET Framework v6.21.0.0
In this section:
What's new
Everything that changed in FlexCel since the last time!
Copyright information
Information about the third party copyrights and licenses of code used by FlexCel.
Licensing information
Single developer license
Site license
• Bug Fix. When in R1C1 mode, full ranges expanding more than 1 row or column like for
example Sheet1!3:5 could be returned as Sheet1!5 only.
• Bug Fix. Sometimes cells formatted as "center on selection" were not rendered when
exporting them to pdf or html.
• Bug Fix. When hiding a column without a given width and the default column width was
different from the Excel default, the column wouldn't be hidden when saving as xls.
• Bug Fix. There could be an error in ClearSheet with some special images.
• ApiMate now reports hidden sheets. ApiMate will now tell you how to hide sheets.
• Improved chm help. The chm help shipped with FlexCel could show javascript errors in
some Windows versions.
• Improved performance with thousands of merged cells. We rewrote the merged cell
handling engine to make it faster and work better when there are thousands of merged
cells.
• Breaking Change: Improved chart rendering. Now FlexCel recalculates the size of the
legends of the charts if those are docked to the top, bottom, right, left or top-right. So if
the size of the series change, the legend box and the rest of the chart will adapt. There are
also other small tweaks in the chart rendering engine to make xls charts more faithful to
what Excel shows. Note: The Excel chart engine has changed a lot since the Excel 2003
times, and Excel 2003 doesn't display charts exactly as Excel 2016. We can't make it work
both ways, so this update makes the chart rendering more like Excel 2016. If you were
rendering old files and relied in the exact position of the legend, this update might move
the position of the legend a little, to position it how Excel 2016 would and not how Excel
2003 would. This is why it is a breaking change.
• New overload for method TPartialExportStart.SaveCss which allows to save the css
without the tags. There is now an overload of TPartialExportState.SaveCss with a
parameter that allows to get the inner html of the classes definition, without the enclosing
tags.
• New methods GetTokens and SetTokens in ExcelFile allow you to parse arbitrary
text. The new methods GetTokens and SetTokens allow you to parse any text into tokens
and then convert those tokens back into a string. Those methods complement the existing
GetFormulaTokens and SetFormulaTokens
• The XlsChart object now returns the 3D properties for xls charts. Now you can read
the 3D properties in charts inside xls files.
• Improved Excel 95 compatibility. Now FlexCel can read some Excel 95 files which would
throw errors before.
• Now FlexCel preserves "new style" sheet and workbook protections in xlsx files. Both
FlexCel and Excel use an old algorithm to compute sheet and workbook protections, and
they both keep doing it this way as it is the only way to port the protections between xlsx
and xls files. But some third party generated files could have a newer style of protections
which are incompatible with xls and FlexCel wasn't understanding them. Now FlexCel will
preserve those new style protections in xlsx files too. The new style protections will be lost
if you save as xls, but that happens in Excel too.
• When wrapping text, now FlexCel recognizes different kind of unicode spaces. Now
other spaces in addition of character 32 are used as separators when rendering the file
and wrapping the text. Note that not breaking spaces (char 160) are still not used as
separators as they aren't supposed to break a line.
• Bug Fix. SetCellFormat with ApplyFormat could format the cells wrong if the cells were
empty and there was column or row format applied.
• Bug Fix. Sometimes when copying sheets form different files, some named ranges would
not be copied.
• Bug Fix. Khmer text could be rendered wrong is some rare cases.
• Bug Fix. When exporting to pdf you could get an error if a character didn't exist and
fallbackfonts was empty.
• Reduced memory usage when exporting. Exporting to PDF and SVG were tweaked to
consume less memory in high-performance environments where many threads are
exporting at the same time. Also the performance of the pdf engine was improved.
• Updated the SkiaSharp library used in .NET Core to the latest. SkiaSharp used is now
1.60, and code was adapted to remove LockPixels/UnlockPixels which don't exist anymore.
Note that due to this changes, FlexCel won't work anymore correctly with SkiaSharp 1.59.
• Images made transparent with Excel now are converted between xls and xlsx files.
Now FlexCel will convert the transparent color parameter between xls and xlsx files.
• Bug Fix. In some cases after copying rows, then deleting sheets and then inserting or
deleting rows, the formula bounds cache could be invalid and formulas would fail to
update in the lase deleting of the rows.
• Bug Fix. The round function now behaves more like Excel and not like C# in some border
cases.
• Bug Fix. Formulas with intersections of a name with a 3d range would be interpreted as
#name instead of the correct formula.
• Bug Fix. In some invalid cases the indirect function would throw exceptions that would be
later processed. While the result was still ok, those exceptions could slow down a lot
recalculation in a file with thousands of formulas.
• Support for shape connectors in xlsx. Now FlexCel will preserve connections between
shapes in xlsx, and convert them from xls to xlsx and viceversa. We've also added the
properties IsConnector StartShapeConnection and EndShapeConnection to allow you to
enter connections with the API. As usual, APIMate will tell you the code needed to add a
connector from an Excel file.
• Bug Fix. The VLOOKUP and HLOOKUP functions now support wildcards (* and ?) in search
strings.
• Improved compatibility with invalid files generated by third party tools. Some xlsx
generators can write invalid column widths. Now when exporting to html/pdf, FlexCel will
consider those widths as default (like Excel does) and not 0 (as it used to do).
• Bug Fix. FlexCel could fail to parse some structured references in tables.
• Bug Fix. When calculating UDFs and there were errors in the arguments, FlexCel could in
some cases return #ERRNAME! instead of evaluating the UDF.
• Bug Fix. In some files the calculated height for items inside Forms Listboxes was too big.
• Bug Fix. FlexCel failed to read custom document properties saved in UTF16.
• Full support for manipulating XML Mappings in xlsx files. Now XML Mappings will be
preserved when opening and saving xlsx/m files, and there are two new commands in the
API to set them or read them with code. The new commands are GetXmlMap and
SetXmlMap . As usual, APIMate will show how to use SetXmlMap . Note: The new API only
works in xlsx/x files, not xls. Xml mappings inside xls files will still be preserved when
opening and saving xls files, but not converted between xls and xlsx.
• Bug Fix. Images made transparent with Excel tools might not preserve their transparency
when saved as xlsx.
• Bug Fix. in .NET Core 2.0 Exceptions thrown by FlexCel would display the message 'Secure
binary serialization is not supported on this platform' instead of the actual error message.
• Bug Fix. When rendering shapes with semi-transparent gradients to PDF or SVG the
gradients were exported as fully opaque.
• Bug Fix. Files with table slicers saved by FlexCel might not open in Excel 2013. (They
already worked fine in Excel 2016, and Excel 2010 doesn't support table slicers).
• Bug Fix. Rotated shapes inside groups in xlsx files could be rendered wrong.
• Bug Fix. Groups that were flipped horizontally or vertically weren't flipped when
rendering. Objects inside were flipped, but the groups themselves weren't.
• Bug Fix. Filled polygons could be exported wrong to PDF in some border cases.
• Bug Fix. Filled polygons could be exported wrong to images with the SKIA backend used
in .NET Core and Android.
• Bug Fix. Legacy system colors in drawings inside xls files could be rendered as transparent
instead of the correct color in border cases.
• Bug Fix. Xlsx files with complex gradients where the stops were not sorted could cause
invalid PDF files.
• Bug Fix. Textboxes with more than 8224 characters would corrupt the file when saved as
xls.
• Updated SkiaSharp to 1.59.2 for .NET Core. Now FlexCel will require SkiaSharp 1.59.2
when using it in .NET Core.
• Ability to copy OLE objects between different files while using xlsx file format. Now
the restriction that you can't copy sheets from one file to another if they have embedded
OLE object has been removed for xlsx files. It is still not possible to copy sheets between
different files with embedded OLE objects in xls.
• Ability to read custom document properties in xls files. Up to now FlexCel could only
read custom document properties in xlsx files. Now it can also read them in xls files. And
now custom properties are migrated from xls files to xlsx too.
• Better handling of URL encoding when encoding some filenames. Now some
filenames containing some characters like "#" will be correctly encoded when linked from
FlexCel. The events that allow you to manually define the links have a new parameter
"UrlNeedsEncoding" which you can set to false to avoid all encoding by FlexCel if you
provide an already encoded URL to the event.
• Bug Fix. The Last print time document property wasn't read in xlsx files.
• Bug Fix. When copying cells from one file to another autofilters would be copied even if
they were not in the range being copied.
• Bug Fix. Formulas referencing sheets which could be interpreted like a R1C1 cell reference
(like "R3C5") were saved without quotes in the sheet name, and thus became invalid
formulas.
• Bug Fix. Modified and creation time were read in UTC, but saved in local time which
would result in a different date being saved back. Now it is all handled in UTC.
• Bug Fix. In some very complex bidirectional reports with sorting in the template the fields
might end up not being sorted correctly, and some might appear twice.
• Better display of negative zero numbers. Now a negative number that displays as zero
like "-0.001" formatted with a "0.0" format string will display as "0.0" and not "-0.0"
• iOS demos updated to require iOS 8 or later so they can be compiled with XCode 9.
iOS demos targeted iOS 6, which isn't supported in XCode 9. Now we target iOS 8.
• Improved xls chart rendering. Now series which are in hidden columns or rows won't
count as series at the moment of drawing the chart, to better copy Excel behavior. Before
this version those series would appear empty, but still take space in the chart.
• Improved compatibility with invalid 3rd party xlsx files. Now FlexCel can open files
where the case of the files inside the container is incorrect. This happens with files
generated by "1C" database and might happen with other 3rd party files.
◦ Tables are now exported to PDF/HTML/SVG/Images and printed with all the table
formatting including banded columns and rows, etc. All formatting is supported.
◦ Now FlexCel can recalculate the structured references used in tables. Everything is
supported, from simple references like Table1[@column] to references in tables
from another file. (for external table references you need to create a Workspace )
◦ Complete API for adding, deleting or modifying tables with code. APIMate was
modified to show how to use the new things in the API.
◦ API for adding, deleting or modifying custom table styles. APIMate shows how to
enter table styles with code.
• Support for reading and writing Strict Open Xml files. Now FlexCel can read and write
Strict Open XML spreadsheets) The default is to save to strict xml only if you opened a
strict xlsx file and saved it, in the other cases we fall back to the standard transitional xlsx.
There is a new property StrictOpenXml which you can set to force saving as strict xlsx, and
read to know if the file you opened was strict xlsx.
• Support for .NET Standard 1.5, 2.0 and .NET Core 2.0. FlexCel nuget package contains
a .NET Core 2.0 assembly, and .NET standard assemblies. The .NET standard assemblies
can be referenced in multiplatform projects and they will be replaced by the
corresponding native assembly.
• FlexCel will now preserve embedded OLE objects in xlsx files. Now FlexCel will
preserve embedded OLE documents (like for example a word document) in xlsx files.
• <#row height> and tags in reports now accept expressions. Now you can write
something like <#row height(<#someexpression>)> where expression will be calculated
at the moment of running the report.
• Now FlexCel converts strings with timestamps to dates more like Excel. In Excel you
can write a string with an invalid timestamp like "3:61" (3 hours 61 minutes, which is 4
hours 1 minute) and it will be accepted by the system. FlexCel was rejecting those
timestamps, but now it accepts them just like Excel.
• Better support for comments in xlsx file in high dpi. The size of the comments is
preserved better now when ScreenScaling is > 0
• Now ExcelFile.RenderObject can render shapes inside groups and use an objectPath
parameter to specify the name of theobject to render. There are new overloads in
ExcelFile.RenderObjects and ExcelFile.RenderObjectAsSVG that take an objectPath
parameter. This allows you to render an individual shape inside a group instead of the full
group, and also to specify directly the name of the shape to render as in
xls.RenderObject(1,"@objectname")
• Reduced memory usage when loading fonts for exporting to PDF. We've optimized
the pdf font engine so it uses less memory when loading the fonts.
• Suport for returning arrays with the INDIRECT function. When doing a Sum, SumIf or
N of an Indirect function which returned an array, FlexCel worked like Excel 2003 or older
and only used the first value of the array. Now it uses the full array in SumIf and N like
Excel 2007 or newer, and in Sum, like Excel 2010 or newer. This allows you to write
formulas like the ones mentioned here: https://www.pcreview.co.uk/threads/indirect-
function-limitations.1750391/ Note that this formula behavior is exclusive to Excel 2010 or
newer: Neither LibreOffice or Google docs implement it.
• All examples available in Github. Now besides being available with the setup and at the
documentation site the examples are also available on Github
• Now when signing PDFs, FlexCel will mark the generated files as requiring Acrobat
8. Due to known vulnerabilities in SHA1, signing with SHA1 is deprecated. So now the
FlexCel signing demos have been modified to use SHA512. As SHA512 requires Acrobat 8
or newer, now the files will be marked as requiring Acrobat 8 or newer. Note that older
acrobat versions will still be able to see the file, but they won't validate the signatures.
• Added recalculation support for new functions. Added support for CEILING.MATH ,
FLOOR.MATH functions added in Excel 2013.
• Breaking Change: Support for double underlines when exporting to pdf and
refactored TUIFont. Now double underlines will be exported to pdf. In order to support
this we had to remove the Underline and Strikeout parameters from the TUIFont
definition, and put them in a separate TUITextDecoration structure. So now the DrawString
methods in PdfWriter require a new TextDecoration parameter, and you don't specify the
underline or strikeout anymore in TUIFont. Also as now TUIFont is different from GDI+
Font by not having underline into it, now you can't convert automatically between a Font
and a TUIFont.
This change can break some code if you are using TUIFont and DrawString directly, but it
should break at compile time and be straightforward to fix. Just create the TUIFonts without
underline, and specify a text decoration when calling drawstring with those fonts. Take a look at
the updated Creating pdf files with pdf api demo to see how it works now.
• Improved conversion of control points in autoshapes between xls and xlsx files. Now
for some shapes like a roundrect or a smiley face are converted better to xlsx when read
from xls files. The default control points for those shapes weren't converted correctly.
• Now you can enter macros that refer to other files with the API. Now when you call
AddButton or similar methods, you can use a macro that refers to a different file like file2!
macro1. As usual APIMate will report the exact syntax to link to a different file.
• Breaking Change: Bug Fix. The parameters MaxWidth and MinWidth of the <#column
width> and <#row height> tags weren't working properly when autofitting. Now they
work according to the docs. If you were using MaxWidth and MinWidth in <row height
(autofit...)> or <column width(autofit...)> please review those tags and make sure
minwidth and maxwidth are in the correct positions.
• FlexCel will now check names of tables are valid when you create a table with the
API. Now FlexCel won't let you name a table with an invalid name (like for example a
name containing spaces).
• Bug Fix. When the print zoom was bigger than 100% the maximum column to print could
be calculated wrong.
• Bug Fix. When evaluating data validations with CheckDataValidation introduced in FlexCel
6.15, INDIRECT functions using RC notation were evaluated wrong.
• Bug Fix. When doing bidirectional reports with multiple horizontal master detail X ranges,
the rows for the vertical ranges could be wrong.
• Bug Fix. When a SPLIT tag was used inside a multiple master-detail relationship in a
report the results could be wrong.
• SKIA library updated to the latest. We have updated the code that uses the SKIA library
in .NET Core to the latest version of the library. We removed calls to deprecated methods
and replaced them with equivalent methods.
• Bug Fix. When exporting arabic rich text with multiple formats in the same cell and a scale
factor different from 1 to pdf the results could have the wrong font sizes.
• Bug Fix. Row() and Col() functions would return 1 when called from <#Format range> or
<#Delete range> tags. Now they return the row and column of the cell where the tag is
written.
• Examples and demos now use SQLite. The database used in the examples was migrated
from SQL Server CE to SQLite. This was because SQL Server CE is deprecated by Microsoft.
• Bug Fix. Setup wasn't correctly registering the .NET 4 and newer assemblies in Visual
Studio, so those wouldn't appear in the "Add reference" dialog as Extensions. (They were
still available if you browsed for them)
• Improved tagging of PDF Files. Now repeated columns and rows are tagged as artifacts
and not real content. This will also fix a rare bug that could happen when exporting a file
with "Columns to Repeat at left" to tagged pdf.
• Improved HTML5 exporting. Updated the HTML5 exporting to comply with the latest
HTML5 standard.
• Improved bidirectional reports. Now you can make a bidirectional report where the
vertical range doesn't cross the horizontal range, but just is contained exactly in the top
and bottom coordinates of the horizontal range.
• Improved unicode Bidi Algorithm. Updated the bid algorithm from version 3.0 to 6.3.
Fixed errors that could happen when using invalid unicode characters.
• Improved compatibility with invalid files. Now FlexCel won't throw an exception by
default when the file has invalid hyperlinks. You can change this behavior by setting the
new "ErrorOnXlsxInvalidHyperlink" property in ExcelFile.ErrorActions
• Bug Fix. FlexCel failed to open some encrypted Xlsx files saved in Excel 2013 or newer
where the key size in the algorithm to encrypt the file was different from the key size in
the algorithm to encrypt the key.
• Bug Fix. When exporting to html a merged cell which had columns or rows not hidden
but with zero width or height could render wrong.
• Bug Fix. Now you can query if a page break at row 0 exists. A page break at row 0 would
mean a page break before the first row, and Excel doesn't obey it, but you might end up
with such a page break when deleting rows. Now you can check if there is a page break on
that row.
• Official support for .NET Core including graphics. Now FlexCel for .NET Core is out of
the beta and fully functional. It now also supports creating pdf files, html, etc., using
SkiaSharp for the graphics support.
• Support for .NET 4.7. There is a new FlexCel.dll targeting .NET 4.7.
• Improved Setup. Now the setup.exe for Windows will include .NET Core and Xamarin
libraries and examples, so there is nothing extra to add. The nuget package flexcel-dnx for
.NET Core has been now replaced by FlexCel.nupkg which supports all platforms that
FlexCel supports. The setup will now automatically register the FlexCel NuGet package
folder so you can just add it from Visual Studio.
• Support for Web Addins, either of Content or Task pane types. Now FlexCel will
preserve Web addins in xlsx files. There are 2 new methods in the API:
ExcelFile.HasWebAddinTaskPanes and ExcelFile.RemoveWebAddinTaskPanes which you
can use to know if there are any task pane addins in the file and remove them. The
content addins are just objects and you can remove them with DeleteObject. You can find
if an object is a Web Add-in by calling the new property TObjectProperties.IsWebAddin
• Support for Table Slicers. Now FlexCel will preserve Table Slicers in xlsx files (xls files
don't support them). Note that Pivot Table Slicers where already preserved, this refers to
the new Table Slicers in Excel 2013.
• New method SetTable allows to modify existing Tables. While you could modify
existing tables by using RemoveTable and AddTable, now you can modify them directly
with SetTable.
• Now you can specify multiple folders with fonts when exporting to pdf. Now in the
OnGetFontFolder event of a FlexCelPdfExport you can return a list of strings separated by
semicolons. So you can return a string like "c:\font1folder;c:\font2folder" and FlexCel will
search for the fonts inside font1folder and font2folder.
• Now <#includes> in reports will balance in the containing band. Now when you
include a subreport in a report, the main parent will be balanced as it is with ranges inside
ranges.
• Bug Fix. When "Precision as displayed" was set to true in the file options, the recalculation
engine could calculate some values with a different precision than the one in the cell.
• Bug fix. When a file with dates starting in 1900 had a linked formula to another file with
dates stating in 1904 the value of the dates in the 1904 file would be considered to be at
1900. Similar for a 1900 file linking to a 1904.
• Bug fix. In some rare cases when changing a style an exception could be thrown.
• Support for Visual Studio 2017 RC. Setup now installs in Visual Studio 2017.
• Support for .NET Core 1.1. Project.json was changed to a csproj in order to build in 1.1.
• Support for drawing mirrored images when rendering. Now when a bitmap is flipped
vertically or horizontally, FlexCel will also draw it like this when exporting it.
• Breaking Change: Support for losslessly rotated JPEG images. Now when inserting
JPEG images that are rotated via the "orientation" attribute in the JPEG file, FlexCel will
automatically rotate the image so it appears with the desired orientation in Excel. Before
FlexCel behaved as Excel 2010 or older, just entering the image as is, so Excel would show
it rotated. Now it works like Excel 2013 or newer, where we rotate the image in Excel to
compensate. All orientation values are supported, included mirrors. Note that this change
might be breaking if you were manually rotating the images before entering so they
would show fine. If you ware rotating the images manually, you should remove the code
as now it will be done automatically. There is also a new method
ImageUtils.GetJPEGOrientation which you can use to tell if a JPEG image is rotated or
not.
• Bug Fix. When autofitting a column which contained multiple lines but the cell was set to
not wrap, FlexCel would consider that the cell could wrap and so end up with a smaller
column width than needed.
• Bug Fix. Xml declarations when writing custom xml parts could be duplicated.
• Bug Fix. When replacing hyperlinks in a shape with a report there could be an exception.
• Bug Fix. Support for reading xlsx files with custom properties with repeated names or
empty names.
• Improved right to left support for text. Now FlexCel will support mixed right to left and
left to right text more as the Unicode BIDI algorithm defines it. The Context property of
the cell is now also used to figure out if it is rtl text embedded in ltr text or ltr text
embedded in rtl text.
files. FlexCel uses "zcDefault" zlib compression level, which normally gives the best ratio
between speed and size. But note that Excel itself uses zcFastest when saving xlsx files,
resulting in faster saves but also bigger files. While you won't probably want to change the
defaults, now you can. Note: We require .NET 4.5 or newer for this property to work.
are loading. Normally FlexCel will use the resolution stored in the images to calculate the
desired width in inches, but now you can override whatever is saved in the file by
changing this property.
• New implementation of wildcard matching for all functions that use them. The new
algorithm to match patterns like * or ? and used in functions like MATCH or COUNTIF is
now much faster and can use much less memory in pathological cases.
• Breaking Change: Better handling of image resolution in reports. Now when adding
an image to a report and resizing it, FlexCel will take in account the image resolution if it is
saved in the image. If the image doesn't have a resolution saved, FlexCel will use the
screen resolution. You can revert to the old way of assuming a resolution of 96 dpi for all
images by changing FlexCelConfig.DpiForReadingImages
• Bug Fix. When exporting to HTML and a merged cell covered hidden rows or columns,
the resulting html could be wrong.
• Bug Fix. When exporting to HTML with embedded SVG images, the fill colors in the SVG
images would be wrong if there were gradients.
• Bug Fix. When exporting to SVG, text in controls or shapes could go a little lower than it
should.
• Bug Fix. The formula parser would fail to detect some unicode characters as valid
characters for a sheet name or named range.
• Better display of complex numeric formats. Now we handle some complex formatting
the same as Excel does, handling also invalid formats which Excel doesn't allow better.
• Bug Fix. Now FlexCel allows names with spaces as macro identifiers when loading files.
While those aren't valid names and Excel won't let you enter them directly, you can enter
them with VBA code, and FlexCel was refusing to read those files. Now FlexCel will open
them correctly.
• Bug Fix. When a file had "Precision as displayed" set and there were cell formats including
percentage signs, the numbers might be rounded wrong.
• Bug Fix. There could be an stack overflow when a camera object rendered a range of cells
which included the cells where the camera object was.
might be reversed. If you are saving as xlsx files, then there is no need to change
anything, as xlsx already worked as expected.
• Better drawing of labels in charts. Now the labels inside charts draw more like Excel
when exporting xls files to pdf or html. If multiple labels would overlap, now FlexCel tries
to separate them. The leader lines in pie charts from the slices to the legends render
better too.
• Bug Fix. When copying sheets in a file, some conditional formats could raise a null
reference exception.
• Improved compatibility with third party created files. Specifically, we now can read
spreadsheets created with google docs which contain pivot tables. Those generate invalid
xlsx files lacking required attributes, and FlexCel would complain about them missing. Now
it will ignore them, and fix the files if you open and save them in FlexCel.
◦ Tables are preserved when editing xlsx files. Note that we refer to the tables
introduced in Excel 2007: Other tables like "what-if" tables were already preserved.
◦ Tables will be copied and modified when you insert or copy ranges.
◦ API for reading tables
◦ Preview API for writing tables. Note that this API is not complete yet and might
fail in some cases. APIMate will show you how to add a table with the API.
◦ There is no rendering yet (exporting tables to pdf, etc), and no calculation of table
references like =SUM(@Table1[column2]
• New properties FullRecalcOnLoad and FullRecalcOnLoadMode in XlsFile.
FullRecalcOnLoad will tell you if the xlsx file opened with FlexCel had the property "Full
Recalc on Load" true. When it is true, normally the file doesn't have the values of the
calculated formulas and you need to do a manual XlsFile.Recalc() to get the values.
FullRecalcOnLoadMode allows you to tell FlexCel how it should mark the files it creates. In
the default mode it will mark them as not needing full calculation id they were calculated
on save by FlexCel (the default) and mark them as needing recalc on open in other case.
Note that those 2 properties only apply to xlsx files: xls files don't have this property and
the value returned by those properties will always be false.
• Some repeated function results are now calculated only once for better recalculation
speed. Now FlexCel can detect repeated subexpressions inside a formula and calculate
them only once. So for example if you have a thousand formulas like =If(A1,2,3... =
Sum($B$1:$E$1000),1,Sum($B$1:$E$1000)) then the Sum($B$1:$E$1000) will be calculated
only once for all the formulas. This can have significant speed improvements if you have
formulas with this pattern.
• Performance improvements in formula parsing. Now the formula parser is a little faster
and that can lead to faster loading of xlsx files with thousands of formulas.
• Performance improvements loading xlsx files with thousands of comments. Now xlsx
files with thousands of comments should load much faster.
• Improved rendering of numbers which don't fit inside a cell. When a number doesn't
fit inside a cell, Excel shows #### instead. But it will always show at least one #, if it can't
fit a complete # into the cell, then it will display it empty. FlexCel was showing part of a #
sign when a full # sign wouldn't fit, not it behaves as Excel and shows the cell empty.
• Bug Fix. In form objects like checkboxes or listboxes saved by Excel 2007 in xlsx files, the
resulting coordinates could be wrong if the value of the anchor took more than one cell.
FlexCel would move the anchor to the next cell, but Excel just ignores the extra width or
height. Note that this only applies to xlsx files saved by Excel 2007, xls files or xlsx files
saved by Excel 2010 or later would be correctly read by FlexCel.
• Bug Fix. When deleting sheets with locally stored defined names and you had multiple
references to those names in a single formula, FlexCel could fail to update the names.
• Bug Fix. When setting a column format for many columns at the same time and reset cells
true, some cells might not be reset.
• Bug Fix. Now FlexCel won't let you enter formulas with unions ranges of numeric or string
values. Before it would allow you to enter a formula like "=1, 2" and it would be
interpreted as the union of the reference "1" and "2". Excel would read it if saved as xls,
but would fail to parse the formula when saved as xlsx. So now we don't allow those
formulas anymore.
• Bug Fix. While it is invalid to write a file with conditional formats or data validations with
formulas that refer to other sheets, Excel can load them (but you won't be able to modify
them). Now FlexCel can read those too without reporting an error.
• Bug Fix. FlexCel could raise an exception when deleting ranges with conditional formats.
• Support for using formulas in "Text" conditional formats. Formulas weren't allowed in
Excel 2007, and FlexCel 6.9 didn't allowed them either. Now they are fully supported, for
Excel 2010 and newer.
• Fixed small validation issues in the xml generated for xlsx files. Some xlsx files
generated by FlexCel could have xml that would not validate against the xlsx reference
files. Excel would still open those files, but the spec wasn't correctly implemented.
• Bug Fix. Sometimes with very complex groups of conditional format rules, some could be
ignored when exporting to pdf.
• Bug Fix. When copying cells with conditional formats from one xlsx file to another, the
borders wouldn't be copied.
◦ All xlsx conditional formats including the new ones added to xlsx in Excel 2010 are
fully parsed and preserved. When you insert and copy ranges, conditional formats
will adapt and be copied too.
◦ Full support to read and write them with the API. APIMate also fully supports them
and will show you the code you need to enter a conditional format into a file.
◦ Full support for exporting all conditional formats to pdf or html. All formats are
exported to pdf. When exporting to html, iconsets and databars are not exported,
but they aren't exported either when you save as html in Excel. Different from Excel,
we export the iconsets to pdf as vectors, so they will look nice in any zoom level.
• Perfomance improvements in exporting. Exporting files to pdf or images is now faster.
Depending in your files, you might see a visible improvement.
• Breaking Change: Removed "classic" OSX and iOS components for Xamarin. Classic
iOS and OSX components are no longer supported by Xamarin packaging and they are
deprecated, so we aren't shipping them anymore.
• Font could be wrong in linked shapes. When you had a shape whose text was linked to
a cell and the value of the cell changed, the font of the shape text would be reset.
• Support for latest version of Xamarin Android. A change in Xamarin now requires a
reference to Java.Interop: http://stackoverflow.com/questions/37788326/c-sharp-xamarin-
java-interop-error
• Bug fix. Some invalid png files could cause exporting to pdf to hang.
• Bug fix. A chart with an empty array as range would throw an Exception when saving in
xlsx files.
• Bug fix. Grouped shapes with more than 2,147,483,647 emus height (approximately
38,000 rows with standard row heights) would be truncated in xlsx files (xls files are always
truncated anyway since that is a limitation of the file format).
• Bug fix. External links could fail to load in xlsx files in Excel 2007. (Excel 2010 and up were
already ok)
• Support for rendering linked images (camera tool) to pdf/html/etc. Now FlexCel will
update the linked images when rendering to show the correct image.
• Form controls are now read from and written to the Excel 2010 stream besides the
Excel 2007 stream. FlexCel now reads the Excel 2010 stream for Form controls and uses it
if available instead of the Excel 2007 stream. It also now writes both an Excel 2007 and
2010 stream. The Excel 2010 stream is better because it saves the coordinates in device
independent units, so controls will look fine when opened in High DPI displays. Excel 2007
on the other hand uses real pixels, which results in different dimensions for the controls
when opened in high dpi mode.
• Files created with NewFile will now not have printer settings, and the locale of them
all will be English. Depending in the Excel version passed to NewFile, FlexCel could add
some printer settings to the empty file, and some versions had different locales. Now all
locales for files created by NewFile are US English, and there are never printer settings.
• Support for camera tool (linked images) in xlsx files. Now "camera tool" pictures will
be preserved when saving to xlsx, and converted between xls and xlsx. They will also
update when you insert rows or columns. Note that FlexCel won't update camera tool
images if the cells change, but they will be updated by Excel when you open the file.
• Breaking Change: Autoshapes in xls files are now rendered using the xlsx definition
of them if it is stored in the file. Excel 2007 and newer save the autoshapes in two
different places inside an xls (not xlsx) file: One that is read by Excel 2003 or older, and the
other which is read by Excel 2007 or newer. FlexCel used to read the section of Excel 2003
or older to render the autoshapes, now it is using the section of Excel 2007 or newer.
While in general this should improve autoshape rendering, note that this change is
potentially breaking if you have files with the correct definition in the xls section and
incorrect in the xlsx section. For more information, please see: http://tmssoftware.com/
site/blog.asp?post=347
• Recovery mode can now open files with invalid format strings. Now when
XlsFile.RecoveryMode = true and the file has invalid format strings, FlexCel will open the
file anyway and report the errors in FlexCelTrace.
• Bug Fix. In some border cases when opening and saving a file multiple times and adding
the same format every time, the format could be added each time instead of detecting it
already existed.
• Bug Fix. Some autoshapes with holes inside could be rendered as fully filled.
• Bug Fix. There could be an error when saving pivot cache slicers or timelines in multiple
sheets.
• Bug Fix. Some colors in controls or shapes in xls files could be read wrong.
• Added support for .NET Core RC2. Now the FlexCel for .NET core version supports (and
requires) .NET Core RC2 (.NET Core SDK 1.0 Preview 1)
• Bug Fix. Fixed error when exporting images to pdf in UWP 10 apps.
• Full support of Hyperlinks in autoshapes in xlsx files. Now hyperlinks in shapes inside
xlsx files are fully preserved as they were in xls files. They also will convert between xls and
xlsx, and you can change the hyperlinks of the shapes with xls.SetObjectProperty. Links are
exported to pdf, html and svg.
• Full support for "Allow users to Edit ranges" in the API. The new methods
XlsFile.Protection.AddProtectedRange , XlsFile.Protection.DeleteProtectedRange ,
XlsFile.Protection.ProtectedRangeCount and
ranges. Note that for simple protection you can still just lock or unlock the cells in the cell
formatting. APIMate should report now how to enter Protected Ranges too.
• New <#Switch> and <#IFS> tags in FlexCel reports. Those tags behave like the IFS and
SWITCH functions added in Excel 2016 january update. They can eliminate "if chains" and
make the expressions simpler. For example <#ifs(<#value> < 10;<#format cell
(red)>;<#value> < 20;<#format cell(yellow)>;true;<#format cell(green)>)>
• Autosize of chart axis when rendering charts. Now when exporting xls charts to pdf/
html/etc, the axis of the chart will move to fit the data in the axis so it doesn't get cut out.
• Support for adding horizontal scrollbars with the API. There is a new property in
TSpinProperties which allows to specify if the scrollbar is horizontal. APIMate will report
how to do it from an horizontal scrollbar in Excel.
• <#IF> tag in reports can now omit the false section. You can now write a tag like <#if
(true;hi)> instead of <#if(true;hi;)>
• Better chart rendering for xls files. Now the labels overflow in a way similar to Excel, and
FlexCel calculates the chart axis positions so t won't overflow.
• Bug Fix. When exporting xls bar and column charts with a single data series and "Vary
colors per point" = true, FlexCel was not changing the colors on each point.
• Bug Fix. When copying sheets with data validations to other files, and the data validations
would refer to a list in a different sheet, the data validation would be copied wrong.
• Bug Fix. When rendering conditional formats in xls files sometimes the background color
could be ignored.
• Updated the RecalcVersion for Excel 2016 to the Excel 2016 January Update. The
january update of Excel 2016 changed the RecalcVersion id saved in the xls and xlsx files.
This means that xls or xlsx files saved with Excel 2016 "pre-january-update" would ask for
saving when opened and closed in Excel 2016 "post january update". Now when you
choose the RecalcVersion in FlexCel to be 2016, FlexCel will identify the file as saved by
"post-january-update" Excel 2016. This will avoid the save dialog when opening in Excel
2016 with all the updates.
• New property UsedZoom in TOneImgExportInfo. The property UsedZoom will tell you
the actual zoom that is going to be used when printing or exporting the sheet. So you
now can call FlexCelImgExport.GetFirstPageExportInfo() and get the zoom of the
pages that will be printed, including the zoom calculated for print to fit if set.
• Improved compatibility with invalid xls and xlsx files. Now FlexCel will fix files which
have an invalid active sheet stored, and set the active sheet to the first in those cases.
• Improved compatibility with thid party xlsx files. FlexCel will now understand xlsx files
which use absolute references like $A$3 in cell value addresses. Note that Excel never
writes absolute references in the cell values addresses, but some third parties might. Now
you will be able to read those files too.
• Bug Fix. When exporting sheets with multiple print ranges, and those print ranges had
different zoom (due to having a PrintToFit zoom), FlexCel could raise an error. Now, similar
to Excel, it will calculate the smallest zoom needed for all the ranges, and use that in all
the print ranges.
• Bug Fix. Rendering of images in headers and footers in xlsx files could be wrong if the
sizes in the file were in mm.
• Support for quoted column names in reports. Now you can quote a column name
inside a tag in a report, like <#"db.column ) "> This can be useful if you have column
names with for example unbalanced parenthesis. Note that you don't need to quote the
name if it has balanced parenthesis.
• Bug Fix. In some cases when opening an xls file with existing formats, and calling
AddFormat for a format already present in the file, FlexCel would fail to realize the format
already existed and create a new one. This could lead to having more formats than the
number allowed by Excel if you opened a file, added existing formats and save it a lot of
times.
• Bug Fix. When <#including> subreports inside FlexCel Reports with the RC option, empty
row formats would be copied to non empty row formats.
• Bug Fix. ActiveX controls with a size larger than an Int32 would raise an Exception when
loading.
• Bug Fix. Bidirectional reports could fill some wrong cells when using multiple master-
details in the rows.
• Bug Fix. Xlsx files with autofilters could become invalid if you deleted the range which
contained the autofilter.
• Bug Fix. VLookup and HLookup would return a match if you searched for a blank string
("") and the cell was empty. Excel doesn't return a match in those cases, and now FlexCel
doesn't either.
• Bug Fix. Double bordered lines could render wrong when the zoom was big (about 200%
or more)
• Bug Fix. Some invalid formulas including more than one "=" sign in a not valid location,
like "=1 + =1" didn't throw an Exception when you tried to manually enter them, and
would raise the exception later when trying to save. Now FlexCel will report those formulas
as invalid when you try to enter them.
• Improved bidirectional reports. Now bidirectional reports can work with rows in master
detail and they also will delete empty column bands if none of the columns has records.
• Bug Fix. Fixed order of records specific for Excel 2010 to workaround a bug in Excel 2010.
Some very complex files could raise an error when opened in Excel 2010, even when they
were correct by the xlsx spec.
• Bidirectional Reports. Now you can create ranges in shape of a cross that expand to the
right and down at the same time. While you could do this before by splitting one of the
ranges in 3, now you can directly intersect the ranges and get the correct result. Take a
look at the new Bidirectional Reports demo and the documentation in the report designer
guide.
• Changed default fallback fonts in pdf. Windows 10 doesn't come with MS Mincho or
MS Gothic installed by default (you need to manually install the language packs to get the
fonts). So now FlexCel looks for both MS Mincho/Gothic (for windows older than 10), and
YuMincho/Gothic for Windows 10.
• The tags <#List>, <#DbValue> and <#Aggregate> can now work inside nested
Array/Linq datasets. Now when you have a master detail relationship where the detail is
a property of the master, FlexCel can find the master dataset for the <#List>, <#DbValue>
and <#Aggregate> even when they are not added with AddTable.
• FlexCel will now set the creation and modification date in xls files too. Now Creation
and Modification dates are stored in xls files, same as they already were in xlsx.
• FlexCel will now allow you to set the file creator for xlsx files. By default, files created
by FlexCel are identified as created by FlexCel in the document properties. Now you can
change the application creator by writing xls.DocumentProperties.SetStandardProperty
(TPropertyId.NameOfCreatingApplication, "SomeNewCreator")
• Bug fix. Macros converted from xls files to xlsx could fail to open in Excel 2016 in some
border cases.
• Improved Getting Started document. Now GettingStarted shows actual code examples
on how to do simple tasks and contains links to all documentation.
• Improved support for DataValidations that have lists with cells from other sheets.
DataValidations with lists of cells from other lists were introduced in Excel 2010, and while
FlexCel preserved them, it wouldn't modify them when inserting or deleting ranges. They
wouldn't either be reported by the API. Now they are modified and also reported by the
API, just like all the other data validations.
• Slicers for Pivot Tables are now preserved in xlsx. Now FlexCel will preserve the slicers
for pivot tables present in xlsx files. This is a feature available only in Excel 2010 or newer,
so you won't see them in older Excel versions, but the generated files will still open
without errors.
• Excel 2010 equations are now preserved in xlsx. Now FlexCel will preserve the new
equations in Excel 2010 (Ribbon->Insert->Equation)
• Center across selection cells are now exported to html. Now html export will export
cells marked as "center across selection", same as exporting to pdf or other exports
already did.
• Full support for formulas attached to textboxes or autoshapes. Now FlexCel will
preserve and convert betwen xls and xlsx textboxes or shapes which have their text linked
to a formula. If you modify the linked cell, the text in the textbox will change.
• Data validations entered manually in xls files could fail to work when opened in
Excel. In some border cases, Excel would report all values as invalid for a data validation
entered with FlexCel, even if the values were valid. This only applied to xls files.
• Now FlexCel can open xlsx files with images with the wrong image type. If an xlsx file
now contains for example a png but it is declared as jpg, now FlexCel will open it as a png
anyway. This will only happen with corrupt files or files generated by incorrect third party
products.
• Error when deleting rows in a pivot table. When deleting rows in a pivot table in an xlsx
file, the rows could go negative creating invalid files.
• Improved compatibility with third party tools. Workaround for some tags not
understood by other third party tools, and now we can read files missing some required
records.
• Enhanced High DPI Support in FlexCelPreview. Now FlexCelPreview supports High DPI
in Windows, besides iOS or OSX as it already did.
• Full support for background images in a sheet. XlsFile adds two new methods to deal
with background images in a sheet: SetSheetBackground and GetSheetBackground.
Background images are now converted between xls and xlsx. ApiMate will also report the
code to add a background image to a sheet. A new property
ExportSheetBackgroundImages allows you to print or export the background images.
(note that Excel never prints the background images, so this property is false by default)
• Full support for manipulating Custom XML parts with XlsFile. The new methods
CustomXmlPartCount AddCustomXmlPart, GetCustomXmlPart and
RemoveCustomXmlPart in XlsFile allow for reading and writing the custom xml files of an
xlsx files, as explained here: https://msdn.microsoft.com/en-us/library/bb608618.aspx
ApiMate will now show how to enter custom xml parts in an xlsx file.
• New property for PDF files: PageLayoutDisplay. The new PageLayoutDisplay property
allows you to specify if to display one or two pages, and continuous scrolling or one page
at a time when opening the document.
• Two new modes for PDF files PageLayout. Now generated PDF files can use a
PageLayout of TPageLayout.OptionalContent to show the optional content panel, or
TPageLayout.AttachmentPanel to show the attachments panel.
• New property ScreenScaling in XlsFile. This new property allows you to workaround
Excel bugs when working in high dpi displays. For more information read http://
www.tmssoftware.com/site/blog.asp?post=311
• Better handling of stored numbers in xlsx. Now numbers are saved in xlsx with a
roundtrip format, which ensures the number we write in the file is exactly the same
number that will be read.
• Ability to <#insert> empty names in reports. Now when you use the <#include> tag in
a report, you can leave the name to include empty. This will mean to insert all the used
range in the active sheet.
• Bug Fix. Now FlexCel will make sure the xml declaration in the custom xml parts added
with AddCustomXmlPart have the same encoding as the encoding being used to store the
file.
• Bug Fix. Xlsx files with external formulas referring to other sheets starting with a number
weren't quoted, and Excel would report an error when opening those files.
• Bug Fix. FlexCel would fail to load files with formulas which pointed to tables in other files
with the new table formula syntax.
• Breaking Change: Improved lookup tag in reports. The <#lookup> tag in reports has
been rewritten to be faster and behave better. IF you are defining your own
VirtualDataSets and overriding the Lookup function you might need to rewrite it, as
parameters changed. But with the new base lookup implementation that is now available
for all, you might just remove the override and use the base.
• SPLIT Datasets in Reports can now be used as datasets for sheets. This allows you to
overflow a report into multiple sheets. When the data in a sheets reaches the maximum of
the split, it will continue in a different sheet. A new sample "Overflow sheets" shows how
to do it.
• Copy to clipboard wasn't working in Excel 2013. We modified the clipboard format so
now it is working.
• Bug Fix. When inserting or deleting columns, array formulas located in other sheets might
not update to take in account those changed rows or columns.
• Bug Fix. Sometimes when moving a range array formulas which pointed to that range
might fail to update.
• Bug Fix. Some functions with array arguments could not be calculated correctly when the
formula was not an array formula.
• Bug Fix. The lookup tag introduced in 6.6.32 could fail if the lookup value was a tag in the
template
• Bug Fix. The functions SumIfs, AverageIfs and CountIfs could give wrong results in some
cases.
• Bug Fix. When rendering a chart with an image inside, there could be an exception.
• Bug Fix. Images inside charts with negative coordinates weren't rendered.
• Bug Fix. Now scatter charts behave like Excel, and if any of the x-axis values is a string, it
will be rendered as a line chart instead.
• Bug Fix. Chart rendering now renders charts where all values are 0.
• Bug Fix. Chart rendering now respects the label positions next to axis, high and low.
• Bug Fix. In some cases when generating reports and exporting them to pdf directly
without saving them as xls/x, there could be a range check error.
• Bug Fix. Tabs inside text in autoshapes now will render as 8 spaces. (note that we don't
use the tab definitions from the autoshape, so this is an approximation)
• Bug Fix. When exporting to bitmaps, the bitmaps where a little bigger than the page size
• Bug Fix. Reports using LINQ could raise an Exception in some cases with null values.
• Improved compatibility with invalid xlsx files generated by third parties. FlexCel can
now read some invalid formulas written in xlsx by other third party products.
• Fix for the latest Xamarin version. Xamarin changed how encodings behave when they
don't exist: Before they used to raise an Exception and now they return null. This broke the
fallback support in older FlexCel versions, and has been fixed now.
• Bug Fix. There could be an error when rendering error bars in charts and there were
missing values.
• Rendering of error bars in xls charts. Now when exporting to pdf/html/etc, FlexCel will
draw error bars. All modes (StdErr, StdDev, fixed, percent, custom) are supported.
• Improved display of line charts. Now colors and sizes of lines in xls charts will be read
from the new xlsx records on it if they exist. This leads to a more faithful rendering,
because the xlsx records have extra information, like for example a line width that isn't
restricted to 4 sizes.
• Improved display of markers in charts. Now markers in charts render much more alike
Excel 2013, with the new options for images, etc.
• Bug fix. XlsFile.FillPageHeaderOrFooter could return an extra "&" character at the end in
some cases.
• Bug fix. Reports might not read expression values when tags had a default value, like
<#value;0>
• Bug fix. Sometimes FlexCel could fail to load an xlsx file with different images with the
same extension but different case (like image1.png and image2.PNG)
• Improved RecoveryMode. Now FlexCel can recover more types of wrong files when
RecoveryMode is true.
• When drawing xls charts, we now use the options for not plotting empty cells. This
option was introduced in Excel 2007, and FlexCel was ignoring it. Now if you choose not to
ignore hidden rows or columns, the chart will render as expected.
• Breaking Change: Now when drawing chart labels that have N/A! error as result,
FlexCel won't draw them. Excel 2003 or older is different in the way it draws #NA! errors
in chart labels from Excel 2007 or newer. In older Excel versions, the label would just draw
as #NA!. In newer Excel versions, it doesn't draw. To be consistent with more modern Excel
versions, now FlexCel won't draw them either when exporting to pdf or html.
• FlexCelReport can use also arrays besides IEnumerable for detail bands. When using
IEnumerable as data source, now you can use a fields which are array instead of an
IEnumerable as detail tables.
• New NOGRAPHICS define. If you define NOGRAPHICS and undefine GDIPLUS in the
FlexCel project properties, you'll get a build which doesn't depend on any drawing engine.
• New DOTNETZIP define. If you define DOTNETZIP in the FlexCel project properties and
add a reference to it, you'll get a build which uses dotnetzip instead of
System.IO.Compression.
• Bug fix. In some cases, when pasting a file with autofilters from Excel you could get a
range error. This is because Excel copies the filter in its totality and part of the filter might
be outside the range copied. Now FlexCel will resize the autofilter if it extends beyond the
copied range.
• Subtotal command in XlsFile. There is a new command xls.SubTotal(...) which works the
same as the command "Subtotal" in the Excel Ribbon, "Data" tab. While you shouldn't use
this when creating new files, it can be useful for formatting old files. For new files, it is best
to just create the subtotals in place.
• New option "ExcelLike" in XlsFile.Sort. Now when doing a XlsFile.Sort command you
can choose between the correct way to handle formulas (this was the only option before)
or the "Excel" way of handling formulas, where references are not updated when a row is
moved of place in the sort. The ExcelLike mode doesn't adapt formulas that reference
those rows, but it can be much faster for tens of thousands of records.
• Improved Xamarin Unified API support. Changed the unified API support to compile in
the latest beta.
• Included reports can now reference the formats of the parent report. Now an
included report can reference the formats of the parent report, same way as it can
reference the expressions.
• Autofitting columns with 90 degree rotation would work always as if the column
had "Wrap text" enabled. When autofitting columns which had a rotation of 90 degrees,
FlexCel would always try to wrap the text so it fitted in many lines, even if the cell wasn't
set to wrap. Now it will only do this if the cell has "Wrap text" on.
• Breaking Change: Removed Xamarin Android 2.2 support. As Froyo (2.2) is now
deprecated, we've removed this support in order to avoid deprecated warnings. Now
minimum supported is 2.3 (Gingerbread).
• Bug Fix. When doing reports with Linq, aggregating a double field could raise Exceptions.
• Pivot tables in xlsx are now copied when you copy sheets. Now if you
InsertAndCopySheet(...) a sheet with a pivot table from an xlsx file, the table will be copied.
(pivot tables in xls were already copied)
• Unknown names in formulas now return #NAME! instead of #NA!. Now when a
formula references a name that doesn't exist, FlexCel will return #NAME! as the formula
result, instead of #NA! as it used to do.
• Bug fix. When setting the text of an object using SetObjectText the font might not be
preserved.
• Bug fix. When changing the font in HtmlFont event in TFlexCelHtmlExport there could be
an Exception.
• Bug fix. RoundUP and RoundDown functions could return the same number and not the
rounded up number in some cases when the number of digits was negative.
• Bug Fix. Rendering some files with thousands of hidden columns could take too long.
• Hidden rows could sometimes count when finding the maximum used column in the
sheet. When printing or exporting an xls/x file, a hidden row with columns outside the
printing range could in some cases cause the maximum column to be that in the hidden
row, which wouldn't be printed.
• Improved compatibility with invalid xls files. Now FlexCel can read some more invalid
xls files created by third parties.
• Sheet names aren't always quoted when returning formula text. In older FlexCel
versions, the sheet was always quoted in formulas. So if you retrieved for example the
formula in A1, it could be 'Sheet1'!A2. Now we quote the sheet only if needed, same as
Excel does. So we would return Sheet1!A2 instead.
• New convenience constructor for XlsFile which takes a Stream. Now you can create an
XlsFile and open a stream in a single operation, without having to first create the XlsFile
and then call xls.Open.
• Improved error message when opening files with 0 bytes. Now when opening files
with 0 bytes or streams with the position at the end, FlexCel will say a clear message
instead of saying that the file format isn't Excel or newer.
• Bug Fix. A local link in a pdf to a page that wasn't exported could cause an Exception.
• Bug Fix. Exporting "Center on selection" cells could be too slow in border cases.
• Bug Fix. XlsFile.SetCommentRow could set the wrong comment in some cases.
• Generic reports using <#table.*> can now reference fixed fields in the table. Now
you can mix <#table.field> with <#table.*> in the same cell.
• Better compatibility with files created by third parties. Now FlexCel will load invalid
xlsx files with repeated comments.
• Bug Fix. Generated xlsx files could be invalid when removing frozen panes from an
existing file.
• Better support for preserving autoshape text in xlsx. Now when you change the text of
an autoshape in xlsx, the existing properties of the text will be preserved.
• Support for reading and writing a cell's text direction (RTL, LTR or Context). Now
you can specify the text direction in a cell, and APIMate will show you how to do it. The
FlexCel rendering engine also now supports better RTL code (still without providing official
RTL support, it is better in this version and usable in most cases)
• Support reading the number of horizontal and vertical page breaks in a sheet. Two
new properties: XlsFile.HPageBreakCount and XlsFile.VPageBreakCount return the count of
page breaks in a sheet.
• Bug Fix. Rendered xls charts could show an extra line in some corner cases with missing
data.
• Bug Fix. TOPN datatables didn't inherit their relationships with master datasets.
• Bug Fix. Macro references in buttons could be copied wrong when copying sheets.
• Bug fix. When copying a range of cells to another sheet which included formulas
introduced in Excel 2007 or newer there could be an error when saving as xls.
• Bug Fix. FlexCel enforced a maximum of 1023 manual page breaks for xls but not for xlsx.
Now We also check that the generated xlsx files don't have more than 1023 manual page
breaks, since that would crash Excel.
• Xamarin Unified API Support. FlexCel now includes two new dlls compiled against
Xamain.Mac.dll and Xamarin.iOS.dll instead of XamMac.dll and monotouch.dll, in order to
support the new Unified API ( http://developer.xamarin.com/guides/cross-platform/
macios/newstyle/ ) This means you can now compile 64 bit iOS and OSX applications with
FlexCel.
• Breaking Change: Generated PDF files are now tagged by default. The files generated
by FlexCel are now tagged by default, as tagging is an accessibility requirement. Tagged
PDF files are bigger than normal files so in order to try to get smaller files FlexCel uses
now features available only in Acrobat 7 or newer. To go back to generating untagged
files you can set FlexCelPdfExport.TaggedPdf = false. To go back to creating files
compatible with Acrobat 5 or newer, set FlexCelPdfExport.PdfVersion = TPdfVersion.v14
• Breaking Change: Generated PDF files are now compatible with Acrobat 7 or newer.
In order to reduce the size of the tagged pdf files that FlexCel now creates by default,
FlexCel now generates files that need Acrobat 7 or newer to open. To go back to creating
files compatible with Acrobat 5 or newer, set FlexCelPdfExport.PdfVersion =
TPdfVersion.v14. Note that as PDF/A-1 requires compatibility with Acrobat 5 or newer,
when exporting PDF/A-1 FlexCel will use v14 automatically. PDF/A-2 and 3 don't require
v14, so it isn't used by default for those formats.
• Breaking Change: Generated PDF files now embed the fonts by default. Now the
default value of FontEmbed in FlexCelPdfExport and PdfWriter is TFontEmbed.Embed.
While this will create slightly bigger files, they will show fine everywhere, including mobile
devices which might not have the fonts. You can revert to the old behavior by changing
FontEmbed to be TFontEmbed.None.
• Breaking Change: FlexCel will throw an Exception when trying to embed a font that
doesn't have a license allowing embedding. FlexCel will now check that the embedded
fonts in PDF have a license that allows embedding. You can revert to the old behavior by
setting UnlicensedFontAction = TUnlicensedFontAction.Ignore, in case you have an
agreement with the Font author. You can also set UnlicensedFontAction =
TUnlicensedFontAction.Replace to replace the unlicensed fonts with a fallback font.
FlexCelTrace will alert when replacing or ignoring a font that is not licensed.
• Ability to embed files inside the PDF. Now you can embed arbitrary files inside the pdf.
This allows for example to ship the original xls/x file inside the pdf. This is supported also
in PDF/A-3.
• Ability to set the language of the PDF files. You can now set a
FlexCelPdfExport.Properties.Language to specify the language of the generated PDF file.
Note that the language will be used by text-to-speech engines to read text out loud, so it
is recommended to set this property.
• PDF properties are now saved in XMP format. PDF properties (like Author, Title, etc.)
are now saved in XMP xml format besides the PDF format. XMP is a requirement for PDF/
A, and allows other tools that don't understand PDF to read the metadata. Note that the
files generated by FlexCel will be now a little bigger due to this metadata, because it can't
be compressed.
• Ability to embed a Color Profile inside the generated pdf files. You can now set a
FlexCelPdfExport.EmbedColorProfile property to embed a color profile in the generated
files. Note that as a color profile isn't required and it increases the size of the generated
files, this option is false by default. But as it is required by PDF/A, a color profile will be
embedded in PDF/A files.
• Breaking Change: Now if you don't specify properties for pdf files in
FlexCelPdfExport (like Author, Title, etc.), those will be read from the Excel file being
exported. If you want to revert to the old behavior, you can set UseExcelProperties = false
in FlexCelPdfExport.
• New structure StandardMimeType returns the mime types for xls, xlsx, xlsm, pdf,
etc. You can use StandardMimeType where you need to specify a mime type for a file
generated with FlexCel, instead of having to manually search for the type.
• Support for <#DBValue> tag in LINQ Reports. DBValue used to work only with
datasets, now you can use it also with LINQ data providers like arrays or lists.
• Improved Search and Replace. Now FlexCel preserves better the format of the cells
being replaced. A new overload of XlsFile.Replace allows you to specify the format or the
values of the replaced cells in a cell by cell basis.
• Support for entering names referring to other files using Excel notation. A normal
reference to another file has the filename inside brackets, like "[file1.xlsx]Sheet1!A1". But in
the case of global names, Excel uses the notation "file1.xlsx!name1", without brackets,
which makes it impossible to know if you are entering a name reference to another file
(file1.xlsx) or a name reference to the same file, in a sheet named file1.xlsx. FlexCel didn't
allow this way to specify the names, and it used to ask for brackets always so you would
have to write [file1.xlsx]!name1 to enter the name. Now you can use the same notation as
Excel, and FlexCel will allow it as long as you setup a TWorkbook before which includes
file1.xlsx.
• Support for format strings that specify fractions. Now when using a format string like
"??/??" the numbers will be displayed as fractions. For example 0.75 will show as 3/4. All
Excel formats for fractions are fully supported.
• New constructor for XlsFile allows to specify the Excel version in one step. Now you
can create a new file in for example Excel 2010 file format by writing XlsFile xls = new
XlsFile(1, TExcelFileFormat.v2010, true); in C# or xls := XlsFile.Create(1,
TExcelFileFormat.v2010, true); in Delphi.
• iOS and OS/X previewer compatibility improved. Some xlsx files generated by FlexCel
that wouldn't show in iOS/OSX previewer will display now. Xlsx charts now update their
caches so the previewer will show them correctly.
• TFlxApplyFont as a new StyleEx property that allows for fine control of which styles
are applied. Before this release you could only apply the full style of the font or nothing
by changing the Style property. Now you can specify individual styles like bold or italics by
changing the StyleEx property.
• XlsFile.Sort does a stable sort. Now when you sort a range of cells, order will be
preserved for items with the same values.
• Bug Fix. Local named ranges could lose their sheet when inserting sheets from other file.
• Shapes inside charts are now preserved in xlsx files.. Now xlsx charts will preserve the
shapes inside.
• Ability to preserve modification date in xlsx files. By default, FlexCel will set the
modification date to the date the file was saved. But if you are modifying an existing file
and want to preserve the original creation date, you can now do it by setting
XlsFile.DocumentProperties.PreserveCreationDate to true.
• Better support for Excel 4.0 macro sheets. Files with Excel 4.0 macros should load
better.
• Bug Fix. XlsFile.Replace might not keep existing cell formats when replacing dates.
• Bug Fix. Chart.DeleteSeries could break the format of the remaining series when called in
a serie at the middle of the chart.
• Bug Fix. There could be an exception when deleting some ranges of cells with hyperlinks.
• Bug Fix. Negative dates when in 1904 mode used to display as ####. Now they display as
in Excel (see http://support.microsoft.com/kb/182247 ). Note that this is not a logical way
to display dates, that is -1 doesn't mean 12/31/1903, but it means "-1/2/1904". Negative
dates actually increase as the number get smaller.
• Support for UTF16 surrogates when exporting to pdf. Now when exporting to pdf,
FlexCel will correctly display UTF16 surrogates. FlexCel already was surrogate-aware in the
rest of the codebase.
• Support for space (" ") named styles. While Excel won't let you enter a cell style named
" ", it will allow you to use it if you manually edit an xlsx file and create it there. To be able
to deal with those files, FlexCel will now support reading and writing styles named with a
space.
• Now when adding controls with linked cells, the linked cells will be modified to
match the initial value of the control. Now when adding comboboxes, listboxes or
checkboxes linked to a cell, the cell will be modified to match. Note that the change
applies to newly created objects, if you change the value of an existing control, the linked
cell was always updated in all FlexCel versions that supported changing control states.
• Bug Fix. Some xlsx files with charts could enter an infinite loop when loading.
• Bug Fix. When replacing rich strings, the rtf runs could be wrong in border cases.
• SVG Exporting. A new component, FlexCelSVGExport allows to export xls/x files to SVG. A
new example "Export SVG" is included too. SVG files are now supported by all major
browsers, desktop and mobile, so you can use them when exporting to html to have
resolution independent images.
• Improved support for document properties. Full support for setting standard or custom
properties in xlsx files. (xls is still read only). A new method
XlsFile.DocumentProperties.RemoveAllProperties will allow you to remove all properties in
the xls or xlsx files, so you can be sure it doesn't contain unwanted information. A new
method XlsFile.DocumentProperties.GetUsedStandardProperties will return a list of the
used standard properties in the file. A new method
XlsFile.DocumentProperties.SetStandardProperty allows to modify standard properties in
xlsx. New methods SetCustomProperty, GetCustomProperty and GetAllCustomProperties
allow to manage custom properties. Document properties will be preserved when opening
xls or xlsx files and saving as xlsx, or when opening and saving as xls. They won't be
preserved when opening xlsx and saving as xls.
• Improved chart rendering. Now charts will have the frame box with rounded corners if
you specify so in Excel.
• Improved APIMate. APIMate now shows how to autofit a comment box to the text size,
and also how to set properties in xlsx.
• Bug fix. Some cells with automatic background colors in xls files could be saved with the
wrong color when converting to xlsx.
• Bug fix. XlsFile.Find could keep returning the same values in corner cases.
• Bug fix. When Inserting an empty sheet locally defined ranges wouldn't update the sheet
where they were defined. This happened only with empty sheets, if you inserted a sheet
with data it would work fine.
• Bug fix. FlexCelPreview could show text as underlined in some third party generated xlsx
files.
• iOS and Android pdf encoding handling. Now FlexCel will create pdf files without using
Win1252 encoding, which isn't included by default in Xamarin for iOS or Android.
• Improved Excel 2013 support. FlexCel could fail to open some complex Excel 2013 xlsx
files.
• Improved handling of dates between 1900-1-1 and 1900-2-28. Excel considers 1900 to
be a leap year, even when it wasn't. ( look at http://support.microsoft.com/kb/214326 ) As
FlexCel uses the .NET DateTime which correctly assumes 1900 wasn't a leap year, dates
between 1900-1-1 and 1900-2-28 would appear in FlexCel as one day before the dates in
Excel. Now FlexCel corrects those dates so they look as in Excel, but the DateTime
datatype still doesn't have Feb-29-1900, so that date will still be wrong. It is still advised to
not use dates before march-1-1900 when working in Excel.
• Support for running in machines with FIPS 140 enabled. Now FlexCel can be used in
machines with FIPS 140 policies enforced. ( see http://support.microsoft.com/kb/811833 )
• New static events in TPdfWriter. There are 3 new static events: GetFontDataGlobal,
GetFontFolderGlobal, OnFontEmbedGlobal. Those work like the already existing
GetFontData, GetFontFolder and OnFontEmbed, but being static, they work an application
level. If you set them, you don't need to set them for every TPdfWriter or
TFlexCelPdfExport instance you create.
• Support for forcing codepage in Excel95. Now you can force a codepage when opening
an Excel 95 file with xls.Open(..., Encoding). While normally you don't need to specify a
codepage for xls95 since it is specified in the file, if the file doesn't have a codepage
record or has it wrong, you can now specify it here.
• SheetProtection is copied when copying sheets from one file to another. Now FlexCel
will copy the sheet protection when you are copying sheets from other file object.
• Bug fix. The rendering engine could fail to draw the top gridline in pages after the first
when PrintGridLines was true and you were repeating rows at the top.
• Bug fix. When opening xls files with data validations and saving them as xlsx, some
relative ranges could point to an incorrect cell range in the xlsx file.
• Bug fix. There was an error when recalculating the =LARGE and =SMALL functions in non
contiguous ranges of cells.
• Bug fix. Sometimes AddFormat() could repeat a format twice in the file.
• Bug fix. In certain cases when a macro name had a dot "." on it, FlexCel could fail to open
the file.
• Improved 3rd party compatibility. Improved generated xls files so they can be loaded
by some 3rd party tools.
• Improved xlsx autoshape rendering and conversion. Now autoshapes in xlsx files are
converted better to xls, and also render more faithfully.
• Improved rendering in iOS and Android. Now texture bitmaps are also supported in iOS
and Android besides Windows.
• iOS7 support. Changes to better support iOS7.Some records in xlsx files have been
changed so the iOS7 viewer can show the files.
• Improved mobile documentation. New demos added for Android and iOS. Reviewed
and improved the documentation.
• Support for the new Excel 2013 xlsx encryption. Xlsx files encrypted with Excel 2013
can now be opened.
• Reduced Memory usage. FlexCel 6 will use from about 1/2 to 1/4 of the memory FlexCel
5 used. We've done a big rearchitecture of the code to ensure it runs fine in memory-
limited devices, and this improvement is also available for Windows.
• More conformant xls files. All xls files created by FlexCel now pass the Microsoft Office
validator if the original file passed it. Note that not all xls files created by Excel pass the
Office validator.
• In OSX and iOS the pdf engine doesn't need more access to the "Fonts" folder. Now
the pdf engine in OSX and iOS an get the fonts directly from memory.
• Support for changing how FlexCel displays the internal numeric formats. Excel has
some internal numeric formats that aren't stored in the file, and different versions of Excel
in different languages might show them different. For example format 37 is defined in
some Excel versions as "#,##0 $;-#,##0 $" while in others is defined as "#,##0 $;(#,##0) $",
(showing negative numbers in parenthesis instead of with a minus sign). The best is not to
use those formats that will display different depending on the Excel version, but if you
need to make FlexCel behave like one specific localized version, you can change those
formats with the static method XlsFile.SetBuiltInFormat(...)
• Support for recalculating XIRR and XNPV functions. XIRR and XNPV are now
recalculated.
• Many improvements and small bug fixes. There are too many small changes to be
mentioned here, but there is hardly any aspect of the library that hasn't been improved.
• Breaking Change: Compact framework is no longer supported. Due to all the work to
support the new platforms, we had to so some cleaning. Compact framework required a
lot of effort to maintain because it lacked too many features, and it isn't being developed
Anymore by Microsoft.
New on v 5.7.18.0
• Bug fix. Images could become invalid when copying sheets between different files.
New on v 5.7.17.0
• Improvements in FlexCelPreview. New AutofitPreview property automatically resizes the
preview so if fits to width, height or page. See the "Custom Preview" demo for more
information. Now by default the preview scrolls past the last page, enough to allow you to
select any page. You can go back to old behavior by setting the "EndPreviewAtLastPage"
property to true. New AutofitPreviewOnce method allows you to autofit the preview just
once. New method MaxPageSize will return the maximum width and height of the pages
in the preview.
• Bug fix. Sometimes when changing some printoptions with the API, number of copies
might be undefined. It will be 1 now in those cases.
• Bug fix. Now FlexCel can read xlsx files with invalid timestamps.
New on v 5.7.16.0
• Support for xltx and xltm files. Now you can save xltx and xltm "template" files as you
could save xlt. The property TExcelFile.IsXltTemplate will tell you if the file you opened was
or not a template and you can change it in order to save templates. Note: FlexCel will
automatically save the files as templates when you save to a file with extension xlt, xltx or
xltm, no matter the value of the IsXltTemplate property. You only need to set it when
saving to streams.
• New method TExcelFile.RecalcRange. RecalcRange can take any formula that evaluates
to a range of cells and return the array of rectangular ranges.
New on v 5.7.14.0
• New properties in FlexCel preview. FlexCelPreview has new properties for customizing
how the page looks like: ShowThumbsPageNumber, PageShadowSize, PageShadowColor,
PageBorderColor, PageBorderWidth, PageBorderStyle, PageNumberBgColor,
PageNumberSelectedBgColor, PageNumberTextColor, PageNumberSelectedTextColor,
Resolution.
• Small improvements. RoundUp and RoundDown functions now ignore roundtrip digits
like Excel. Names that evaluate to rectangular coordinates now can show top, left, right
and bottom coordinates even if they are formulas that evaluate to rectangles, and not
direct ranges.
• Bug fixes. The preview rectangle representing the page was a little larger than what it
should be. Html exporting bug when trying to export some files with 16384 columns.
Improved compatibility with xlsx files created by third parties.
New on v 5.7.10.0
• Performance improvements. Rendering a file with thousands of merged cells is much
faster now.
New on v 5.7.9.0
• Bug Fix. Fixed a bug in the preview component. Bug was introduced in 5.7.6.0.
New on v 5.7.8.0
• Bug Fix. References to external files when saving complex xlsx files which were converted
from xls or manually created could be invalid.
• New Recovery Mode. A new property in XlsFile, "RecoveryMode", tells FlexCel to try to
ignore many common errors in corrupt files so you might be able to open them.
• Buf fixes. Improved compatibility with complex and third party created files, fixes in the
preview component and many other small fixes and improvements.
New on v 5.7.2.0
• Bug fix. TRangeCopyMode.OnlyFormulasAndNoObjects would copy objects when
copying from one file to another.
• Bug fix. Offset function would return error when recalculating if you used missing
arguments in the 2 last parameters.
New on v 5.7.1.0
• Bug fix. Some complex merged/span cells could cause wrong results when exporting to
HTML.
• Bug fix. XlsFile.AddSheet added a sheet before the last position, not after the last one.
Now it behaves as expected.
IMPORTANT: If you are creating very big xlsx file in threads, please make sure to update to
this version.
• Support for xlsx in mono and .NET 2.0. Now xlsx is fully supported in both Mono and
.NET 2.0.
• ATLEAST tag for the config sheet in reports. AtLeast ensures a datasource has at least n
records, and if it hasn't it will return a default value for the records between Count
(Datasource) and n. See the documentation in "Using FlexCel Report" pdf.
• Master detail reports can now be defined with 2 ranges that expand to the same
range. Now if you need for example a Master range and a Detail range in the same row,
you can make the Master range bigger than Detail (for example Master = A1:B1 and
Detail = A1). While both ranges are the same (both A1:XFE1), FlexCel now can realize the
bigger range is the master and use it to know the master-detail relationship. Before you
would get a "ranges intersect" message if Master wasn't actually bigger than detail.
• New "Text Qualifier" parameter when importing csv files. Now when using
XlsFile.Import(...) to import a text file, you can define a text qualifier different from a
double quote ("). The text qualifier is used when the delimiter is part of the field and so it
must be quoted.
• Bug fixes. When exporting to PDF and dpi wasn't 96 and using "XP Style dpi scale" the
resulting PDF might be wrong. Issues when printing all sheets in a workbook and skipping
the first pages. Allowed to read duplicated row records so some invalid files can be read.
• Bug Fixes. Now recalculation works more like in Excel in Lookup and Match functions.
• Bug Fixes. Now FlexCel can read invalid xls files which have wrong strings. PdfExport
could raise an Exception rendering 0 pixel width metafiles.
• Bug Fix. Some numbers near the maximum possible in Excel (1e308) could be stored with
reduced precision.
• Bug fixes. Small issues in chart preserving. Speed issues when rendering big metafiles.
• Support for reading Excel 5 and 95 xls files. While Excel 95 is not in mainstream use,
many third party libraries produce xls 95 files today, and now you can read those files
directly with FlexCel. After opening them, they must be saved as xls 97 or xlsx.
• Support for accessing nested properties when using LINQ in reports. Now when using
LINQ in reports you can access nested properties from the template. If for example you
have a class "Orders" and this class has a nested class "Customer", you can write in the
template "<#Orders.Customer.Name>". You can use as many nesting dots as needed, as
long as the properties have a single value.
• Support for calculating circular references. Now FlexCel can calculate iterative
workbooks. The new properties "OptionsRecalcCircularReferences",
"OptionsRecalcMaxIterations" and "OptionsRecalcMaxChange" in XlsFile allow you to
control the iterative recalculation. As always, the APIMate tool will show how to set those
properties in a sheet.
• APIMate improvements. APIMate will now show the schema of the fonts when using
themed fonts (Excel 2007).
• Bug fix. There could be an error when manually copying formulas that included named
ranges from a workbook to another.
• Bug fixes. <#delete range> tags could work wrong in nested reports. Xlsx files could be
invalid after copying cells from other workbook. Better compatibility with xls files
generated by other 3rd party tools.
• Bug fix. .NET 3.5 XMLReader can hang when reading some malformed xlsx files. Now
FlexCel will throw and Exception when reading those files in .NET 3.5. Note that .NET 4
already worked fine and keeps doing so.
• Bug fix. Xlsx files could save the same image more than once when used in many places.
Now only one copy of every unique image is stored.
• Bug fix. Xlsx files without printer information could default to landscape instead of
portrait.
• Bug fix. Problem when saving xlsx files with more than one pivot table in different sheets.
• Support for rendering non contiguous print areas. Now when exporting to Pdf/Html/
Images/etc. FlexCel will honor print areas that have many different sections, like "=Sheet1!
$A$1:$B$4,Sheet1!$D$5:$F$7".
• Bug fix. In some cases, after copying a chart from one sheet to another, you wouldn't be
able to select the chart anymore from FlexCel to continue working with it.
• Performance improvements. Exporting to pdf now is faster for some particular files. A
new property "IgnoreFormulaText" in XlsFile allows you to ignore the formula text when
reading the cells in a file, speeding up the reading. Look at the "Performance" pdf for
more information.
• Bug fix. Malformed hyperlinks now can be read. FlexCel can now read xlsx files with
malformed hyperlinks.
• Bug fixes. Fixed problem that could rarely happen with nested relationships inside
ADO.NET tables in a report.
• Bug fixes. Support for reading [this row] tokens in formulas inside Excel 2007 tables. We
don't still process them and they will be imported s #REF!, but at least you can read the
file.
• Bug fixes. Issue when copying data validations and conditional formats by columns.
• Bug fixes. When sorting a cell range formulas referring to that range could be offset by
one.
• New "Virtual Mode" for reading xls and xlsx files on demand. The new "Virtual Mode"
allows you to read huge xls/xlsx files on demand, without loading the full file into memory.
If you are importing big files with FlexCel, this new mode can make a big difference. Look
at the "Virtual Mode" demo for more information.
• Support for reading and writing encrypted xlsx files. Reading and writing encrypted
xlsx files is fully supported, both Excel 2007 (Standard Encryption) and Excel 2010 (Agile
Encryption)
• Support for protected xlsx files. Full support for protected xlsx workbooks and sheets,
including password protection.
• Pivot Table preservation in xlsx files. Pivot tables are now preserved when saving xlsx
files, and they can be used in reports, and copied between sheets or files.
• Macro preservation in xlsx files. Macros are now preserved when when opening xls files
and saving as xls or xlsx, or when opening xlsx files and saving as xlsx.
• Full support for R1C1 formulas. Now you can use the R1C1 notation besides A1 when
entering or reading formulas. You can change the cell reference mode with the property
XlsFile.FormulaReferenceStyle.
• Full support for all objects in the Forms palette (radio buttons/group boxes/
comboboxes/listboxes/spins/labels/scrollbars/buttons) in the API, rendering, and
xlsx. The new methods allow you to add and modify any object in the forms toolbar. Also
now all those objects will be printed/exported to pdf/html/images, and they are fully
supported in xlsx too.
• Support for Autoshapes in xlsx. Now autoshapes in xlsx are preserved, converted
between xls and xlsx, and rendered.
• Header and footer images support in xlsx files. Images in headers and footers are now
fully supported in xlsx.
• XlsFile now implements IEnumerable. You can now loop though the cells in an Excel file
with a foreach loop.
• New overload of XlsFile. NewFile allows to specify the version of Excel used to create the
file. Now you can specify the version of the blank xls or xlsx files created by FlexCel.
Different versions fo Excel have different default fornts, columns widths, etc, and now you
can specify exactly which version you are creating. This is specially useful for APIMate,
since in older FlexCel versions it would always create a 2003 xls file, and modify it with
code to match the newer versions. Now it creates the correct file, and code needed is
much less.
• Fixed length text exporting now exports merged cells and cells that span to the
right. When exporting Excel files to fixed length text, now merged cells and cells spanning
to the right will use the full length available instead of cutting at the cell end.
• Bug fixes and performance optimizations. Xlsx files now load and save much faster, and
performance was improved also for xls files. Pdf files are faster too. A new document in
how to get the best performance in FlexCel is also included.
• Experimental MonoTouch support. FlexCel can now be compiled for MonoTouch. With
it, you can read, write and recalculate xls files in iPhone apps. Note that this support is
basic and not fully tested, even when it looks to be working fine. Xlsx file format and
rendering (exporting to pdf/html/printing) are not supported. Search for "MonoTouch.sln"
in the distribution.
• Support for Recalculation of 8 built in functions new to Excel 2010. Includes support
for: NETWORKDAYS.INTL, WORKDAY.INTL, AGGREGATE, CEILING.PRECISE, ISO.CEILING,
FLOOR.PRECISE, PERCENTILE.EXC, QUARTILE.EXC Look at SupportedFunctions.xls in
documentation for more details.
• Support for all Excel 2010's "Renamed Functions". Now you can enter any of the Excel
2010 renamed functions in FlexCel, and those functions whose previous name was
previously recalculated in FlexCel (and have the same paramters) will also recalculate now
in FlexCel. Renamed functions: BETA.DIST, BETA.INV, BINOM.DIST, BINOM.INV,
CHISQ.DIST.RT, CHISQ.INV.RT, CHISQ.TEST, CONFIDENCE.NORM, COVARIANCE.P,
EXPON.DIST, F.DIST.RT, F.INV.RT, F.TEST, GAMMA.DIST, GAMMA.INV, HYPGEOM.DIST,
LOGNORM.DIST, LOGNORM.INV, MODE.SNGL, NEGBINOM.DIST, NORM.DIST, NORM.INV,
NORM.S.DIST, NORM.S.INV, PERCENTILE.INC PERCENTRANK.INC, POISSON.DIST,
QUARTILE.INC, RANK.EQ, STDEV.P, STDEV.ST.DIST.2T, T.DIST.RT, T.INV.2T, T.TEST, VAR.P,
VAR.S, WEIBULL.DIST, Z.TEST Look at SupportedFunctions.xls in documentation for more
details.
• New "BALANCED COLUMNS" mode for reports. Now you can do parallel column
reports where all columns stay balanced and cells are automatically added to pad the
columns with less records. Look at the new "Balanced Columns" demo for an example in
how to use it.
• New FIXEDN ranges for reports. FixedN ranges will behave as "FIXED" ranges for the
first n records, and then behave as normal "__" ranges. For example the name
"__db__FIXED2" will overwrite the 2 first records in the template, and then insert the rest.
Look at the new "Balanced Columns" demo for an example in how to use it.
• New ROWS function for reports. Allows to create datasources in the fly from the
template with a defined number of rows. Look at the new "Balanced Columns" demo for
an example in how to use it.
• Improved Medium trust support. Our obfuscation tool was having issues when running
in Medium Trust, now it should be fixed. This allows for deployment in shared hosting like
godaddy.
• Many small fixes and enhancements. As always, a lot of small fixes and improvements
have been done.
• Breaking Change: Deprecated support for .NET 1.1. In order to move faster to the new
technologies, we had to deprecate .NET 1.1 support for this version.
• Full Comment support in xlsx. Now comments are fully supported in xlsx besides xls.
You can also set extended properties like the comment color directly from the API.
• Full Data Validation support in xlsx. Now data validation is fully supported in xlsx
besides xls.
• Full Hyperlink support in xlsx. Now Hyperlinks are fully supported in xlsx besides xls.
• Full Checkbox support in the API, rendering, and xlsx. The new methods: Get/
SetCheckboxState, Get/SetCheckboxLinkedCell and AddCheckbox allow you to add and
modify checkboxes states and linked cells. Also now checkboxes will be printed/exported
to pdf/html/images, and they are fully supported in xlsx too.
• New "DateFormats" parameter supported when opening or importing CSV files, and
also when setting cells from string. This parameter allows you to specify only a subset
of supported datetime formats when importing, to ensure .NET won't interpret invalid
strings as dates. For example, calling: xls.Open("test.csv", TFileFormats.Text, ';', 1, 1, null,
new string[] { "d/M/yyyy", "hh:mm" }, Encoding.Default, true); will only import dates in
format "d/m/yyyy" or times in format "hh:mm".
• New "FirstSheetVisible" property in XlsFile. This property controls what is the first sheet
tab that is shown in the sheet bar at the bottom of Excel.
• Added support for new functions in recalculation. Added support for FREQUENCY.
• Performance Improvements and bug fixes. The move away from .NET 1.1 allowed us to
switch to generics much more of the code, with up to 10% speed up. Together with other
performance improvements, 5.2 can be up to 30% faster in some cases.
• Database in all demos migrated from Access to SQL Server compact. As Microsoft still
doesn't support the JET driver in 64 bits, we changed the demos to use SQL Server
Compact Edition instead. This way you will be able to test the database demos in pure 64
bits.
• Tested against Office 2010 RTM. Generated files have been tested against the release
version of Office 2010.
• BASIC IMAGE SUPPORT IN XLSX. Simple images are now fully supported in xlsx besides
xls. They will be converted and preserved when you open an xls file and save as xlsx or
viceversa, and also rendered to pdf, etc. Grouped images and autoshapes are still not
supported in xlsx, but coming soon.
• THEME SUPPORT. Now the rendering engine will use other themes besides the standard
office theme, and you are also able to modify the themes in a sheet.
• NEW METHODS IN XLSFILE FOR EXPORTING AND IMPORTING FROM TEXT FILES.
The new methods XlsFile.Import and XlsFile.Export provide more flexibility when working
with text files than the existing XlsFile.Open/XlsFile.Save methods. Now you can specify a
"fixed length" file besides a text delimited file, and you can also import a text file in the
middle of an existing file.
• NEW COPY MODE ALLOWS TO COPY OBJECTS MARKED AS "DON'T COPY" when
copying ranges or sheets. TRangeCopyMode.AllIncludingDontMoveAndSizeObjects will
copy everything when used in InserAndCopyRange. InserAndCopySheets will use this
mode now by default.
• NEW METHOD GETUSEDNAMEDRANGES IN THE API. Returns which ranges are being
used in formulas inside the sheet and which aren't.
• TOOLS ARE NOW PRECOMPILED WITH .NET 3.5. The tools like ApiMate and
FlexCelDesigner used to come precompiled with .NET 1.1, so you can use them no matter
which .NET version you have in your development machine. But as .NET 1.1 doesn't
support xlsx, now they come with 3.5.
• IMPROVED SUPPORT FOR OTHER THIRD PARTY EXCEL GENERATED FILES. While we
support virtually every xls file Excel generates (and we are not aware of any file we can't
retrieve if it has been saved with Excel and it is in xls 97 or up), some third party apps
create files with wrong information, that rely in bugs (and sometimes even in buffer
overflows) in Excel to work. In this release we implemented support for many of those
files, including files generated by SAP
• BUG FIXES. This is primary a maineneance release, and there are many bug fixes and
small improvements, mainly in the Excel 2007 support (both xlsx and "Excel 2007 xls") It is
recommended that you update from 5.0.
•Basic support for reading and writing xlsx file format. Note that due to framework
limitations, you need .NET 3.5 for xlsx support. •Expanded the rows to 1048576 and the
columns to 16384. A compatibility mode still lets you work with the smaller grid should
you need to do so. •Support for Excel's 2007 true color and themes. Breaking change:
ColorIndex properties don't exist anymore and now are just Color. You can still access the
color indexes with Color.Index. •Support for gradients in cell backgrounds; to get/set them
or to export them to pdf/images/print. •Support for a different header and footer for the
first page and for even pages; to get/set them or to export them to pdf/images/print.
•Support for comments in named ranges. •Cell indentation can go up to 250 characters
instead of the old 15. •Methods OptionsMultithreadRecalc, OptionsForceFullRecalc,
OptionsAutoCompressPictures, OptionsBackup, OptionsCheckCompatibility in XlsFile class
allow to configure the corresponding settings in an Excel file.
Please note that xlsx file format support in 5.0 doesn't include objects/charts/images or
conditional formats. That will be added along the 5.n series.
Except for the bigger number of rows and columns (which can't be retrofitted to xls), all new
properties will be saved even to the old xls files. They won't be available when opening the files
in Excel 2003, but they will show in Excel 2007. And they will be used by FlexCel when printing or
exporting to pdf/html, etc.
Take a look at the new section "Considerations about Excel 2007 support" in the API Guide for
more information about updating to xlsx support.
• NEW HTML 3.2 SIMPLE EXPORTING MODE. This new exporting mode won't use CSS or
floating images, and most settings will be done through simple tags. While some style
tags are still used when there is no other option, they are mostly not used either. This
mode isn't as faithful reproducing Excel files as the existing ones, and it doesn't validate
either, but it can be very useful when you need simple HTML more than exact
representation of the xls file. It can be used with devices or browsers that don't support
CSS, or in places where you can't change the existing CSS definitions (for example if you
are adding a table in a blog post, where you can't change the page headers to include
other CSS file).
• WHAT-IF TABLES. Now FlexCel can recalculate What-if tables, and you can add or read
the What-if tables in a file. APIMate also supports What-if tables now, and will show the
syntax to create them.
• ADDED RECALCCELL METHOD TO THE API. This method allows you to calculate only a
cell and its dependencies, not the whole workbook. It can be useful if you are using
FlexCel as a calculator and making thousands of recalculations where you are only
interested in the value of one cell.
• ADDED RECALCEXPRESSION METHOD TO THE API. With this method you can calculate
any formula that is not in the file. For example, if you want to know the sum of the cells in
column a of a worksheet, you can use xls.RecalcExpression("=sum(A:A)").
• SUPPORT FOR ENTERING MULTICELL FORMULAS WITH THE API. Now you can not
only enter array formulas with the API as you could before, but also enter array formulas
that span over more than one cell. We only added the ability to add them from the API,
FlexCel was already fully aware of multicell array formulas and could recalculate them too.
ApiMate will show you the syntax to enter them.
• IMPROVED SUPPORT FOR DATE AXIS IN CHARTS. Now date axis in charts behave
exactly the same way they do in Excel.
• IMPROVED SUPPORT FOR NUMERIC FORMATS. Now "*", "_" and "?" characters in
format strings are fully supported when rendering files, and will show exactly as they do in
Excel.
• NEW SAVEFORHASHING METHOD. This method will save the file in a file format that
will remain the same if the file didn't change, ignoring the timestamps present in the xls
file format. So you can hash this value and use the hash to compare it to a new file, and
know if something changed. Cell selections and sheet selections are not saved by default,
but they can be included.
• IMPROVED PERFORMANCE. FlexCel 5 has been a big rewrite that allowed use to tweak
many places for even better performance.
New on v 4.9.6.2
• IMPROVED AUTOFIT OF MERGED CELLS. Now when autofitting rows and a merged cell
has more than one row, you can select which one of the rows from the merged cell will be
updated. Same for autofitting columns and merged cells with more than one column. This
applies to both reports and API. See "Autofitting Merged Cells" section in the API Guide
for more information.
• SYNTAX HIGHLIGHT WHEN DEBUGGING REPORTS. Now when in debug mode, strings
will be maroon, booleans blue and errors red.
• ABILITY TO MODIFY CHART SERIES FROM THE API. Now you can directly modify chart
series from the API.
• FULL SUPPORT FOR WORKING WITH NAMED STYLES FROM THE API. Now you can
create, modify or remove named styles in the Excel file. Also apply styles or find our which
styles are applied to a cell.
• TTC FONT SUPPORT WHEN EXPORTING TO PDF. Now TTC (True Type Collection) fonts
are fully supported when exporting to PDF. This includes subsetting.
• NEW SETEXPRESSION METHOD IN FLEXCEL REPORT. Now you can use the
SetExpression method in FlexCelReport to dynamically add formulas to a report. For
example, you might have an edit box where the user enters an expression like "<#evaluate
(<#Order.Amount> * <#Order.Vat>)>", and this expression will be used in the final report.
With this method you can reuse the same template to evaluate different formulas.
• IMPROVED HTML RENDERING. Fixed small browser incompatibilities. Chart sheets now
are exported too. Now FlexCelViewer renders by default in XHTML 1.1, to be compatible
with the designer.
• SMALL BUG FIXES. Autofilters now are updated when inserting or deleting columns.
New on v 4.9.5.0
• FONT SUBSTITUTION IN PDF. A new Property "FallbackFonts" in FlexCelPdfExport allows
you to specify a list of "Fallback" fonts that FlexCel will use when the character to print is
not in the main font. See "Dealing with missing fonts and glyphs" in
UsingFlexCelPdfExport.pdf for more information.
• NEW DBVALUE TAG FOR REPORTS. Allows you to know the value of any record of a
data table, for example to merge similar cells. See the new "Merging Similar Cells" demo.
• NEW SEMIABSOLUTEREFERENCES properties in the API and in Reports. Now you can
control how to change absolute references in formulas referring to cells inside the block
being copied. For example in Excel, if you have Cell A1: 1, Cell B1: =$A$1, and copy the
row down, the new row will be Cell A2: 1, Cell B2: =$A$1. If you set this new property to
true, Cell B2 will be =$A$2, since A2 is inside the block being copied. You can use this on
the API when copying blocks with absolute references you would like to change, or in
multi master detail reports, to ensure absolute references point to the right place.
• SHEET TAB COLOR SUPPORT. Now you can read or set the color of a sheet tab using the
new SheetTabColorIndex property in XlsFile. (This feature is supported in Excel XP or
newer)
• SHEET TAB COLOR EXPORTED TO HTML. Now by default if a sheet has a tab color, it
will be shown in the resulting HTML file. You can change this by setting the new
"UseSheetTabColors" property in the StandardSheetSelector class to false.
• BUG FIXES. Fixed an error when subsetting complex true type fonts when exporting to
pdf. Fixed an error when entering bmp image in compact framework.
New on v 4.9.1.0
• Breaking Change: SUPPORT FOR EMBEDDING FONT SUBSETS IN PDF. Now FlexCel
can embed only the subset of characters being used from a font into a PDF file, allowing
smaller PDF files when embedding unicode fonts. NOTE: This is a BREAKING change, since
font subsetting is enabled now by default. If you want to keep the old behavior (for
example to have editable PDF files) you need to set FontSubset property to DontSubset in
the PDF export components.
• ADDED TOP(N) FILTER FOR REPORTS. With this new filter you can get the top n items
from a table directly from the template without touching the code. See the modified
"Fixed Forms With Datasets" demo.
• IMPROVED HTML RENDERING. Improved how exported HTML files are generated.
• IMPROVED PDF EXPORT. Added a new event allowing to control whether to embed or
not and individual font. See the modified "Export PDF" demo.
• BUG FIXES. Fixed bug with some functions when recalculating linked files. Fixed overflow
exception when creating charts with very large values.
New on v 4.9.0.0
• RECALCULATION OF LINKED FILES. Now FlexCel can recalculate across linked files, even
files with circular links. See the new section about Workspaces in the PDF API Guide.
• AGGREGATE SUPPORT IN REPORTS. The new tag "Aggregate" allows to sum, average or
find the minimum or maximum value in a dataset from the template. You can use it when
you can't modify the data layer. See the new "Aggregates" demo.
• SUPPORT FOR HTML TAGS WHEN REPLACING TEXT IN AUTOSHAPES. Now you can
use html inside autoshapes as you could use inside normal cells.
New on v 4.8.0.1
• IMPROVED RECALCULATION. Fixed a bug that might cause a file not to be recalculated
in the second time you call recalc with complex files. Small performance improvements.
• IMPROVED FORMAT DISPLAY. Added support for [mm], [hh] and [ss] format specifiers
for elapsed time.
New on v 4.8.0.0
• PDF SIGNING. Now you can digitally sign the generated pdfs, with both a visible or non
visible signature.
• NEW APIMATE TOOL. This new tool can convert an Excel file to code, so you can see how
to call the FlexCel APIs. Code can be generated in C#, VB.NET or Delphi.NET. A flash demo
showing how to use it is available at http://www.tmssoftware.com/flexcel/tutorial.htm
• IMPROVED HTML GENERATION. Includes the ability to export headers and footers as
blocks above and below the spreadsheet, and fixes to workaround internet explorer bugs.
Exporting headers and footers to HTML is off by default, but you can turn it on. (Look at
the Export to HTML demo)
• BUG FIXES. Small fix with formulas in Data Validation, and support for [>n] tags in
numeric formatting expressions.
New on v 4.7.0.1
• FLEXCEL DESIGNER BUG FIX. FlexCel designer could raise an Exception when started.
New on v 4.7.0.0
• INTELLIGENT PAGE BREAKS. Even when there is no direct support for widow/orphan
lines in Excel, FlexCel now provides a way to keep rows and columns together avoiding
page breaks in the middle of important data. You tell FlexCel which rows you want to keep
together, and it will automatically add page breaks at the needed points in the file so it
prints as you want to. This new feature can be used both from the API or from the reports.
For more information, look at the "Intelligent Page Breaks" demos in the Report and API
sections.
• BETTER ERROR HANDLING OF PAGE BREAK ERRORS. In previous FlexCel versions you
could choose whether to raise an Exception or silently ignore errors when trying to insert
more than the maximum allowed number of manual page breaks (1026). In this version
you can insert as many Page Breaks as you want, and the error or silent ignore will be
done at save time. This allows to have more than 1026 manual page breaks when
exporting to PDF without saving as xls.
• <#DEFINED FORMAT> TAG FOR REPORTS. Allows to know if a user defined format is
defined or not. Look at the Intelligent Page Breaks demo in the report section.
• NEW <#IMGPOS> AND <#IMGFIT> <#IMGDELETE> TAGS FOR REPORTS. You can
use ImgPos to center or align an image dynamically inside a cell. ImgFit will resize the
rows and columns below the image so the image is fit in one cell, and ImgDelete will
delete an image. Take a look at the modified Images demo or at the new Features Page
demo.
• IMPROVED AUTOFIT IN API AND <#AUTOFIT> TAG FOR REPORTS. Now you can
Autofit rows and columns setting a maximum and a minimum height/width for the autofit.
• IMPROVED <#IMGSIZE> TAG FOR REPORTS. Now the ImageSize tag can do a "Best Fit"
resize. You define the maximum size of the image in the template, and ImageSize will
resize the image so it is as big as possible keeping the aspect ratio and inside the bounds
you select. Look at the modified Images demo.
• BUG FIXES. Small bug fix for border cases when inserting columns. AND function now can
AND over a range of cells. Report Expressions now can use named ranges.
New on v 4.6.0.0
• SUPPORT FOR VISUAL STUDIO 2008 AND .NET 3.5 FRAMEWORK. Also updated Setup
with the option for installing into VS2008.
• NATIVE DEMOS FOR VISUAL BASIC.NET. All more than 50 demos have been converted
to Visual Basic .NET, allowing for easier study for vb users.
• SUPPORT FOR CUSTOM EXCEL FORMULA FUNCTIONS. Now you can define your own
classes that implement Excel custom formula functions (like for example the ones in the
Analisis Toolpack Addin, or any function you define using a macro). You can read formulas
using those functions from Excel, write them or calculate them. See the "Custom Excel
Formula Functions" demo for more information.
• SUPPORT FOR AUTOFILTERS IN API. Now you can read or write autofilters in a sheet
using the API.
• IMPROVED COPYING FROM ONE FILE TO ANOTHER. Now when copying between files
Charts will be copied too, and all external references will be converted to the new file. Also
Autofilters will be copied when there are no autofilters in the destination sheet.
• IMPROVED GENERIC REPORTS. Now you can write expressions in a cell with a
<#DataSet.*> tag, leading to much more powerful generic reports. Also now if the cell
with the <#DataSet.**> tag has an autofilter, the autofilter will be propagated to the
following columns. See the improved Generic Reports demo for more information.
• MORE CUSTOMIZATION IN THE <#INCLUDE> TAG. Now you can include files in your
reports without running a report on the include, and also specify if you want to copy the
column widths and row heights from the included report into the parent. See the
documentation in the Include tag.
• IMPROVED XLS COMPATIBILITY WITH EXCEL 2007. Some really complex image
manipulations could cause Excel 2007 to fail to load the generated xls files. It has been
fixed now.
• NEW OBJECT EXPLORER AND ADVANCED API DEMOS. The first shows how objects in
a sheet are nested, and the second shows how to use different methods in the API.
• FLEXCELASPVIEWER. Allows viewing Excel files as html directly from any ASP.NET
application. Just drop the component in a WebForm, and assign it to an xls file. (only
ASP.NET 2.0 supported)
• META TEMPLATES. The new <#PREPROCESS> tag allows a template to modify itself
before creating a report. You can now for example create reports that will automatically
delete a column from the report template if the dataset does not have the field. See the
"Meta Templates" demo for more information.
• PARTIAL FORMAT DEFINITIONS IN REPORTS. Now you can define formats in the config
sheet that will apply only a part of the cell format. For example, if you name a format
"Header(background)" it will only apply the background of the cell and not all the other
properties. See EndUserGuide.pdf for more information, and the Multiple Sheet Report
demo for an example.
• FULL TEXT SEARCH IN DEMOS. Now you can easily find the demo that shows a feature
you are interested in by typing in the Search box in MainDemo.
• BUG FIXES AND SPEED IMPROVEMENTS. Small fixes in rendering and overall speed
enhancements.
• BUG FIXES. Error in border cases when adapting formulas after inserting rows in different
sheets.
• NEW HTML CAPABILITIES. Now you can directly enter HTML formatted strings into an
Excel cell, using TRichString.FromHtml(). Also you can convert the rich text in an Excel cell
into an HTML string. A new property "HtmlMode" on FlexCelReport allows you to do
reports from HTML data. Also the new <#HTML> tag allows to select which cells you want
to use html and which ones you don't no matter the "HtmlMode" value. See the "HTML
Reports" demo for more information.
• SUPPORT FOR WRITING PXL 2.0(POCKET EXCEL) FILES. Now you can not only read but
also create native Pxl 2.0 files.
• READING DOCUMENT PROPERTIES. Now you can read the Author, Title, etc. of any xls
document.
• AUTOFITTING SUPPORT. Now you can autofit rows or columns with XlsFile.AutoFitRow,
XlsFile.AutoFitCol and XlsFile.AutoFitWorkbook methods. On reports, the new tags <#Row
Height> and <#Column Width> allow to change the row height / column width in a
report, and to hide, show or autofit columns and rows. See the new Autofitting demo on
the reports section.
• VIRTUAL DATASETS. Now you can use any data you like as source for your reports, not
only Datasets. See the new Virtual DataSets demo.
• SUPPORT FOR MANUAL FORMULAS IN REPORTS. New report tags <#Formula> and
<#Ref> allow replacing tags inside formulas, creating customizable formulas depending
on the report data. (See the new "Manual Formulas" demo).
• FIXED BANDS ON REPORTS. Now, by defining "__band__FIXED" ranges you can have
bands that don't insert cells when moving down. See Fixed Forms With Datasets demo.
• IMPROVED TAG REPLACE. Report Tags are replaced now also on WordArt objects and
Screen tips inside Hyperlinks.
• SPLIT TAG ON REPORTS. Allows splitting a datatable every n rows. See Split demo for
more information.
• USER TABLE TAG ON REPORTS. Allows defining the datasets you want to use directly on
the template. See User Tables demo for more information.
• IMPROVED ARRAY FORMULA SUPPORT. Now you can enter array formulas with the API
(for example "{=Average(if(a1:a3=3;1))}" ). And now FlexCel can calculate array formulas
too, including array formulas that cover more than one cell.
• ADDED BOOKMARKS TO PDF. Now you can automatically add a bookmark on each
sheet when exporting to pdf, or manually modify the bookmarks too. See the "Custom
Preview" demo, on the button to export to pdf when "All Sheets" is selected.
• IMPROVED RENDERING. Added support to print and Export to PDF more than 70
Autoshapes: From block arrows to FlowCharts to basic shapes. See the new file
SupportedAutoshapes.xls for more information.
• IMPROVED RENDERING. Now FlexCel can print and export to pdf basic WordArt text.
Not all effects or types of WordArt are supported, but text is shown.
• IMPROVED RENDERING. Improved shadow support for autoshapes, and also gradient,
texture, pattern and image fills supported.
• ADDED MOVERANGE TO THE API. Allows moving a range of cells in a sheet the same
way Excel moves them, adapting all formula references as needed. The same as the
existing InsertAndCopyRange and DeleteRange methods, this method is fully optimized to
perform thousands of moves by second.
• ADDED Find, Replace and Sort METHODS TO THE API. While you could always do this
by code, now it is easier to search inside, replace or sort a range.
• ADDED XlsFile.Protection.WriteAccess PROPERTY. Lets you know which user has a file
opened in Excel. See the modified Getting started demo.
• NEW READING FILES DEMO. Showing how to import an Excel file into a DataSet /
Datagrid.
• IMPROVED MONO PDF SUPPORT. Now FlexCel will try to automatically find the fonts
when running on Linux. Also added a section on UsingFlexCelPdfExport.pdf explaining
how to create pdf files from MONO.
• 1904 DATES SUPPORT. Full support for 1904 based dates, allowing interoperability with
xls files created on Apple computers.
• IMPROVED HELP FILES. Help files are now created with SandCastle, and can be
integrated inside the VS IDE.
• IMPROVEMENTS ON PDF API. Now you can write transparent text with the API, for
example to superimpose a watermark to a FlexCel generated file. (See PDF Export demo)
• NEW UTILITY API FUNCTIONS. Added 2 overload to SetCellFormat allowing to set the
format to a range of cells and change only one attribute on a range of cells (for example
change only the line style, keeping the existing fonts)
• NEW FEATURED DEMO. FlexCel Image Explorer allows you to see and extract the images
you have inside an Excel file.
• RELATIONSHIPS ON THE TEMPLATE. Now you can add data relationships directly on
the template, allowing for example to "split" a dataset into master/detail and relate the 2
new datasets. See "Master Detail on one Table" demo for more information.
• DISTINCT FILTER. Now you can use a "DISTINCT() filter to filter unique values on a
dataset. See "Master Detail on one Table" demo for more information.
• MERGE RANGE TAG. With it you can conditionally merge cells on a band. See "Master
Detail on one Table" demo for more information.
• PARAMETERS ON REPORT EXPRESSIONS. Now you can use parameters when defining
report expressions. For more information, see Expression Parameters demo.
• NEW FEATURED DEMOS. Showing how to access a web service from FlexCel or how to
export an AdvWebGrid. Modified the Print/Preview demo to show how to export
Multipage tiff files.
• NEW DEMO ON HOW TO DIRECTLY OPEN THE GENERATED FILES. The "Getting
Started" and "Getting Started Reports" demos have been modified to show how to directly
open the generated files without asking the user to save the file. (note that for this to
work, the user must have excel on his machine).
• OPTIMIZED PRINTING ENGINE. Much faster and with lots of new features. (like
repeating rows/columns, brightness/contrast adjustments on images, printing column and
row headers, printing different types of borders and much more)
• IMPROVED RECALCULATION. More than 100 functions supported, and now much more
like Excel. New methods include: Find, Proper, Concatenate, Exact, Rept, Clean, Search,
Substitute, Text, Index, Match, RoundUp, RoundDown, Even, Odd, Subtotal, CountA, Value,
Sumproduct. Also now supported intersect and union of ranges while recalculating, and
arrays as parameters of formulas.
• BASIC AUTOSHAPES SUPORT. Now you can retrieve all their values and change their
text. FlexCelReport will replace text inside Autoshapes also.
• SUPPORT FOR READING/WRITING HEADER AND FOOTER IMAGES. Now you can
access images on headers and footers, and they will be printed and exported to pdf too.
Note that images on headers and footers are only supported on Excel XP and newer, older
Excel versions will open the file but will not display the graphics.
• CELL SELECTIONS. Now you can read and write cell selections on a file. A new property
on FlexCelReport, "ResetCellSelections", allows you to reset all selections on all sheets to
"A1", so you do not need to worry about selection positions when saving the template.
• SET NAMED RANGES. Now you can add or modify named ranges, including ones as the
print area.
• OPTIMIZED FOR .NET 2.0. If you define the "FRAMEWORK20" conditional define, a lot of
2.0 ONLY features (like Generics) will be used in places where they can improve
performance.
• REGULAR EXPRESSIONS ON REPORTS. You can use the new <#REGEX()> tag to perform
regular expression replaces on the reports. See "Regular Expressions" demo for more
information.
• COMPACT FRAMEWORK ASSEMBLIES. While you can still use the same dll for both
compact and full framework (as before) now we include a special FlexCelCF solution that
will create an assembly specifically targeted to CF.
• CODENAMES. Now you can read the codenames of the sheets. This is useful because
codenames never change, while sheet names can be changed by the user.
• HYPERLINK SUPPORT ENHANCED. Now empty hyperlinks on report will not show, and
also the syntax for tags is changed to "*.tag.*" for Excel2003 compatibility. (old <.tags>
still work, but it is recommended to use the new syntax for new development)
• ENHANCED DELPHI.NET SUPPORT. Added a new demo with Delphi.Net, and now all
BDP datatypes are supported.
• NEW FUNCTIONS FOR REPORTING. Now you can use Sum, Average, Round, Abs,
Ceiling, Floor, Exp, Int, Ln, Log, Log10, Pi, Power, Rand, Sign, Sqrt, Trunc, Count, Radians,
Degrees, Sin, Cos, Tan, ASin, ACos, ATan, ATan2, SumIf, CountIf, Date, DateValue, Day,
Month, Year, Time, TimeValue, Hour, Minute, Second, Now, Today, Error.Type, IsBlank,
IsErr, IsError, IsLogical, IsNA, IsNonText, IsNumber, IsREF, IsText, Type, Na, Choose, Offset,
HLookup and VLookup when creating expressions for a report. All of those functions will
be evaluated when recalculating formulas too.For a complete list of supported functions,
see EndUserGuide.pdf, "Evaluating Expressions"
• SMALL PERFORMANCE IMPROVEMENTS. FlexCel 3.1 is even faster than 3.0. Not much,
because 3.0 was quite fast, but a little faster.
Functions marked with strikeout are not implemented yet. To know if a specific spreadsheet has
all the formulas supported by FlexCel, you can use the "Validate Recalc" demo
Formulas on column "Array Enabled" mean that you can enter them inside an Array formula with
a range argument (for example "if(a1:a10 < 3;…;….)") All formulas can be used inside Array
Formulas, but only the ones on this list can be used with array arguments where they would
expect a single value. By "Array formulas" we mean formulas you enter with Shift-Ctrl-Enter in
Excel
Financial
NameImplementedArray enabled
Db ✓
Ddb ✓
Fv ✓
Ipmt ✓
Irr ✓
Ispmt
Mirr ✓
Nper ✓
Npv ✓
Pmt ✓
PPmt ✓
Pv ✓
Rate ✓
Sln ✓
NameImplementedArray enabled
Syd ✓
Vdb
Statistical
Name ImplementedArray enabled
Avedev ✓ ✓
Average ✓ ✓
Averagea ✓ ✓
Betadist
Betainv
Binomdist ✓
Chidist ✓
Chiinv ✓
Chitest ✓ ✓
Confidence ✓
Correl ✓
Count ✓ ✓
Counta ✓ ✓
Countblank ✓ ✓
Countif ✓ ✓
Covar ✓
Critbinom
Devsq ✓
Expondist ✓
Fdist
Finv
Fisher ✓ ✓
Fisherinv ✓ ✓
Forecast
Frequency ✓ ✓
Ftest
Gammadist ✓
Gammainv ✓
Gammaln ✓ ✓
Geomean ✓
Growth
Harmean ✓
Hypgeomdist ✓
Intercept ✓
Database
Name ImplementedArray enabled
DAverage ✓
DCount ✓
DCounta ✓
DGet ✓
DMax ✓
Text
Name ImplementedArray enabled
Asc ✓ ✓
Bahttext
Char ✓ ✓
Clean ✓ ✓
Code ✓ ✓
Concatenate ✓ ✓
Dollar ✓
Exact ✓
Find ✓
Fixed ✓
Jis
Left ✓ ✓
Len ✓ ✓
Lower ✓ ✓
Mid ✓ ✓
Phonetic
Proper ✓ ✓
Replace ✓ ✓
Rept ✓
Right ✓ ✓
Search ✓ ✓
Substitute ✓
T ✓
Text ✓
Trim ✓ ✓
Logical
NameImplementedArray enabled
And ✓ ✓
False ✓ ✓
If ✓ ✓
Not ✓ ✓
Or ✓ ✓
True ✓ ✓
Information
Name ImplementedArray enabled
Cell ✓
Error.Type ✓ ✓
Info
Isblank ✓ ✓
Iserr ✓ ✓
Iserror ✓ ✓
Islogical ✓ ✓
Isna ✓ ✓
Isnontext ✓ ✓
Isnumber ✓ ✓
Isref ✓
Istext ✓ ✓
N ✓
Na ✓ ✓
Type ✓ ✓
• Total: 238
• Implemented: 213 (89%)
Renamed functions
This is a list of the functions renamed in Excel.
• Supported means that FlexCel understands the function and you can add it with the API.
• Implemented for recalc means that FlexCel knows how to calculate the function.
NOTE
Normally, if a function renamed is implemented for recalc in FlexCel under the old name then
it will also be implemented for recalc in the new name.
But some renamed functions like Norm.S.Dist have a different list of. parameters. Those might
not be implemented for recalc.
Copyright information
Main Copyright
Unless in the parts specifically mentioned below, all files in this distribution are copyright (c)
Adrian Gallero and licensed under the terms detailed in the file license.rtf.
SHA1 Algorithm.
FlexCel uses the SHA1 algorithm which is Copyright (C) The Internet Society (2001). All Rights
Reserved.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
Acknowledgement
Funding for the RFC Editor function is currently provided by the
Internet Society.
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Unicode Data Files include all data files under the directories http://
www.unicode.org/Public/, http://www.unicode.org/reports/, and http://
www.unicode.org/cldr/data/. Unicode Data Files do not include PDF online code
charts under the directory http://www.unicode.org/Public/. Software includes
any source code published in the Unicode Standard or under the directories
http://www.unicode.org/Public/, http://www.unicode.org/reports/, and http://
www.unicode.org/cldr/data/.
Copyright (c) 1991-2012 Unicode, Inc. All rights reserved. Distributed under
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR
SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
these Data Files or Software without prior written authorization of the
copyright holder
/*
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* This is part of HarfBuzz, an OpenType Layout engine library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
This file contains a color profile which will be embedded when you create PDF/A files, or when
you set the PDF generation options to embed the color profile. It is licensed on the following
terms:
PdfStandardFontInfo.data.gz contains postcript data for fonts, and data comes from the AFM
files published by Adobe, which are distributed under the following license:
This file and the 14 PostScript(R) AFM files it accompanies may be used,
copied, and distributed for any purpose and without charge, with or without
modification, provided that all copyright notices are retained; that the AFM
files are not distributed without this file; that all modifications to this
file or any of the AFM files are prominently noted in the modified file(s); and
that this paragraph is not modified. Adobe Systems has no responsibility or
obligation to support the use of the AFM files.
This file is used in the FlexCel demos (not used in the FlexCel itself) and it is under the following
license:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Neither the name of Microsoft nor the names of its contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The SQLite files are used only in the demos (not in used in FlexCel itself). SQLite is in the Public
Domain:
All of the code and documentation in SQLite has been dedicated to the public
domain by the authors. All code authors, and representatives of the companies
they work for, have signed affidavits dedicating their contributions to the
public domain and originals of those signed affidavits are stored in a firesafe
at the main offices of Hwaci. Anyone is free to copy, modify, publish, use,
compile, sell, or distribute the original SQLite code, either in source code
form or as a compiled binary, for any purpose, commercial or non-commercial,
and by any means.
This is a legal agreement between you (the "User") and Axialis Software
Corporation ("Axialis"). By downloading this object pack (the "Objects") from
Axialis.com, the user agrees to the following:
License Grant
You MAY:(a) assemble the Objects to create Icons using an Axialis product only;
(b) use the Objects "as is" in personal or commercial software projects (menus,
toolbars, dialog boxes...) as long as you own a licensed version of the
associated Axialis Product; (c) use the created Icons in personal or commercial
software projects (menus, toolbars, dialog boxes...) as long as you own a
licensed version of the associated Axialis Product.
Restrictions
You MAY NOT:(a) use the Objects without owning a licensed version of the
associated Axialis Product; (b) redistribute, loan, rent, sell the Objects "as
is" and/or the created Icons as a set of Icons or individually; (c) use the
Copyright / Ownership
The Objects are proprietary products of AXIALIS and are protected by copyright
and other intellectual property laws. The Objects are licensed and not sold.
You acquire only the right to use the Objects and do not acquire any rights,
express or implied, in the Software other than those specified in this License.
Disclaimer of warranties
The Objects is supplied "as is". AXIALIS disclaims all warranties, expressed or
implied, including, without limitation, the warranties of merchantability and
of fitness for any purpose. The user must assume the entire risk of using the
Objects.
Disclaimer of damages
• using the component for development of applications or any type of software module in
general by a single developer within the company holding the license.
• sell any commercial compiled application with the control, published by the company
holding the license.
• make modifications to the source code of component for own use.
• use the component and source code on all development systems used by the developer
assigned by the company holding the license.
• request future versions of the component during the license period of two years after
purchase date. After expiry of the registration TMS software can no longer provide any old
version of software, documentation or samples. TMS software is not a backup service and
expects backups to be made by the licensed user.
• access to priority email, web forum support by the single developer assigned by the
company holding the license during the license period of two years.
• sell any number of applications in any quantity without any additional run-time fees
required
• distributing parts or full source code of any component from TMS software.
• using parts or full source code of components from the TMS software for creating any
type of other components that are distributed or sold with or without source code.
• changing the source code of any component from TMS software and sell or distribute this
as a modified product.
• creating a descendant compiled product such as OCX, ActiveX, .NET, Web, VCL control
and sell or distribute this as a product
• using the control in applications sold with different publisher name than the company
holding the license
• transfer the license to another developer
• transfer the license to another company
• using the components by multiple developers in the company holding the license
• installing the components on machines not primarily used by the licensed developer
• the license agreement terminates immediately after violation of any of the terms and
conditions described
Disclaimer:
THIS SOFTWARE IS PROVIDED TO YOU "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED INCLUDING BUT NOT LIMITED TO THE APPLIED WARRANTIES OF
MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. YOU ASSUME THE ENTIRE
RISK AS TO THE ACCURACY AND THE USE OF THE SOFTWARE AND ALL OTHER RISK ARISING
OUT OF THE USE OR PERFORMANCE OF THIS SOFTWARE AND DOCUMENTATION.
tmssoftware.com bvba SHALL NOT BE LIABLE FOR ANY DAMAGES WHATSOEVER ARISING OUT
OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF tmssoftware.com bvba HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. TO THE MAXIMUM EXTENT PERMITTED
BY APPLICABLE LAW, IN NO EVENT SHALL tmssoftware.com bvba BE LIABLE FOR ANY
CONSEQUENTIAL, INCIDENTAL, DIRECT, INDIRECT, SPECIAL, PUNITIVE, OR OTHER DAMAGES
WHATSOEVER, INCLUDING BUT NOT LIMITED TO DAMAGES OR LOSS OF BUSINESS PROFITS,
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS,
EVEN IF tmssoftware.com bvba HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
• using the component for development of applications or any type of software module in
general by all developers within the company holding the license.
• sell any commercial compiled application with the control, published by the company
holding the license.
• make modifications to the source code of component for own use.
• use the component and source code on all development systems used by all developers in
the company holding the license.
• request future versions of the component during the license period of two years after
purchase date. After expiry of the registration TMS software can no longer provide any old
version of software, documentation or samples. TMS software is not a backup service and
expects backups to be made by the licensed user.
• access to priority email, web forum support by all developers in the company holding the
license.
• sell any number of applications in any quantity without any additional run-time fees
required
• change at any time the number of developers using the TMS software components within
the company holding the license
• notify TMS software at any time to allow new developers within the company to access
the priority email, web forum support during the license period of two years.
• allow any number of developers within the company holding the license to access the web
based interface for obtaining product updates.
• distributing parts or full source code of any component from TMS software.
• using parts or full source code of components from the TMS software for creating any
type of other components that are distributed or sold with or without source code.
• changing the source code of any component from TMS software and sell or distribute this
as a modified product.
• creating a descendant compiled product such as OCX, ActiveX, .NET, Web, VCL control
and sell or distribute this as a product
• using the control in applications sold with different publisher name than the company
holding the license
• the license agreement terminates immediately after violation of any of the terms and
conditions described
Disclaimer:
THIS SOFTWARE IS PROVIDED TO YOU "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED INCLUDING BUT NOT LIMITED TO THE APPLIED WARRANTIES OF
MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. YOU ASSUME THE ENTIRE
RISK AS TO THE ACCURACY AND THE USE OF THE SOFTWARE AND ALL OTHER RISK ARISING
OUT OF THE USE OR PERFORMANCE OF THIS SOFTWARE AND DOCUMENTATION.
tmssoftware.com bvba SHALL NOT BE LIABLE FOR ANY DAMAGES WHATSOEVER ARISING OUT
OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF tmssoftware.com bvba HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. TO THE MAXIMUM EXTENT PERMITTED
BY APPLICABLE LAW, IN NO EVENT SHALL tmssoftware.com bvba BE LIABLE FOR ANY
CONSEQUENTIAL, INCIDENTAL, DIRECT, INDIRECT, SPECIAL, PUNITIVE, OR OTHER DAMAGES
WHATSOEVER, INCLUDING BUT NOT LIMITED TO DAMAGES OR LOSS OF BUSINESS PROFITS,
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS,
EVEN IF tmssoftware.com bvba HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.