Skip to content

Expressions

Expressions compute what a report shows: visible, filter, variables, sort keys, group keys and chart values are expressions, and text is a text template with expressions in braces. They are a small, sandboxed language: no code from .NET runs, so templates from people you do not fully trust are safe. Every expression is type-checked in the scope it runs in when the template is compiled, before any data is read.

visible: =Total > 0 # a property that takes an expression starts with =
filter: Status == 'open' # where only an expression fits, the = is optional
value: "{Customer}: {Sum(Total):C2}" # a text template

Text properties (value, title, alt, link…) are text templates: text with expressions in braces.

  • {Sum(Total):C2} formats the value with a .NET format after the colon, in the report’s culture.
  • {{ and }} are literal braces.
  • A colon only starts a format outside parentheses and strings, so a condition in a template is written in parentheses: {(Paid ? 'Paid' : 'Due')}.
  • A template that is exactly one expression with no format, such as "{Logo}", gives the value itself rather than text. That is how images, barcodes and attachments take bytes from your data.

boolean, integer, decimal, double, string, date, dateTime, time and bytes. Any value can be null.

  • 42 is an integer, 4.2 a decimal and 4.2e0 a double. Strings are in 'single' or "double" quotes, with \n, \t, \\, \' and \" escapes.
  • Integers widen to decimals or doubles as needed. Decimals and doubles never mix: Price * Rate with a decimal price and a double rate is an error that suggests ToDecimal() or ToDouble(), so money never passes silently through binary floating point.
  • Dividing two integers gives a decimal: 7 / 2 is 3.5.
  • An integer that overflows is an error, not a wrap-around.

From the lowest precedence to the highest:

Operators
a ? b : c Condition
a ?? b a, or b when a is null
|| Or
&& And
== != Equal, not equal
< <= > >= Comparison
+ - Add, subtract; + with text on either side joins them, formatting the other side in the report’s culture
* / % Multiply, divide, remainder
! - + Not, negate

Writing =, <>, & or | gives an error that says what to write instead.

Nulls follow SQL: arithmetic with null is null; comparisons with null are false, except == null; a condition that is null counts as false; and aggregates skip nulls. ?? and Coalesce put a value in its place. In a text template, null shows as nothing.

A name is looked up, in order, as a field of the current table row, a field of the body row, a variable, a group (its current key), a parameter, then PageNumber and TotalPages.

  • Names are case-sensitive; a near miss gets a “did you mean” suggestion.
  • Parent.X reads the enclosing row’s field X from inside a table or chart, where a field of the same name would hide it: filter: =InvoiceId == Parent.Id.
  • An expression that uses PageNumber or TotalPages is evaluated during layout, once the pages are known.

Sum, Count, Avg, Min, Max, First and Last summarize the rows of a scope: Sum(Total), Avg(Quantity * Price), Count().

  • An optional last argument names the scope: 'report', a group’s name, or a dataset’s name: Sum(Quantity * UnitPrice, 'lines').
  • Without it, a group’s header or footer covers its group, the detail its innermost group, a table’s cells and footer the table’s rows, and everything else the whole report.
  • With no rows, Sum and Count are 0 and the others are null. Avg of integers is a decimal.
  • Aggregates cannot be nested or use PageNumber, and are not allowed in filters, sort keys, group keys or parameter defaults. A group’s header can show its group’s total: it is computed before the header is laid out.

Function names are not case-sensitive.

Upper(text)

The text in upper case, by the report’s culture. Null when an argument is null.

Lower(text)

The text in lower case, by the report’s culture. Null when an argument is null.

Trim(text)

The text without white space at either end. Null when an argument is null.

Len(text)

The length of the text, in characters. Null when an argument is null.

Left(text, count)

The first count characters of the text, or all of it if it is shorter. Null when an argument is null.

Right(text, count)

The last count characters of the text, or all of it if it is shorter. Null when an argument is null.

Substring(text, start, length?)

The part of the text from start (counting from 0), length characters long or to the end. Positions past the end are clamped. Null when an argument is null.

Contains(text, value)

Whether the text contains value (case-sensitive). Null when an argument is null.

StartsWith(text, value)

Whether the text starts with value (case-sensitive). Null when an argument is null.

EndsWith(text, value)

Whether the text ends with value (case-sensitive). Null when an argument is null.

Replace(text, find, replacement)

The text with every find replaced by replacement (case-sensitive). An empty find changes nothing. Null when an argument is null.

PadLeft(text, width, char?)

The text padded on the left to width characters with char, a space by default: PadLeft(Code, 6, '0'). Null when an argument is null.

PadRight(text, width, char?)

The text padded on the right to width characters with char, a space by default. Null when an argument is null.

IsEmpty(text)

Whether the text is null or empty.

Abs(number)

The absolute value, of the same type. Null when an argument is null.

Round(number, digits?)

The number rounded to digits decimal places (0 by default, at most 15), halves away from zero: Round(2.5) is 3. Of the same type. Null when an argument is null.

Floor(number)

The largest whole number not greater than the number, of the same type. Null when an argument is null.

Ceiling(number)

The smallest whole number not less than the number, of the same type. Null when an argument is null.

Least(value, value, ...)

The smallest of the values, which may be numbers, text, dates or times. Nulls are skipped.

Greatest(value, value, ...)

The largest of the values, which may be numbers, text, dates or times. Nulls are skipped.

ToDecimal(value)

A number, or text such as '1234.5' (invariant culture), as a decimal. Null when an argument is null.

ToDouble(value)

A number, or text such as '1.5e3' (invariant culture), as a double. Null when an argument is null.

ToInteger(value)

A number, or text such as '42', as an integer. Decimals and doubles are truncated towards zero. Null when an argument is null.

Format(value, format?)

The value as text, with a .NET format and the report’s culture: Format(Total, 'C') is what {Total:C} shows in a text template.

Today()

Today’s date, from the clock the report is filled with, so a report can be reproduced. Null when an argument is null.

Now()

The date and time, from the clock the report is filled with. Null when an argument is null.

Date(year, month, day)

A date. Null when an argument is null.

Year(date)

The year of a date or date and time. Null when an argument is null.

Month(date)

The month (1 to 12) of a date or date and time. Null when an argument is null.

Day(date)

The day of the month of a date or date and time. Null when an argument is null.

AddDays(date, days)

The date moved by a number of days (negative to go back), of the same type. Null when an argument is null.

AddMonths(date, months)

The date moved by a number of months, of the same type. The day is kept, or becomes the month’s last: 31 January plus one month is the end of February. Null when an argument is null.

AddYears(date, years)

The date moved by a number of years, of the same type. Null when an argument is null.

DaysBetween(from, to)

The number of days from from to to, negative when to is earlier. Null when an argument is null.

Coalesce(value, value, ...)

The first value that is not null.

IsNull(value)

Whether the value is null.

The text after the colon in {value:format}, and the second argument of Format, is a .NET format string, applied in the report’s culture. For example, in en-US:

Template Shows
{1234.5:C} $1,234.50
{1234.5:C0} $1,235
{1234.5:N0} 1,235
{0.256:P0} 26%
{0.256:P1} 25.6%
{3.14159:F1} 3.1
{42:D5} 00042
{3.5:000.00} 003.50
{Date(2026, 9, 24):d} 9/24/2026
{Date(2026, 9, 24):D} Thursday, September 24, 2026
{Date(2026, 9, 24):yyyy-MM-dd} 2026-09-24
{Date(2026, 9, 24):MMM yyyy} Sep 2026

In de-DE the first one is 1.234,50 € and {0.256:P0} is 26 %. Give the number of decimals (N2, C2, F1): without it, the default comes from the operating system’s culture data and can differ between systems. See .NET’s numeric and date and time format strings for all of them.

A mistake in an expression is found when the template is compiled, with where it is: the property’s path and the column. A problem that only the data can cause, such as a division by zero or a value of the wrong type in a field, shows #Error where the value would be and is reported with its row, or fails the report if you prefer (FillOptions.Errors).