Как создать отчет из кода приложения в FastReport .NET

28.02.2016

На новой работе мне было поручено разобраться с генератором отчетов FastReport .NET. Раньше мне приходилось иметь дело с другими системами отчетности, например, Crystal Reports и Microsoft Reporting Services. Их функционала было достаточно для меня, до того, как я познакомился с FastReport.

Действительно мощный продукт с широким функционалом. Одна из наиболее понравившихся мне особенностей FastReport .NET – возможность создания отчетов прямо из кода пользовательского приложения. В этой статье я хочу рассмотреть пример использования такой «фичи». Это действительно удобно, когда не нужна куча файлов, поставляемых вместе с exe-шником. Кроме того, можно полностью контролировать создание отчета самостоятельно, изменяя вид объекта отчета в зависимости от логики приложения.

Прежде всего, объясню, чем же отличается построение отчета из кода пользовательского приложения от классической разработки шаблона в специальном дизайнере.

Обычно, генератор отчетов предоставляет специальный дизайнер для разработки шаблона отчета. Это может быть компонент IDE, либо просто внешняя программа. Разработчик размещает компоненты вывода данных на странице отчета, задает их свойства. Это похоже на конструирование формы приложения в проекте Windows Forms.

Помимо такого классического способа создания шаблона отчета, FastReport дает возможность создать шаблон, используя те же компоненты, но не визуальным редактором, а привычным для программиста кодом в приложении. Создается класс отчета, в него добавляются объекты отчета, настраивается источник данных. При определенной практике, создание отчета из кода займет немногим больше времени, чем из визуального редактора. Интересно, что в итоге такой шаблон отчета можно просмотреть все в том же визуальном редакторе и сохранить как файл.

Итак, рассмотрим все на примере.

Создаем приложение Windows Forms на языке C# (конечно же FastReport .Net должен быть установлен к этому моменту).

На форме размещаем одну кнопку, которая будет запускать наш отчет. Забегая вперед, скажу, что мы будем не только показывать отчет в режиме предварительного просмотра, но и делать его экспорт в PDF. Поэтому предусмотрим эту опцию добавив CheckBox:

write in PDF

 

Создаем обработчик события нажатия кнопки. Здесь будет весь код нашего приложения.

Перво-наперво, добавляем ссылку на библиотеку FastReport.dll (которая находится в паке FastReport .Net).

Также, добавляем библиотеки FastReport, FastReport.Utils и FastReport.Data в using.

Создаем экземпляр класса Report:

1
2
3
4
5
6
7
8
9
 private void RunBtn_Click(object sender, EventArgs e)
 
 {
 
 //Create instance of class Report
 
 Report report = new Report();
 
 }

 В нашем отчете будут выводиться данные из базы, так что необходимо подключить источник данных:

1
2
3
4
5
//load data
 
DataSet ds = new DataSet();
 
ds.ReadXml(AppFolder + "\\nwind.xml");

 Базу данных я взял из поставки FastReport .Net в папке Reports.

Теперь необходимо зарегистрировать источник данных в отчете:

1
2
3
//Register data source
 
report.RegisterData(ds);

 Чтобы использовать таблицу из зарегистрированного источника данных, нужно включить ее:

1
2
3
//Enable data table
 
report.GetDataSource("Products").Enabled = true;

 Подготовительные работы считаю на этом законченными. Приступаем  непосредственно к созданию шаблона отчета. Создаем страницу отчета:

1
2
3
//Add report page
 
ReportPage page = new ReportPage();

 И добавляем ее в отчет:

1
report.Pages.Add(page);

 Всем объектам отчета нужно давать уникальные имена. Можно придумывать их самостоятельно и присваивать свойству Name, а можно использовать функцию, которая сама сгенерирует уникальное имя:

1
page.CreateUniqueName();

 Итак, страница отчета готова к наполнению. Создаем бэнд «Заголовок группы»:

1
2
3
//Create GroupHeader band
 
GroupHeaderBand group = new GroupHeaderBand();

 Созданный бэнд добавляем на страницу:

1
2
3
page.Bands.Add(group);
 
group.CreateUniqueName();

 Задаем высоту бэнда:

1
group.Height = Units.Centimeters * 1;

 Условие группировки и порядок сортировки:

1
2
3
group.Condition = "[Products.ProductName].Substring(0,1)";
 
group.SortOrder = FastReport.SortOrder.Ascending;

 Теперь нужно наполнить созданный бэнд данными. Для этого нужно создать текстовый объект со ссылкой на поле из источника данных:

1
2
3
// create group text
 
TextObject groupTxt = new TextObject();

 Важный параметр Parent, который указывает на бенд, где будет размещен текстовый объект:

1
2
3
groupTxt.Parent = group;
 
groupTxt.CreateUniqueName();

 Задаем размер и границы текстового объекта:

1
groupTxt.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 1);

 И, собственно, сам текст:

1
groupTxt.Font = new Font("Arial", 14, FontStyle.Bold);

 Остальные настройки относятся к внешнему виду текста:

1
2
3
4
5
groupTxt.Text = "[[Products.ProductName].Substring(0,1)]";
 
groupTxt.VertAlign = VertAlign.Center;
 
groupTxt.Fill = new LinearGradientFill(Color.LightGoldenrodYellow, Color.Gold, 90, 0.5f, 1);

 Теперь самое интересное - создание бэнда «Данные»: 

1
2
3
// create data band
 
DataBand data = new DataBand();

 Присваиваем бэнд данных группе:

1
2
3
group.Data = data;
 
data.CreateUniqueName();

 Назначаем источник данных для бэнда «Данные»:

1
2
3
data.DataSource = report.GetDataSource("Products");
 
data.Height = Units.Centimeters * 0.5f;

 Тут же можно задать фильтрацию бэнда с помощью свойства Filter. Теперь заполним бэнд данными с помощью текстового объекта:

1
2
3
4
5
6
7
8
9
10
11
// create product name text
 
TextObject productText = new TextObject();
 
productText.Parent = data;
 
productText.CreateUniqueName();
 
productText.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 0.5f);
 
productText.Text = "[Products.ProductName]";

 Подвал группы создается для конкретного экземпляра группы. Это очень удобно, так как не позволит запутаться, если в отчете несколько заголовков групп. Итак, создаем подвал группы:     

1
2
3
4
5
6
7
// create group footer
 
 group.GroupFooter = new GroupFooterBand();
 
 group.GroupFooter.CreateUniqueName();
 
 group.GroupFooter.Height = Units.Centimeters * 1;

 Чтобы подвал группы не пустовал, добавим в него итоговое значение, которое будет отображать количество продуктов в группе:

1
2
3
4
5
 // create total
 
 Total groupTotal = new Total();
 
 groupTotal.Name = "TotalRows";

 Задаем тип вычислений, бэнд, для которого проводятся вычисления, и бэнд, в котором будет отображен результат. Так как мы считаем количество элементов, задавать конкретное поле для расчетов не нужно (делается с помощью groupTotal.Expression).

1
2
3
4
5
 groupTotal.TotalType = TotalType.Count;
 
 groupTotal.Evaluator = data;
 
 groupTotal.PrintOn = group.GroupFooter;

 Созданный итог нужно добавить в каталог итогов отчета. Так сказать, зарегистрировать:

1
report.Dictionary.Totals.Add(groupTotal);

 Как и любые выражения, которые нужно отобразить, итог выводится посредством текстового объекта:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// show total in the group footer
 
 TextObject totalText = new TextObject();
 
 totalText.Parent = group.GroupFooter;
 
 totalText.CreateUniqueName();
 
 totalText.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 0.5f);
 
 totalText.Text = "Rows: [TotalRows]";
 
 totalText.HorzAlign = HorzAlign.Right;
 
 totalText.Border.Lines = BorderLines.Top;

 Вот и все. Отчет готов. Теперь мы можем либо отобразить его, либо запустить в дизайнере. А можно сразу же экспортировать в нужный нам формат данных. Задействуем CheckBox, который мы добавили на форму:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 if (PDFCheckBox.Checked)
 
 {
 
 report.Prepare();
 
FastReport.Export.Pdf.PDFExport export = new FastReport.Export.Pdf.PDFExport();
 
export.Export(report);
 
}
 
 else
 
 report.Show();

 Если CheckBox отмечен, то будет показано диалоговое окно сохранения файла pdf. Иначе будет запущен отчет в режиме предварительного просмотра. Тут надо отметить, что можно сделать экспорт и без отображения диалогового окна. Так сказать, в сохранение в скрытом режиме. Тогда экспорт будет выглядеть так:

1
export.Export(report, @"C:\Temp\ReportFromCode.pdf");

 где первый параметр – экземпляр отчета, а второй – итоговый файл.

 Что же у нас получилось в итоге:

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 //Create instance of class Report
 
 Report report = new Report();
 
 
 
 //load data
 
 DataSet ds = new DataSet();
 
 ds.ReadXml(AppFolder + "\\nwind.xml");
 
 
 
 //Register data source
 
 report.RegisterData(ds);
 
 
 
 //Enable data table
 
 report.GetDataSource("Products").Enabled = true;
 
 
 
 //Add report page
 
 ReportPage page = new ReportPage();
 
 report.Pages.Add(page);
 
 page.CreateUniqueName();
 
 
 
 //Create GroupHeader band
 
 GroupHeaderBand group = new GroupHeaderBand();
 
 page.Bands.Add(group);
 
 group.CreateUniqueName();
 
 group.Height = Units.Centimeters * 1;
 
 group.Condition = "[Products.ProductName].Substring(0,1)";
 
 group.SortOrder = FastReport.SortOrder.Ascending;
 
 
 
 // create group text
 
 TextObject groupTxt = new TextObject();
 
 groupTxt.Parent = group;
 
 groupTxt.CreateUniqueName();
 
 groupTxt.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 1);
 
 
 
 groupTxt.Text = "[[Products.ProductName].Substring(0,1)]";
 
 groupTxt.Font = new Font("Arial", 14, FontStyle.Bold); 
 
 groupTxt.VertAlign = VertAlign.Center;
 
 groupTxt.Fill = new LinearGradientFill(Color.LightGoldenrodYellow, Color.Gold, 90, 0.5f, 1);
 
 
 
 // create data band
 
 DataBand data = new DataBand();
 
 group.Data = data;
 
 data.CreateUniqueName();
 
 data.DataSource = report.GetDataSource("Products");
 
 data.Height = Units.Centimeters * 0.5f;
 
 
 
 // create product name text
 
 TextObject productText = new TextObject();
 
 productText.Parent = data;
 
 productText.CreateUniqueName();
 
 productText.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 0.5f);
 
 productText.Text = "[Products.ProductName]";
 
 
 
 // create group footer
 
 group.GroupFooter = new GroupFooterBand();
 
 group.GroupFooter.CreateUniqueName();
 
 group.GroupFooter.Height = Units.Centimeters * 1;
 
 
 
 // create total
 
 Total groupTotal = new Total();
 
 groupTotal.Name = "TotalRows";
 
 groupTotal.TotalType = TotalType.Count;
 
 groupTotal.Evaluator = data;
 
 groupTotal.PrintOn = group.GroupFooter;
 
 report.Dictionary.Totals.Add(groupTotal);
 
 
 
 // show total in the group footer
 
 TextObject totalText = new TextObject();
 
 totalText.Parent = group.GroupFooter;
 
 totalText.CreateUniqueName();
 
 totalText.Bounds = new RectangleF(0, 0, Units.Centimeters * 10, Units.Centimeters * 0.5f);
 
 totalText.Text = "Rows: [TotalRows]";
 
 totalText.HorzAlign = HorzAlign.Right;
 
 totalText.Border.Lines = BorderLines.Top;
 
 
 
 if (PDFCheckBox.Checked)
 
 {
 
 report.Prepare();
 
 FastReport.Export.Pdf.PDFExport export = new FastReport.Export.Pdf.PDFExport();
 
 export.Export(report);
 
 //export.Export(report, @"C:\Temp\ReportFromCode.pdf");
 
 }
 
 else
 
 report.Show();

 И сам отчет:

Report

 

Подведем итоги. FastReport .NET порадовал еще одной интересной фичей – создание отчета из кода. Когда это может быть полезно? Если вы не хотите плодить кучу отдельных файликов с шаблонами отчетов или хотите скрыть шаблон отчета внутри программы во избежание порчи или изменения шаблона. Также удобно менять шаблон отчета прямо во время выполнения вашего приложения. Это дает большую гибкость отчетам и возможность использовать один шаблон, изменяя его в зависимости от логики программы.

Лично мне привычно и удобно использовать объекты в коде программы. Так что создание отчета практически ничем не отличается от написания основного кода оконного приложения.

20 ноября 2024

Локализация и смена языков в FastReport VCL

FastReport VCL поддерживает 40 языков для локализации интерфейса и позволяет изменять язык на лету через меню или код, без перекомпиляции.
1 ноября 2024

Новые возможности редактора отчетов FastReport VCL

Рассматриваем новые возможности редактора отчетов: выносные линии, подсветка пересекающихся объектов, обновлённые деревья отчетов и данных.
30 октября 2024

Использование стилей при создании отчетов в FastReport VCL

В статье подробно рассматривается одна из новых возможностей FastReport VCL – применение стилей и страниц стилей.