Club.CNews: Reports for Silverlight или построитель отчетов c шаблонами и с группировкой

Установка Reports

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

image

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

image

На данный момент нам потребуется первый в списке “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:

image

Надо добавить 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, то можно увидеть свойства класса, которые можно отобразить в шаблоне:

image

Мой шаблон будет такой:

 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: }

Ну а теперь, давайте попробуем напечатать. Ура! Что-то видно на превью, да и на принтер что-то улетело… зажужжал гадина:

image

Надо бы 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>

Нажимаем печать… И!…

Untitled-3

Страница уже выглядит по другому. Про Вас не обращать внимание на шапку, немного кривая вышла, что, кстати, подтверждает чистоту эксперимента.

Группировка и Агрегирующие функции

Эта библиотека была изначально задумана как “способная группировать и агрегировать данные при печати. Пришло время проверить на что она способна. Перед тем как добавить группировку надо подключить 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>

Компилируем… Запускаем…. Нажимаем предварительный просмотр… И… вуаля:

image

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

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

image

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

©  CNews