/// <summary> /// Constructor /// </summary> /// <param name="report">report document</param> /// <param name="data">multiple report data</param> /// <exception cref="ArgumentException">Need at least two ReportData objects</exception> public MultipleReportPaginator(ReportDocument report, IEnumerable<ReportData> data) { using (var counter = new TimeCounter("\tMultipleReportPaginator Total: {0}", true, true)) { if (data == null) throw new ArgumentException("Need at least two ReportData objects"); // create a list of report paginators and compute page counts _pageCount = 0; foreach (ReportData rd in data) { if (rd == null) continue; ReportPaginator paginator = new ReportPaginator(report, rd); _reportPaginators.Add(paginator); paginator.PageShift = _pageCount; paginator.TotalPageCount = _pageCount; counter.ShowTick("\tMultipleReportPaginator Paginator: {0}"); DocumentPage dp = paginator.GetPage(0); if ((dp != DocumentPage.Missing) && (dp.Size != Size.Empty)) _pageSize = paginator.PageSize; _pageCount += paginator.PageCount; counter.ShowTick("\tMultipleReportPaginator PageCount: {0}"); } counter.ShowTick("\tMultipleReportPaginator Paginators: {0}"); foreach (ReportPaginator paginator in _reportPaginators) paginator.TotalPageCount = _pageCount; if (_reportPaginators.Count <= 0) throw new ArgumentException("Need at least two ReportData objects"); counter.ShowTick("\tMultipleReportPaginator PageCount: {0}"); } }
/// <summary> /// Helper method to create page header or footer from flow document template /// </summary> /// <param name="data">enumerable report data</param> /// <returns></returns> /// <exception cref="ArgumentNullException">data</exception> public XpsDocument CreateXpsDocument(IEnumerable<ReportData> data) { using (var counter = new TimeCounter("CreateXpsDocument Total: {0}", true, true)) { if (data == null) throw new ArgumentNullException("data"); int count = 0; ReportData firstData = null; foreach (ReportData rd in data) { if (firstData == null) firstData = rd; count++; } if (count == 1) return CreateXpsDocument(firstData); // we have only one ReportData object -> use the normal ReportPaginator instead MemoryStream ms = new MemoryStream(); Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); string pack = "pack://report.xps"; PackageStore.RemovePackage(new Uri(pack)); PackageStore.AddPackage(new Uri(pack), pkg); XpsDocument doc = new XpsDocument(pkg, CompressionOption.NotCompressed, pack); XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(doc), false); //DocumentPaginator paginator = ((IDocumentPaginatorSource)CreateFlowDocument()).DocumentPaginator; counter.ShowTick("CreateXpsDocument Prepare: {0}"); MultipleReportPaginator rp = new MultipleReportPaginator(this, data); counter.ShowTick("CreateXpsDocument Paginator: {0}"); rsm.SaveAsXaml(rp); counter.ShowTick("CreateXpsDocument SaveAsXaml: {0}"); return doc; } }
/// <summary> /// Creates a flow document of the report data /// </summary> /// <returns></returns> /// <exception cref="ArgumentException">Flow document must have a specified page height</exception> /// <exception cref="ArgumentException">Flow document must have a specified page width</exception> /// <exception cref="ArgumentException">"Flow document must have only one ReportProperties section, but it has {0}"</exception> public FlowDocument CreateFlowDocument(out Hint hints) { using (var counter = new TimeCounter("\t\t\tReportDocument Total {0}")) { FlowDocument res = GetFlowDocument(); counter.ShowTick("\t\t\t\tReportDocument Load XAML: {0}"); if (res.PageHeight == double.NaN) throw new ArgumentException("Flow document must have a specified page height"); if (res.PageWidth == double.NaN) throw new ArgumentException("Flow document must have a specified page width"); // remember original values _pageHeight = res.PageHeight; _pageWidth = res.PageWidth; // search report properties DocumentWalker walker = new DocumentWalker(); List<SectionReportHeader> headers = walker.Walk<SectionReportHeader>(res); List<SectionReportFooter> footers = walker.Walk<SectionReportFooter>(res); var hintList = walker.Walk<ReportHint>(res); hints = Hint.None; foreach (var hint in hintList) { hints |= hint.Hint; // remove properties section from FlowDocument DependencyObject parent = hint.Parent; if (parent is FlowDocument) { ((FlowDocument)parent).Blocks.Remove(hint); parent = null; } if (parent is Section) { ((Section)parent).Blocks.Remove(hint); parent = null; } } List<ReportProperties> properties = walker.Walk<ReportProperties>(res); if (properties.Count > 0) { if (properties.Count > 1) throw new ArgumentException(String.Format("Flow document must have only one ReportProperties section, but it has {0}", properties.Count)); ReportProperties prop = properties[0]; if (prop.ReportName != null) ReportName = prop.ReportName; if (prop.ReportTitle != null) ReportTitle = prop.ReportTitle; if (headers.Count > 0) PageHeaderHeight = headers[0].PageHeaderHeight; if (footers.Count > 0) PageFooterHeight = footers[0].PageFooterHeight; // remove properties section from FlowDocument DependencyObject parent = prop.Parent; if (parent is FlowDocument) { ((FlowDocument)parent).Blocks.Remove(prop); parent = null; } if (parent is Section) { ((Section)parent).Blocks.Remove(prop); parent = null; } } // make height smaller to have enough space for page header and page footer res.PageHeight = _pageHeight - _pageHeight * (PageHeaderHeight + PageFooterHeight) / 100d; // search image objects List<Image> images = new List<Image>(); walker.Tag = images; walker.VisualVisited += new DocumentVisitedEventHandler(walker_VisualVisited); walker.Walk(res); // load all images foreach (Image image in images) { if (ImageProcessing != null) ImageProcessing(this, new ImageEventArgs(this, image)); try { if (image.Tag is string) image.Source = new BitmapImage(new Uri("file:///" + Path.Combine(_xamlImagePath, image.Tag.ToString()))); } catch (Exception ex) { // fire event on exception and check for Handled = true after each invoke if (ImageError != null) { bool handled = false; lock (ImageError) { ImageErrorEventArgs eventArgs = new ImageErrorEventArgs(ex, this, image); foreach (var ed in ImageError.GetInvocationList()) { ed.DynamicInvoke(this, eventArgs); if (eventArgs.Handled) { handled = true; break; } } } if (!handled) throw; } else throw; } if (ImageProcessed != null) ImageProcessed(this, new ImageEventArgs(this, image)); // TODO: find a better way to specify file names } return res; } }