Once upon a time in a land far far away there were three little report slots: InlineReportSlot, FileReportSlot and UrlReportSlot, who decided to build reports on their own. They realized that they need to store layout details somewhere, but each decided for himself.
The laziest of the slots, InlineReportSlot, decided to incorporate report layout details directly in .csproj file. “It will take one milisecond”, he said, and having done that, spent the rest of the day jumping and playing. But then a Big Boss came along and asked to share report layout across several reports, and our little report slot couldn’t do that.
FileReportSlot decided to store report layout details in a separate file. “The file can live on a file share and can be easily copied if needed”, he said. But then a Big Boss came along and asked to share report layout across different domains, and our little FileReportSlot couldn’t accomplish that.
The wisest of the three slots, UrlReportSlot, decided to locate report layout behind the URL, so he painstakingly set up a web service that would return an .rst file if a certain URL is accessed. It was not possible to update such a report from the Report Designer, but this seemed logical. The Big Boss came along, and happily generated all his reports.
How (and why) create a custom report slot.
However, it’s not the full story, as you might guess! As though three kinds of slots was not enough, it’s also possible to create custom (!) report slots, and below I will show an example. There may be many reasons why you might want to customize a slot, and here’s my take:
Imagine you have 1.000 corporate reports with company address in the footer. If the address would change, you’d need to go through all the reports and manually update them. Uh oh. Storing common data in one place would eliminate the issue, as will it shrink all your report files a bit
One way to solve the problem is to use custom report slots. Such slot will get “unique” content of a report, append the common footer and return the full content to the client.
So let’s go ahead and create one!
First off I create a FileReportSlot and build a report on top of it, including the common footer. Then I look info .rst content. Unfortunately these files are one-liners, while I prefer formatted xml, so I go to xmlindent.com, paste file content (single line), and get some nicely indented xml, which lets me easy identify the footer element.
Now goes the code. I subclass my custom report slot from FileReportSlot, to reuse its load/save capabilities:
public class CustomFileReportSlot : FileReportSlot { public CustomFileReportSlot(IContainer container) : base(container) { } public override Document LoadReport() { var report = base.LoadReport(); report.AddFooterTo("dataBand1"); return report; } public override void SaveReport(Document document) { document.RemoveFooterFrom("dataBand1"); base.SaveReport(document); } }
Obviously there are no such methods as AddFooterTo and RemoveFooterFrom – those are my extensions, provided below:
internal static void AddFooterTo(this Document report, string dataBandName) { var footer = CreateFooter(); foreach (Page page in report.Pages) { var dataBand = page.ControlByName(dataBandName) as DataBand; if (dataBand != null) dataBand.Controls.Add(footer); } } private static Footer CreateFooter() { object footerObj = null; using (var reader = new XmlTextReader(new StringReader(_footerXml))) { var serializer = XSerializationManager.GetSerializer(typeof(Footer)); serializer.Deserialize(ref footerObj, reader, typeof(Footer), new XSerializationManager()); } return footerObj as Footer; } ...
And the only thing left is to define _footerXml which I won’t do because you probably want to download the full solution anyway. And, just for reference, below goes the final result with the footer being autogenerated:
Happy customizing!