This article is the second part of the Template Syntax series of articles. For first part, please visit Template Syntax - Part 1 of 2.
Outputting Expression Results
You can output expression results to your reports using expression tags. An expression tag denotes a placeholder for an expression result within a template. While building a report, the corresponding expression is evaluated, and this placeholder is replaced with the formatted result of the expression.
An expression tag has no name and consists of the following elements:
An expression enclosed by brackets
An optional format string enclosed by double quotes and preceded by the “:” character
An optional html switch
<<[expression]:"format"-html>>
If html switch is not present, the result of the corresponding expression is written to a document as a plain text at runtime. Font attributes are derived from the first character of the corresponding tag in this case.
If html switch is present, the expression result is considered to be a HTML block and is written as such. This feature is useful, when you need to format text parts of an expression result in different ways. For example, the following tag is replaced with a content like “Bold and italic text” at runtime.
<<["<b>Bold</b> and <i>italic</i> text"]-html>>
To format a numeric or date-time expression result, you can specify a format string as an element of the corresponding expression tag. Such format strings are the same as the ones that you pass to IFormattable.ToString method implementors. That is, for example, given that d is a DateTime value, you can use the following template to format the value using the “yyyy.MM.dd” pattern.
<<[d]:"yyyy.MM.dd">>
Outputting Sequential Data
You can output a sequence of elements of the same type to your report using a data band. A data band has a body that represents a template for a single element of such a sequence. While building a report, sequence elements are enumerated, and the following procedure takes place for each of the elements:
The data band body is duplicated and appended to the report.
The appended data band body is populated with the element’s data.
Note – A data band body can contain nested data bands.
A data band body is defined between the corresponding opening and closing foreach tags within a template as follows.
<<foreach...>>data_band_body<</foreach>>
You can reference an element of the corresponding sequence in template expressions within a data band body using an iteration variable. At runtime, an iteration variable represents a sequence element for which an iteration is currently being performed. You can declare an iteration variable within the corresponding opening foreach tag.
An opening foreach tag defines a foreach statement enclosed by brackets. The following table describes elements of this statement.
Element
Optional?
Remarks
Iteration Variable Type
Yes
You can specify the type of an iteration variable explicitly. This type must be known by the engine (see Setting up Known External Types for more information). If you do not specify the type explicitly, it is determined implicitly by the engine depending on the type of the corresponding sequence.
Iteration Variable Name
Yes
You can specify the name of an iteration variable to use it while accessing the variable’s members. The name must be unique within the scope of the corresponding foreach tag. If you do not specify the name, you can access the variable’s members using the contextual object member access syntax (see Using Contextual Object Member Access" for more information).
“in” Keyword
No
Sequence Expression
No
A sequence expression must return an IEnumerable implementor.
The complete syntax of a foreach tag (including optional elements) is as follows.
A common data band is a data band which body starts and ends within paragraphs that belong to a single story or table cell.
In particular, a common data band can be entirely located within a single paragraph. In this case, while building a report, the band is replaced with contents that are entirely located within the same paragraph as well. The following example illustrates such a scenario. Given that items is an enumeration of the strings “item1”, “item2”, and “item3”, you can use the following template to enumerate them with commas in a single paragraph.
In this case, the engine produces a report as follows.
Theitemsare:item1,item2,item3,andothers.
When the body of a common data band starts and ends within different paragraphs, the engine duplicates on iteration only those paragraph breaks which are located within the body. The following table illustrates the relevant cases.
Note – Examples in the table are given with paragraph marks shown as per Microsoft Word® editor.
Template
Report
prefix <<foreach [item in items]>><<[item]>>¶ <</foreach>>suffix
prefix item1¶ item2¶ item3¶ suffix
prefix<<foreach [item in items]>>¶ <<[item]>><</foreach>> suffix
prefix¶ item1¶ item2¶ item3 suffix
prefix¶ <<foreach [item in items]>><<[item]>>¶ <</foreach>>suffix
prefix¶ item1¶ item2¶ item3¶ suffix
prefix<<foreach [item in items]>>¶ <<[item]>><</foreach>>¶ suffix
prefix¶ item1¶ item2¶ item3¶ suffix
prefix¶ <<foreach [item in items]>>¶ <<[item]>>¶ <</foreach>>¶ suffix
prefix¶ ¶ item1¶ ¶ item2¶ ¶ item3¶ ¶ suffix
While building a report, duplicated paragraph breaks derive common attributes from their template prototypes. In particular, this fact enables you to build numbered or bulleted lists in reports dynamically. For example, given the above declaration of items, you can get a report with their numbered list using the following template.
Merging Table Cells Dynamically
You can merge table cells with equal textual contents within your reports dynamically using cellMerge tags. Syntax of a cellMerge tag is defined as follows.
<<cellMerge>>
By default, a cellMerge tag causes a cell merging operation only in a vertical direction during runtime. However, you can alter this behavior in the following ways:
To merge cells only in a horizontal direction, use the horz switch as follows.
<<cellMerge -horz>>
To merge cells in both – vertical and horizontal – directions, use the both switch as follows.
<<cellMerge -both>>
For two or more successive table cells to be merged dynamically in either direction by the engine, the following requirements must be met:
Each of the cells must contain a cellMerge tag denoting a cell merging operation in the same direction (or directions).
Each of the cells must not be already merged in another direction. This requirement does not apply when a both switch is used.
The cells must have equal textual contents ignoring leading and trailing whitespaces.
Consider the following template.
…
…
…
…
«cellMerge»«[value1]»
…
…
«cellMerge»«[value2]»
…
…
…
…
If value1 and value2 have the same value, say “Hello”, table cells containing cellMerge tags are successfully merged during runtime and a result report looks as follows then.
...
...
...
...
Hello
...
...
...
...
...
...
If value1 and value2 have different values, say “Hello” and “World”, table cells containing cellMerge tags are not merged during runtime and a result report looks as follows then.
…
…
…
…
Hello
…
…
World
…
…
…
…
Note – A cellMerge tag can be normally used within a table data band.
You can define an additional restriction on dynamic merging of table cells by providing an expression for acellMerge tag using the following syntax.
<<cellMerge [expression]>>
During runtime, expressions defined for cellMerge tags are evaluated and dynamic cell merging is discarded for those tags, which expressions return unequal values, even if all other conditions for merging such as equal cell textual contents are met. In particular, this feature is useful when cells corresponding to different data band sequence elements should not be merged as shown in the following example.
Assume that you have the Invoice and InvoiceItem classes defined in your application as follows.
Given that invoices is an enumeration of Invoice instances, you could use the following template to output information on several invoices in one table.
#
Ware
Pack
Quantity
«foreach [invoice in invoices]»«foreach [item in invoice.Items]»«[invoice.Number]»«cellMerge»
«[item.Ware]»«cellMerge»
«[item.Pack]»
«[item.Quantity]»«/foreach»«/foreach»
A result document would look as follows then.
#
Ware
Pack
Quantity
11342
Natural Mineral Water
Bottle 1.0 L
30
Bottle 0.5 L
50
15385
Bottle 1.0 L
110
That is, cells corresponding to the same wares at different invoices would be merged, which is unwanted. To prevent this from happening, you can use the following template instead.
#
Ware
Pack
Quantity
«foreach [invoice in invoices]»«foreach [item in invoice.Items]»«[invoice.Number]»«cellMerge»
«[item.Ware]»«cellMerge [invoice.IndexOf()]»
«[item.Pack]»
«[item.Quantity]»«/foreach»«/foreach»
Then, a result document looks as follows.
#
Ware
Pack
Quantity
11342
Natural Mineral Water
Bottle 1.0 L
30
Bottle 0.5 L
50
15385
Natural Mineral Water
Bottle 1.0 L
110
Numbered Lists
“1. " in the above template stands for a numbered list label.
1.<<foreach[iteminitems]>><<[item]>><</foreach>>
In this case, the engine produces a report as follows.
1.item12.item23.item3
Dynamic list numbering restart
This feature is useful when working with a nested numbered list within a data band as shown in the following example.
Assume that you have the Order and Service classes defined in your application as follows.
Given that orders is an enumeration of Order instances, you could try to use the following template to output information on several orders in one document.
That is, there would be a single numbered list across all orders, which is not applicable for this scenario. However, you can make list numbering to restart for every order by putting a restartNum tag into your template before a corresponding foreach tag as follows.
You can use a restartNum tag without a data band to dynamically restart list numbering for a containing paragraph, for example, the tag can be used to restart list numbering for a document inserted dynamically.
Table-Row Data Bands
A table-row data band is a data band which body occupies single or multiple rows of a single document table. The body of such a band starts at the beginning of the first occupied row and ends at the end of the last occupied row as follows.
<<foreach ...>> ...
...
...
...
...
...
...
...
... <</foreach>>
The following examples in this section are given using `ds`, a `DataSet` instance containing `DataTable` and `DataRelation` objects according to the following data model.
The most common use case of a table-row data band is the building of a document table that represents a list of items. You can use a template like the following one to achieve this.
Client
Manager
Contract Price
<<foreach [c in ds.Contracts]>><<[c.Clients.Name]>>
<<[c.Managers.Name]>>
<<[c.Price]>><</foreach>>
Total:
<<[ds.Contracts.Sum(c => c.Price)]>>
In this case, the engine produces a report as follows.
Client
Manager
Contract Price
A Company
John Smith
1200000
B Ltd.
John Smith
750000
C & D
John Smith
350000
E Corp.
Tony Anderson
650000
F & Partners
Tony Anderson
550000
G & Co.
July James
350000
H Group
July James
250000
I & Sons
July James
100000
J Ent.
July James
100000
Total:
4300000
To populate a document table with a master-detail data, you can use nested table-row data bands like in the following template.
Manager/Client
Contract Price
<<foreach [m in ds.Managers]>><<[m.Name]>>
<<[m.Contracts.Sum(c => c.Price)]>>
<<foreach [c in m.Contracts]>> <<[c.Clients.Name]>>
<<[c.Price]>><</foreach>><</foreach>>
Total:
<<[ds.Contracts.Sum(c => c.Price)]>>
In this case, the engine produces a report as follows.
Manager/Client
Contract Price
John Smith
2300000
A Company
1200000
B Ltd.
750000
C & D
350000
Tony Anderson
1200000
E Corp.
650000
F & Partners
550000
July James
800000
G & Co.
350000
H Group
250000
I & Sons
100000
J Ent.
100000
Total:
4300000
You can normally use common data bands nested to table-row data bands as well like in the following template.
Manager
Clients
<<foreach [m in ds.Managers]>><<[m.Name]>>
<<foreach [c in m.Contracts]>><<[c.Clients.Name]>><br/><</foreach>><</foreach>>
In this case, the engine produces a report as follows.
Manager
Clients
John Smith
A Company B Ltd. C & D
Tony Anderson
E Corp. F & Partners
July James
G & Co. H Group I & Sons J Ent.
Extension Methods of Iteration Variables
GroupDocs.Assembly Engine provides special extension methods for iteration variables of any type. You can normally use these extension methods in template expressions. The following list describes the extension methods.
IndexOf()
Returns the zero-based index of a sequence item that is represented by the corresponding iteration variable. You can use this extension method to distinguish sequence items with different indexes and then handle them in different ways. For example, given that items is an enumeration of the strings “item1”, “item2”, and “item3”, you can use the following template to enumerate them prefixing all of them but the first one with commas.
In this case, the engine produces a report as follows.
Theitemsare:item1,item2,item3.
NumberOf()
Returns the one-based index of a sequence item that is represented by the corresponding iteration variable. You can use this extension method to number sequence items without involving Microsoft Word® lists. For example, given the previous declaration of items, you can enumerate and number them in a document table using the following template.
No.
Item
<<foreach [item in items]>><<[item.NumberOf()]>>
<<[item]>><</foreach>>
In this case, the engine produces a report as follows.
No.
Item
1
item1
2
item2
3
item3
Charts Representing Sequential Data
GroupDocs.Assembly Engine enables you to use charts to represent your sequential data. To declare a chart that is going to be populated with data dynamically within your template, do the following steps:
Add a chart to your template at the place where you want it to appear in a result document.
Configure the appearance of the chart.
Add required chart series and configure their appearance as well.
Add a title to the chart, if missing.
Add an opening foreach tag to the chart title.
Depending on the type of the chart, add x tags to the chart title or chart series’ names as follows.
<<x[x_value_expression]>>
For a scatter or bubble chart, you can go one of the following ways:
To use the same x-value expression for all chart series, add a single x tag to the chart title after the corresponding foreachtag.
To use different x-value expressions for every chart series, add multiple x tags to chart series’ names – one for each chart series. An x-value expression for a scatter or bubble chart must return a numeric value.
For a chart of another type, add a single x tag to the chart title after the corresponding foreach tag. In this case, an x-value expression must return a numeric, date, or string value.
For a chart of any type, add y tags to chart series’ names as follows.
<<y[y_value_expression]>>
An y-value expression must return a numeric value.
For a bubble chart, add size tags to chart series’ names as follows.
<<size[bubble_size_expression]>>
A bubble-size expression must return a numeric value.
For a chart with dynamic data, you can select which series to include into it dynamically based upon conditions. In particular, this feature is useful when you need to restrict access to sensitive data in chart series for some users of your application. To use the feature, do the following steps:
Declare a chart with dynamic data in the usual way
For series to be removed from the chart based upon conditions dynamically, define the conditions in names of these series using removeif tags having the following syntax
<<removeif[conditional_expression]>>
A conditional expression must return a Boolean value.
Note: A closing foreach tag is not used for a chart. While composing expressions for x, y, and size tags, you can normally reference an iteration variable declared at the corresponding foreach tag in a chart title in the same way as if you intended to output results of expressions within a data band.
Note: You can normally use charts with dynamic data within data bands. During runtime, a chart with a foreach tag in its title is processed by the engine as follows:
A sequence expression declared at the foreach tag is evaluated and iterated.
For every sequence item, expressions declared at x, y, and size tags are evaluated.
Results of these expressions are used to populate corresponding chart series.
All foreach, x, y, and size tags are removed from the chart title and chart series’ names. Consider the following example. Assume that you have the Manager and Contract classes defined in your application as follows.
Given that managers is an enumeration of Manager instances, you can use the following template to represent total contract prices achieved by managers in a column chart.
In this case, the engine produces a report as follows.
Enumeration Extension Methods
The Enumeration
GroupDocs.Assembly Engine enables you to perform common manipulations on a sequential data through the engine’s built-in extension methods for IEnumerable. These extension methods mimic some extension methods of IEnumerable providing the same signatures and behavior features. Thus, you can group, sort, and perform other sequential data manipulations in template expressions in a familiar way.
The Enumeration Extension
Below table describes the built-in extension methods. The following notation conventions are used within the table:
Selector stands for a lambda function returning a value and taking an enumeration item as its single argument. See Using Lambda Functions for more information.
ComparableSelector stands for Selector returning IComparable.
Predicate stands for Selector returning a Boolean value.
Examples in this table are given using persons and otherPersons, enumerations of instances of the Person class that is defined as follows.
persons.Average(p => p.Age) The input selector must return a value of any type that has predefined or user-defined addition and division operators.
Concat(IEnumerable)
persons.Concat(otherPersons) An implicit reference conversion must exist between types of items of concatenated enumerations.
Contains(Object)
persons.Contains(otherPersons.First())
Count()
persons.Count()
Count(Predicate)
persons.Count(p => p.Age > 30)
Distinct()
persons.Distinct()
First()
persons.First()
First(Predicate)
persons.First(p => p.Age > 30)
FirstOrDefault()
persons.FirstOrDefault()
FirstOrDefault(Predicate)
persons.FirstOrDefault(p => p.Age > 30)
GroupBy(Selector)
persons.GroupBy(p => p.Age) Or persons.GroupBy(p => new { Age = p.Age, Count = p.Children.Count() }) This method returns an enumeration of group objects. Each group has a unique key defined by the input selector and contains items of the source enumeration associated with this key. You can access the key of a group instance using the Key property. You can treat a group itself as an enumeration of items that the group contains.
Last()
persons.Last()
Last(Predicate)
persons.Last(p => p.Age > 100)
LastOrDefault()
persons.LastOrDefault()
LastOrDefault(Predicate)
persons.LastOrDefault(p => p.Age > 100)
Max(ComparableSelector)
persons.Max(p => p.Age)
Min(ComparableSelector)
persons.Min(p => p.Age)
OrderBy(ComparableSelector)
persons.OrderBy(p => p.Age) Or persons.OrderBy(p => p.Age).ThenByDescending(p => p.Name) Or persons.OrderBy(p => p.Age).ThenByDescending(p => p.Name).ThenBy(p => p.Children.Count()) This method returns an enumeration ordered by a single key. To specify additional ordering keys, you can use the following extension methods of an ordered enumeration:
ThenBy(ComparableSelector)
ThenByDescending(ComparableSelector)
OrderByDescending(ComparableSelector)
persons.OrderByDescending(p => p.Age) Or persons.OrderByDescending(p => p.Age).ThenByDescending(p => p.Name) Or persons.OrderByDescending(p => p.Age).ThenByDescending(p => p.Name).ThenBy(p => p.Children.Count()) See the previous note.