Club.CNews: Reports for Silverlight или построитель отчетов c шаблонами и с группировкой
Установка Reports
Библиотека Calabonga.Silverlight.Reports специально упакована в nuget-пакет чтобы можно было с легкостью устанавливать/обновлять/удалять эту библиотеку. Давайте предположим, что Nuget Manager у Вас уже установлен, и тогда можно сразу перейти к установке пакет отчетов. Я создал новый Silverlight-проект чтобы продемонстрировать работу библиотеки. Нажимаем на проекте правую кнопку мыши и запускаем менеджер Nuget:

В открывшемся окне менеджера в поле поиска вбиваем “calabonga” и перед нами все пакеты, которые доступны под брендом “calabonga”:

На данный момент нам потребуется первый в списке “Silverlight Reporting”. А следом за ним я установлю “Samlpe classes and data” чтобы было “что” печать и группировать.
Кнопка “Печать” и “Предварительный просмотр”
На главную страницу проекта добавлю две кнопки, а перед этим разобью на области сетку. А также добавлю ContentControl для того чтобы в него поместить предварительный просмотр сформированного отчета перед печатью:
1: <Grid x:Name="LayoutRoot"
2: Background="White">
3: <Grid.RowDefinitions> 4: <RowDefinition Height="36" />
5: <RowDefinition Height="*" />
6: </Grid.RowDefinitions> 7: <Button Content="Печать"
8: Height="23"
9: HorizontalAlignment="Left"
10: Margin="13,6,0,0"
11: Name="print"
12: VerticalAlignment="Top"
13: Width="75"
14: Click="print_Click" />
15: <Button Content="Просмотр"
16: Height="23"
17: HorizontalAlignment="Left"
18: Margin="94,6,0,0"
19: Name="preview"
20: VerticalAlignment="Top"
21: Width="75"
22: Click="preview_Click" />
23: <ContentControl Grid.Row="1"
24: VerticalContentAlignment="Stretch"
25: HorizontalContentAlignment="Stretch"
26: Name="previewBox" />
27: </Grid> Разметка простая, давай те теперь создадим шаблон.
Создаем шаблон печати (ItemTemplate)
Добавлю в проект новый Silverlight UserControl. Пусть называется он Report1.xaml:

Надо добавить namespace для того чтобы Report стал доступен:
xmlns:clb="http://schemas.calabonga.com"
Я во всех своих библиотеках предпочитаю использовать такой namespace, что не приходилось “бегать, искать” что в какой сборке. Вы можете использовать такой же подход в своих сборках. Как сделать “красивый” namespace уже было описано ранее.
Теперь пришло время подготовить данные для печати. Создадим пару-тройку переменных:
1: private List<Person> printData;
2: private Report1 report1;
Теперь вернемся в разметку самого шаблона, надо же к нему как-нибудь обращаться и для этого дадим ему имя, пусть зовут его CalaReport:
1: <Grid x:Name="LayoutRoot"
2: Background="White">
3: 4: <clb:Report Title="Первый отчет" x:Name="CalaReport">
5: 6: </clb:Report> 7: 8: </Grid> А теперь добавим шаблон (ItemTemplate), который будет “рисовать” Person. Если посмотреть в библиотеку SilverlightSampleData, то можно увидеть свойства класса, которые можно отобразить в шаблоне:

Мой шаблон будет такой:
1: <clb:Report.ItemTemplate> 2: <DataTemplate> 3: <Grid> 4: <Grid.ColumnDefinitions> 5: <ColumnDefinition /> 6: <ColumnDefinition /> 7: <ColumnDefinition /> 8: <ColumnDefinition /> 9: <ColumnDefinition /> 10: <ColumnDefinition /> 11: <ColumnDefinition /> 12: </Grid.ColumnDefinitions> 13: <Border Grid.Column="0"
14: Style="{StaticResource border}">
15: <TextBlock Text="{Binding Path=[Name]}"
16: Grid.Column="0" />
17: </Border> 18: <Border Grid.Column="1"
19: Style="{StaticResource border}">
20: <TextBlock Text="{Binding Path=[Gender]}"
21: Grid.Column="1" />
22: </Border> 23: <Border Grid.Column="2"
24: Style="{StaticResource border}">
25: <TextBlock Text="{Binding Path=[IsMember]}"
26: Grid.Column="2" />
27: </Border> 28: <Border Grid.Column="3"
29: Style="{StaticResource border}">
30: <TextBlock Text="{Binding Path=[Age]}"
31: Grid.Column="3" />
32: </Border> 33: <Border Grid.Column="4"
34: Style="{StaticResource border}">
35: <TextBlock Text="{Binding Path=[Description]}"
36: Grid.Column="4" />
37: </Border> 38: <Border Grid.Column="5"
39: Style="{StaticResource border}">
40: <TextBlock Text="{Binding Path=[Weight]}"
41: Grid.Column="5" />
42: </Border> 43: <Border Grid.Column="6"
44: Style="{StaticResource border}">
45: <TextBlock Text="{Binding Path=[Country]}"
46: Grid.Column="6" />
47: </Border> 48: </Grid> 49: 50: </DataTemplate> 51: </clb:Report.ItemTemplate> Обратите внимание на то, как осуществляется привязка – в квадратных скобках. Просто мне кажется “так красивее” :)
И добавим еще и стили, которые использованы в шаблоне для отображения.
1: <UserControl.Resources> 2: <Style TargetType="TextBlock"
3: x:Key="header">
4: <Setter Property="FontSize"
5: Value="11" />
6: <Setter Property="HorizontalAlignment"
7: Value="Center" />
8: <Setter Property="FontWeight"
9: Value="Bold" />
10: </Style> 11: <Style TargetType="Border"
12: x:Key="border">
13: <Setter Property="BorderBrush"
14: Value="Gray" />
15: <Setter Property="BorderThickness"
16: Value="1" />
17: <Setter Property="Margin"
18: Value="1" />
19: <Setter Property="Padding"
20: Value="3" />
21: </Style> 22: </UserControl.Resources> Напишем код обработки нажатия кнопок, я приведу весь код, чтобы больше к нему не возвращаться, потому что он не измениться:
1: public partial class MainPage : UserControl
2: { 3: private List<Person> printData;
4: private Report1 report1;
5: 6: public MainPage()
7: { 8: InitializeComponent(); 9: Loaded += new RoutedEventHandler(MainPage_Loaded);
10: } 11: 12: void MainPage_Loaded(object sender, RoutedEventArgs e)
13: { 14: report1 = new Report1();
15: printData = People.GetPeople(); 16: report1.CalaReport.ItemsSource = printData; 17: } 18: 19: private void print_Click(object sender, RoutedEventArgs e)
20: { 21: report1.CalaReport.Print(); 22: } 23: 24: private void preview_Click(object sender, RoutedEventArgs e)
25: { 26: previewBox.Content = report1.CalaReport.GetPreview(previewBox); 27: } 28: } Ну а теперь, давайте попробуем напечатать. Ура! Что-то видно на превью, да и на принтер что-то улетело… зажужжал гадина:

Надо бы HeaderTemplate и FooterTemplate добавить – для красоты и эстетики:
1: <clb:Report.PageHeaderTemplate> 2: <DataTemplate> 3: <Grid MinHeight="40"
4: Background="Wheat">
5: <Grid.ColumnDefinitions> 6: <ColumnDefinition /> 7: <ColumnDefinition /> 8: <ColumnDefinition /> 9: <ColumnDefinition /> 10: <ColumnDefinition /> 11: <ColumnDefinition /> 12: <ColumnDefinition /> 13: <ColumnDefinition /> 14: <ColumnDefinition /> 15: </Grid.ColumnDefinitions> 16: <Border Grid.Column="0"
17: Style="{StaticResource border}">
18: <TextBlock Text="Имя"
19: Style="{StaticResource header}" />
20: </Border> 21: <Border Grid.Column="1"
22: Style="{StaticResource border}">
23: <TextBlock Text="Пол"
24: Style="{StaticResource header}" />
25: </Border> 26: <Border Grid.Column="2"
27: Style="{StaticResource border}">
28: <TextBlock Text="Участник"
29: Style="{StaticResource header}" />
30: </Border> 31: <Border Grid.Column="3"
32: Style="{StaticResource border}">
33: <TextBlock Text="Возраст"
34: Style="{StaticResource header}" />
35: </Border> 36: <Border Grid.Column="4"
37: Style="{StaticResource border}">
38: <TextBlock Text="Описание"
39: Style="{StaticResource header}" />
40: </Border> 41: <Border Grid.Column="5"
42: Style="{StaticResource border}">
43: <TextBlock Text="Вес"
44: Style="{StaticResource header}" />
45: </Border> 46: <Border Grid.Column="6"
47: Style="{StaticResource border}">
48: <TextBlock Text="Страна"
49: Style="{StaticResource header}" />
50: </Border> 51: </Grid> 52: </DataTemplate> 53: </clb:Report.PageHeaderTemplate> и нижняя часть:
1: <clb:Report.ReportFooterTemplate> 2: <DataTemplate> 3: <Grid HorizontalAlignment="Stretch"
4: MinHeight="30"
5: Background="LightGray">
6: <TextBlock Text="Этот отчет сгенерирован при помощи библиотеки Calabonga.Silverlight.Reports." />
7: </Grid> 8: </DataTemplate> 9: </clb:Report.ReportFooterTemplate> Нажимаем печать… И!…

Страница уже выглядит по другому. Про Вас не обращать внимание на шапку, немного кривая вышла, что, кстати, подтверждает чистоту эксперимента.
Группировка и Агрегирующие функции
Эта библиотека была изначально задумана как “способная группировать и агрегировать данные при печати. Пришло время проверить на что она способна. Перед тем как добавить группировку надо подключить mscorlib.dll:
1: xmlns:sys="clr-namespace:System;assembly=mscorlib"
Добавим группировку по стране (Country):
1: <clb:Report.Groups> 2: <clb:GroupDefinitions> 3: <clb:GroupDefinitions.GroupedFields> 4: <sys:String>Country</sys:String> 5: </clb:GroupDefinitions.GroupedFields> 6: </clb:GroupDefinitions> 7: </clb:Report.Groups> Определение поля по которому группировать происходит в четвертой строке. Можно добавить сколь угодно параметров группировки (конечно же в разумных пределах). А добавлю-ка я сразу еще и по половому признаку группировку поместив строку:
1: <sys:String>Gender</sys:String> Между 4 и 5 строками. Теперь раз добавлена группировка следует добавить и шаблоны для группировки (Header и Footer), иначе прилетит ошибка. Шаблон группировки выглядит так:
1: <clb:Report.GroupHeaderTemplate> 2: <DataTemplate> 3: <Border BorderBrush="Black"
4: BorderThickness="1"
5: Background="Silver"
6: MinHeight="40">
7: <StackPanel Orientation="Horizontal">
8: <TextBlock Text="{Binding Path=[Country]}"
9: Margin="30,0,20,0"
10: Style="{StaticResource header}" />
11: <TextBlock Text="{Binding Path=[Gender]}"
12: Margin="30,0,20,0"
13: Style="{StaticResource header}" />
14: <TextBlock Text="{Binding Path=[Weight]}"
15: Margin="30,0,20,0"
16: Style="{StaticResource header}" />
17: <TextBlock Text="{Binding Path=[Age]}"
18: Margin="30,0,20,0"
19: Style="{StaticResource header}" />
20: </StackPanel> 21: </Border> 22: </DataTemplate> 23: </clb:Report.GroupHeaderTemplate> 24: <clb:Report.GroupFooterTemplate> 25: <DataTemplate> 26: <Border BorderBrush="Black"
27: BorderThickness="1"
28: Background="Gainsboro"
29: MinHeight="30">
30: <StackPanel Orientation="Horizontal">
31: <TextBlock Text="Итого вес:"
32: Margin="0,0,20,0" />
33: <TextBlock Text="{Binding Path=[Weight]}"
34: Style="{StaticResource header}"
35: Margin="0,0,60,0" />
36: <TextBlock Text="Возраст средний:"
37: Margin="0,0,20,0" />
38: <TextBlock Text="{Binding Path=[Age]}"
39: Style="{StaticResource header}" />
40: </StackPanel> 41: </Border> 42: </DataTemplate> 43: </clb:Report.GroupFooterTemplate> И, как Вы уже наверное заметили, в шаблонах используются агрегирующие данные. А чтобы они появились следует добавить информацию об агрегации групп:
1: <clb:GroupDefinitions.Aggregations> 2: <clb:AggregateFieldDefinition Field="Weight">
3: <clb:AggregateFieldDefinition.AggregateFunctions> 4: <clb:AggregateFunctionDefinition Function="Sum" />
5: </clb:AggregateFieldDefinition.AggregateFunctions> 6: </clb:AggregateFieldDefinition> 7: <clb:AggregateFieldDefinition Field="Age">
8: <clb:AggregateFieldDefinition.AggregateFunctions> 9: <clb:AggregateFunctionDefinition Function="Average" />
10: </clb:AggregateFieldDefinition.AggregateFunctions> 11: </clb:AggregateFieldDefinition> 12: </clb:GroupDefinitions.Aggregations> Компилируем… Запускаем…. Нажимаем предварительный просмотр… И… вуаля:

Шаблоны подготовленные мной конечно оставляют желать лучшего по части эстетического вида, но как бы там не было отчет на бумаге.
Так же хотелось бы добавить, что существуют еще и другие варианты шаблонов. А так же достаточное количество событий.

Надеюсь, что контрол будет востребован. На этом всё. Пишите комментарии.
© CNews
