Reports for Silverlight или построитель отчетов c шаблонами и с группировкой
WPF, MVVM, Silverlight | создано: 28.07.2011 | опубликовано: 28.07.2011 | обновлено: 13.01.2024 | просмотров: 8333 | всего комментариев: 8
Эта статья является всего лишь демонстрацией работы с отчетами в Silverlight. В статье показано как строить отчет с использованием шаблонов и как включить группировку данных, а также как задавать результаты группировки (aggregations).
Установка Reports
Библиотека Calabonga.Silverlight.Reports специально упакована в nuget-пакет чтобы можно было с легкостью устанавливать/обновлять/удалять эту библиотеку. Давайте предположим, что Nuget Manager у Вас уже установлен, и тогда можно сразу перейти к установке пакет отчетов. Я создал новый Silverlight-проект чтобы продемонстрировать работу библиотеки. Нажимаем на проекте правую кнопку мыши и запускаем менеджер Nuget:

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

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

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

Мой шаблон будет такой:
<clb:Report.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Name]}"
Grid.Column="0" />
</Border>
<Border Grid.Column="1"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Gender]}"
Grid.Column="1" />
</Border>
<Border Grid.Column="2"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[IsMember]}"
Grid.Column="2" />
</Border>
<Border Grid.Column="3"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Age]}"
Grid.Column="3" />
</Border>
<Border Grid.Column="4"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Description]}"
Grid.Column="4" />
</Border>
<Border Grid.Column="5"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Weight]}"
Grid.Column="5" />
</Border>
<Border Grid.Column="6"
Style="{StaticResource border}">
<TextBlock Text="{Binding Path=[Country]}"
Grid.Column="6" />
</Border>
</Grid>
</DataTemplate>
</clb:Report.ItemTemplate>
Обратите внимание на то, как осуществляется привязка – в квадратных скобках. Просто мне кажется “так красивее” :)
И добавим еще и стили, которые использованы в шаблоне для отображения.
<UserControl.Resources>
<Style TargetType="TextBlock"
x:Key="header">
<Setter Property="FontSize"
Value="11" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="FontWeight"
Value="Bold" />
</Style>
<Style TargetType="Border"
x:Key="border">
<Setter Property="BorderBrush"
Value="Gray" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Margin"
Value="1" />
<Setter Property="Padding"
Value="3" />
</Style>
</UserControl.Resources>
Напишем код обработки нажатия кнопок, я приведу весь код, чтобы больше к нему не возвращаться, потому что он не измениться:
public partial class MainPage : UserControl
{
private List<Person> printData;
private Report1 report1;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
report1 = new Report1();
printData = People.GetPeople();
report1.CalaReport.ItemsSource = printData;
}
private void print_Click(object sender, RoutedEventArgs e)
{
report1.CalaReport.Print();
}
private void preview_Click(object sender, RoutedEventArgs e)
{
previewBox.Content = report1.CalaReport.GetPreview(previewBox);
}
}
Ну а теперь, давайте попробуем напечатать. Ура! Что-то видно на превью, да и на принтер что-то улетело… зажужжал гадина:

Надо бы HeaderTemplate и FooterTemplate добавить – для красоты и эстетики:
<clb:Report.PageHeaderTemplate>
<DataTemplate>
<Grid MinHeight="40"
Background="Wheat">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Style="{StaticResource border}">
<TextBlock Text="Имя"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="1"
Style="{StaticResource border}">
<TextBlock Text="Пол"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="2"
Style="{StaticResource border}">
<TextBlock Text="Участник"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="3"
Style="{StaticResource border}">
<TextBlock Text="Возраст"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="4"
Style="{StaticResource border}">
<TextBlock Text="Описание"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="5"
Style="{StaticResource border}">
<TextBlock Text="Вес"
Style="{StaticResource header}" />
</Border>
<Border Grid.Column="6"
Style="{StaticResource border}">
<TextBlock Text="Страна"
Style="{StaticResource header}" />
</Border>
</Grid>
</DataTemplate>
</clb:Report.PageHeaderTemplate>
и нижняя часть:
<clb:Report.ReportFooterTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch"
MinHeight="30"
Background="LightGray">
<TextBlock Text="Этот отчет сгенерирован при помощи
библиотеки Calabonga.Silverlight.Reports." />
</Grid>
</DataTemplate>
</clb:Report.ReportFooterTemplate>
Нажимаем печать… И!…

Страница уже выглядит по другому. Про Вас не обращать внимание на шапку, немного кривая вышла, что, кстати, подтверждает чистоту эксперимента.
Группировка и Агрегирующие функции
Эта библиотека была изначально задумана как “способная группировать и агрегировать данные при печати. Пришло время проверить на что она способна. Перед тем как добавить группировку надо подключить mscorlib.dll:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Добавим группировку по стране (Country):
<clb:Report.Groups>
<clb:GroupDefinitions>
<clb:GroupDefinitions.GroupedFields>
<sys:String>Country</sys:String>
</clb:GroupDefinitions.GroupedFields>
</clb:GroupDefinitions>
</clb:Report.Groups>
Определение поля по которому группировать происходит в четвертой строке. Можно добавить сколь угодно параметров группировки (конечно же в разумных пределах). А добавлю-ка я сразу еще и по половому признаку группировку поместив строку:
<sys:String>Gender</sys:String>
Между 4 и 5 строками. Теперь раз добавлена группировка следует добавить и шаблоны для группировки (Header и Footer), иначе прилетит ошибка. Шаблон группировки выглядит так:
<clb:Report.GroupHeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="1"
Background="Silver"
MinHeight="40">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=[Country]}"
Margin="30,0,20,0"
Style="{StaticResource header}" />
<TextBlock Text="{Binding Path=[Gender]}"
Margin="30,0,20,0"
Style="{StaticResource header}" />
<TextBlock Text="{Binding Path=[Weight]}"
Margin="30,0,20,0"
Style="{StaticResource header}" />
<TextBlock Text="{Binding Path=[Age]}"
Margin="30,0,20,0"
Style="{StaticResource header}" />
</StackPanel>
</Border>
</DataTemplate>
</clb:Report.GroupHeaderTemplate>
<clb:Report.GroupFooterTemplate>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="1"
Background="Gainsboro"
MinHeight="30">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Итого вес:"
Margin="0,0,20,0" />
<TextBlock Text="{Binding Path=[Weight]}"
Style="{StaticResource header}"
Margin="0,0,60,0" />
<TextBlock Text="Возраст средний:"
Margin="0,0,20,0" />
<TextBlock Text="{Binding Path=[Age]}"
Style="{StaticResource header}" />
</StackPanel>
</Border>
</DataTemplate>
</clb:Report.GroupFooterTemplate>
И, как Вы уже наверное заметили, в шаблонах используются агрегирующие данные. А чтобы они появились следует добавить информацию об агрегации групп:
<clb:GroupDefinitions.Aggregations>
<clb:AggregateFieldDefinition Field="Weight">
<clb:AggregateFieldDefinition.AggregateFunctions>
<clb:AggregateFunctionDefinition Function="Sum" />
</clb:AggregateFieldDefinition.AggregateFunctions>
</clb:AggregateFieldDefinition>
<clb:AggregateFieldDefinition Field="Age">
<clb:AggregateFieldDefinition.AggregateFunctions>
<clb:AggregateFunctionDefinition Function="Average" />
</clb:AggregateFieldDefinition.AggregateFunctions>
</clb:AggregateFieldDefinition>
</clb:GroupDefinitions.Aggregations>
Компилируем… Запускаем…. Нажимаем предварительный просмотр… И… вуаля:

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

Надеюсь, что контрол будет востребован. На этом всё. Пишите комментарии.
Обновление Версия 1.1 от 18.08.2011: Добавлено ReportHeaderTemplate (Отображется один раз на первой странце отчета).
Обновление Версия 1.2 от 23.08.2011: Теперь если вы хотите отобразить номер строки, то для этого достаточно просто указать в щаблоне ItemTemplate текстовое поле с привязкой к данным по свойству [Index], например:
<TextBlock Text="{Binding Path=[Index]}" />
Это поле теперь добавляет обязательно и значит и простом и в группированном отчете можно указывать текущий записи.
Обновление Версия 1.3 29.09.2011: Добавлены свойства к отчету, в которых можно хранить данные, например, для заголовков и для оснований отчета. Фамилия должностного лица, название компании, место для подписи с инициалами.
В коде:
view3.Report.Properties.Add("buhgalter","Страшная Татьяна Леонидовна");
В шаблоне:
<clb:Report.ReportHeaderTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Magenta">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Главный бухгалтер:"
FontSize="34"
Grid.Row="0" />
<TextBlock Text="{Binding Path=Properties[buhgalter]}"
FontSize="23"
Grid.Row="1" />
</Grid>
</DataTemplate>
</clb:Report.ReportHeaderTemplate>
Ссылки по теме
Комментарии к статье (8)
Hi, i trying your examples but it has some errors in code. Can you please send me a simple example.
Tnx
Hi, Ervin, I uploaded demo project which showed in article. You can download it and if you have questions I'll answer with pleasure.
Здравствуйте! У меня вопрос по библиотеке Calabonga.Silverlight.Reports.
привязка данных к шаблону <TextBlock Text="{Binding Path=[Age]}" Grid.Column="3" /> в квадратных скобках [Age] - нужна не только для красоты она просто не работает без квадратных скобок! Также при добавлении формата Text="{Binding Path=[Weight], StringFormat='0:N2'}" - форматирование не происходит! Как-то можно ето исправить ?
Для os...
Вот не готов я ответить на ваш вопрос. Первое что приходит в голову, так это "лишние одиночные кавычки"... В крайнем случае, прошу обратиться к MSDN
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.stringformat(v=vs.110).aspx
или
в этой статье
Уважаемый Calabonga, вопрос по печати отчета. Если ли возможность допечатывать не умещающиеся столбцы (минимальная ширина столбца обязательна)? Хотелось бы выводить на печать как можно больше информации :) Буду рад любой подсказке, в какую сторону копать.
К сожалению, не очень понимаю о чем идет речь. Что значит "допечатывать"?
Calabonga, ситуация такова, есть отчет в десять столбцов фиксированной ширины MinWidth="145" (уже делать не вижу смысла). При попытке печати данного отчета не умещающиеся столбцы "обрезаются", как-нибудь можно распечатать не уместившиеся столбцы?
Calabonga, спасибо за ответ. :)