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>
Компилируем… Запускаем…. Нажимаем предварительный просмотр… И… вуаля:
Шаблоны подготовленные мной конечно оставляют желать лучшего по части эстетического вида, но как бы там не было отчет на бумаге.
Так же хотелось бы добавить, что существуют еще и другие варианты шаблонов. А так же достаточное количество событий.
Надеюсь, что контрол будет востребован. На этом всё. Пишите комментарии.