Reports for Silverlight или построитель отчетов c шаблонами и с группировкой
WPF, MVVM, Silverlight | создано: 28.07.2011 | опубликовано: 28.07.2011 | обновлено: 13.01.2024 | просмотров: 7824 | всего комментариев: 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, спасибо за ответ. :)