Разделы

ПО Софт

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

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

Дмитрий Шулинин, UserGate: Выиграли те, кто полагался на SIEM собственной разработки
Безопасность

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

image

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