Articles about Master Report arrive in a jostling crowd, but before we take the topic off the table let me show you just one more useful trick.
All the examples on Master Reports, available online, explicitly specify MasterReport property in the child templates:
As useful as it might be for a “child-only” template (which always gets rendered within some specific master report), it should not stampede us into assuming that it is the best solution
On one hand, specifying a master template means you can no longer easily preview the report from Report Designer: a message box “Master Report is null” pops up and so you have to remove the master name, hit Preview again, and add the master name back when you’re done. On the other hand, you may simply want a report to allow both independent rendering as well as rendering as a part of several master reports – and all of that without having to change its .rst template, please!
Possible with SharpShooter? You bet.
Any template inside any master report.
So we want to take an arbitrary report template (with a blank MasterReport property), and inject it into an arbitrary master report.
For that it’s good to reuse the console application we created previously: it takes paths of a child and master template files, and path for .pdf output where it would save the final rendering:
Obviously it won’t work if the “potential child” template has a blank MasterReport. But we know that both report name and its master name are just attributes in the template content (which is, well, XML). So we can parse the provided .rst files and supply the correct names at runtime:
var xMaster = XDocument.Load(masterPath); var masterName = xMaster.GetRootAttribute("Name"); var xChild = XDocument.Load(childPath); var childMasterName = xChild.GetRootAttribute("MasterReport"); childMasterName.Value = masterName.Value;
And GetRootAttribute is a simple extension on XDocument to make code cleaner:
internal static XAttribute GetRootAttribute(this XDocument document, string attributeName) { return document .Descendants("root") .Single() .Attribute(attributeName) ?? new XAttribute(attributeName, ""); }
But how to apply the parsed XML into ReportSlot objects? Previously we used perhaps a slightly unintuitive code with XSerializationManager but, fortunately, that’s necessary only if working with the base ReportSlot class. If we’d agree to use InlineReportSlots, we can just assign the parsed XML straight into the DocumentStream property:
masterSlot.DocumentStream = xMaster.ToString(); childSlot.DocumentStream = xChild.ToString();
After we’ve done the above, nothing else should be changed in the console app. Neato – just a few lines of code and lo and behold, a lot more interesting use cases are available to us. If you feel like giving this one a try – make sure to flick through the code here at BitBucket or download a .zip archive and play with it!
Summary.
Master Report is a great solution for displaying reports within other reports, but requirement to always specify MasterReport property in the child template may restrict your possible use cases.
In this post, we’ve come up with a solution – tweaking XML of a report template on the fly – which allows to have a report template with a blank MasterReport property (effectively, a template that was never expected to be a subreport) and render it as a subreport within an arbitrary master report.