/// <summary>
        /// Clones a visual block
        /// </summary>
        /// <param name="block">block to be cloned</param>
        /// <param name="pageNumber">current page number</param>
        /// <returns>cloned block</returns>
        /// <exception cref="InvalidProgramException">Error cloning XAML block</exception>
        private ContainerVisual CloneVisualBlock(Block block, int pageNumber)
        {
            FlowDocument tmpDoc = new FlowDocument();

            tmpDoc.ColumnWidth = double.PositiveInfinity;
            tmpDoc.PageHeight  = _report.PageHeight;
            tmpDoc.PageWidth   = _report.PageWidth;
            tmpDoc.PagePadding = new Thickness(0);

            string xaml     = XamlWriter.Save(block);
            Block  newBlock = XamlReader.Parse(xaml) as Block;

            if (newBlock == null)
            {
                throw new InvalidProgramException("Error cloning XAML block");
            }
            tmpDoc.Blocks.Add(newBlock);

            DocumentWalker walkerBlock = new DocumentWalker();
            ArrayList      blockValues = new ArrayList();

            blockValues.AddRange(walkerBlock.Walk <IInlineContextValue>(tmpDoc));

            // fill context values
            FillContextValues(blockValues, pageNumber);

            DocumentPage dp = ((IDocumentPaginatorSource)tmpDoc).DocumentPaginator.GetPage(0);

            return((ContainerVisual)dp.Visual);
        }
        /// <summary>
        /// Build cache
        /// </summary>
        private void BuildCache()
        {
            DocumentWalker walker = new DocumentWalker();

            walker.VisualVisited += WalkerVisualVisited;
            walker.Walk(_flowDocument);
        }
        private void WalkerVisualVisited(object sender, object visitedObject, bool start)
        {
            if (!(visitedObject is Image))
            {
                return;
            }

            DocumentWalker walker = sender as DocumentWalker;

            if (walker == null)
            {
                return;
            }

            List <Image> list = walker.Tag as List <Image>;

            if (list == null)
            {
                return;
            }

            list.Add((Image)visitedObject);
        }
        /// <summary>
        /// Fills document with data
        /// </summary>
        /// <exception cref="InvalidDataException">ReportTableRow must have a TableRowGroup as parent</exception>
        protected virtual void FillData()
        {
            ArrayList blockDocumentValues  = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(IInlineDocumentValue));  // walker.Walk<IInlineDocumentValue>(_flowDocument);
            ArrayList blockTableRows       = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(ITableRowForDataTable)); // walker.Walk<TableRowForDataTable>(_flowDocument);
            ArrayList blockAggregateValues = _dynamicCache.GetFlowDocumentVisualListByType(typeof(InlineAggregateValue));       // walker.Walk<InlineAggregateValue>(_flowDocument);
            ArrayList charts = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(IChart));                              // walker.Walk<IChart>(_flowDocument);
            ArrayList dynamicHeaderTableRows = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(ITableRowForDynamicHeader));
            ArrayList dynamicDataTableRows   = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(ITableRowForDynamicDataTable));
            ArrayList documentConditions     = _dynamicCache.GetFlowDocumentVisualListByInterface(typeof(IDocumentCondition));

            List <Block> blocks = new List <Block>();

            if (_blockPageHeader != null)
            {
                blocks.Add(_blockPageHeader);
            }
            if (_blockPageFooter != null)
            {
                blocks.Add(_blockPageFooter);
            }

            DocumentWalker walker = new DocumentWalker();

            blockDocumentValues.AddRange(walker.TraverseBlockCollection <IInlineDocumentValue>(blocks));

            Dictionary <string, List <object> > aggregateValues = new Dictionary <string, List <object> >();

            FillCharts(charts);

            // hide conditional text blocks
            foreach (IDocumentCondition dc in documentConditions)
            {
                if (dc == null)
                {
                    continue;
                }
                dc.PerformRenderUpdate(_data);
            }

            // fill report values
            foreach (IInlineDocumentValue dv in blockDocumentValues)
            {
                if (dv == null)
                {
                    continue;
                }
                object obj;
                if ((dv.PropertyName != null) && (_data.ReportDocumentValues.TryGetValue(dv.PropertyName, out obj)))
                {
                    dv.Value = obj;
                    RememberAggregateValue(aggregateValues, dv.AggregateGroup, obj);
                }
                else
                {
                    if ((_data.ShowUnknownValues) && (dv.Value == null))
                    {
                        dv.Value = "[" + ((dv.PropertyName != null) ? dv.PropertyName : "NULL") + "]";
                    }
                    RememberAggregateValue(aggregateValues, dv.AggregateGroup, null);
                }
            }

            // fill dynamic tables
            foreach (ITableRowForDynamicDataTable iTableRow in dynamicDataTableRows)
            {
                TableRow tableRow = iTableRow as TableRow;
                if (tableRow == null)
                {
                    continue;
                }

                TableRowGroup tableGroup = tableRow.Parent as TableRowGroup;
                if (tableGroup == null)
                {
                    continue;
                }

                TableRow currentRow;

                DataTable table = _data.GetDataTableByName(iTableRow.TableName);

                for (int i = 0; i < table.Rows.Count; i++)
                {
                    currentRow = new TableRow();

                    DataRow dataRow = table.Rows[i];
                    for (int j = 0; j < table.Columns.Count; j++)
                    {
                        string value = dataRow[j].ToString();
                        currentRow.Cells.Add(new TableCell(new Paragraph(new Run(value))));
                    }
                    tableGroup.Rows.Add(currentRow);
                }
            }

            foreach (ITableRowForDynamicHeader iTableRow in dynamicHeaderTableRows)
            {
                TableRow tableRow = iTableRow as TableRow;
                if (tableRow == null)
                {
                    continue;
                }

                DataTable table = _data.GetDataTableByName(iTableRow.TableName);

                foreach (DataRow row in table.Rows)
                {
                    string    value     = row[0].ToString();
                    TableCell tableCell = new TableCell(new Paragraph(new Run(value)));
                    tableRow.Cells.Add(tableCell);
                }
            }

            // group table row groups
            Dictionary <TableRowGroup, List <TableRow> > groupedRows = new Dictionary <TableRowGroup, List <TableRow> >();
            Dictionary <TableRowGroup, string>           tableNames  = new Dictionary <TableRowGroup, string>();

            foreach (TableRow tableRow in blockTableRows)
            {
                TableRowGroup rowGroup = tableRow.Parent as TableRowGroup;
                if (rowGroup == null)
                {
                    continue;
                }

                ITableRowForDataTable iTableRow = tableRow as ITableRowForDataTable;
                if ((iTableRow != null) && (iTableRow.TableName != null))
                {
                    string tableName;
                    if (tableNames.TryGetValue(rowGroup, out tableName))
                    {
                        if (tableName != iTableRow.TableName.Trim().ToLowerInvariant())
                        {
                            throw new ReportingException("TableRowGroup cannot be mapped to different DataTables in TableRowForDataTable");
                        }
                    }
                    else
                    {
                        tableNames[rowGroup] = iTableRow.TableName.Trim().ToLowerInvariant();
                    }
                }

                List <TableRow> rows;
                if (!groupedRows.TryGetValue(rowGroup, out rows))
                {
                    rows = new List <TableRow>();
                    groupedRows[rowGroup] = rows;
                }
                rows.Add(tableRow);
            }

            // fill tables
            foreach (KeyValuePair <TableRowGroup, List <TableRow> > groupedRow in groupedRows)
            {
                TableRowGroup rowGroup = groupedRow.Key;

                ITableRowForDataTable iTableRow = groupedRow.Value[0] as ITableRowForDataTable;
                if (iTableRow == null)
                {
                    continue;
                }

                DataTable table = _data.GetDataTableByName(iTableRow.TableName);
                if (table == null)
                {
                    if (_data.ShowUnknownValues)
                    {
                        // show unknown values
                        foreach (TableRow tableRow in groupedRow.Value)
                        {
                            foreach (TableCell cell in tableRow.Cells)
                            {
                                DocumentWalker         localWalker = new DocumentWalker();
                                List <ITableCellValue> tableCells  = localWalker.TraverseBlockCollection <ITableCellValue>(cell.Blocks);
                                foreach (ITableCellValue cv in tableCells)
                                {
                                    IPropertyValue dv = cv as IPropertyValue;
                                    if (dv == null)
                                    {
                                        continue;
                                    }
                                    dv.Value = "[" + dv.PropertyName + "]";
                                    RememberAggregateValue(aggregateValues, cv.AggregateGroup, null);
                                }
                            }
                        }
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    List <TableRow> listNewRows = new List <TableRow>();
                    TableRow        newTableRow;

                    // clone XAML rows
                    List <string> clonedRows = new List <string>();
                    foreach (TableRow row in rowGroup.Rows)
                    {
                        TableRowForDataTable reportTableRow = row as TableRowForDataTable;
                        if (reportTableRow == null)
                        {
                            clonedRows.Add(null);
                        }
                        clonedRows.Add(XamlWriter.Save(reportTableRow));
                    }

                    for (int i = 0; i < table.Rows.Count; i++)
                    {
                        DataRow dataRow = table.Rows[i];

                        for (int j = 0; j < rowGroup.Rows.Count; j++)
                        {
                            TableRow row = rowGroup.Rows[j];

                            TableRowForDataTable reportTableRow = row as TableRowForDataTable;
                            if (reportTableRow == null)
                            {
                                // clone regular row
                                listNewRows.Add(XamlHelper.CloneTableRow(row));
                            }
                            else
                            {
                                // clone ReportTableRows
                                newTableRow = (TableRow)XamlHelper.LoadXamlFromString(clonedRows[j]);

                                foreach (TableCell cell in newTableRow.Cells)
                                {
                                    DocumentWalker         localWalker = new DocumentWalker();
                                    List <ITableCellValue> newCells    = localWalker.TraverseBlockCollection <ITableCellValue>(cell.Blocks);
                                    foreach (ITableCellValue cv in newCells)
                                    {
                                        IPropertyValue dv = cv as IPropertyValue;
                                        if (dv == null)
                                        {
                                            continue;
                                        }
                                        try
                                        {
                                            object obj = dataRow[dv.PropertyName];
                                            if (obj == DBNull.Value)
                                            {
                                                obj = null;
                                            }
                                            dv.Value = obj;

                                            RememberAggregateValue(aggregateValues, cv.AggregateGroup, obj);
                                        }
                                        catch
                                        {
                                            if (_data.ShowUnknownValues)
                                            {
                                                dv.Value = "[" + dv.PropertyName + "]";
                                            }
                                            else
                                            {
                                                dv.Value = "";
                                            }
                                            RememberAggregateValue(aggregateValues, cv.AggregateGroup, null);
                                        }
                                    }
                                }
                                listNewRows.Add(newTableRow);

                                // fire event
                                _report.FireEventDataRowBoundEventArgs(new DataRowBoundEventArgs(_report, dataRow)
                                {
                                    TableName = dataRow.Table.TableName, TableRow = newTableRow
                                });
                            }
                        }
                    }
                    rowGroup.Rows.Clear();
                    foreach (TableRow row in listNewRows)
                    {
                        rowGroup.Rows.Add(row);
                    }
                }
            }

            // fill aggregate values
            foreach (InlineAggregateValue av in blockAggregateValues)
            {
                if (String.IsNullOrEmpty(av.AggregateGroup))
                {
                    continue;
                }

                string[] aggregateGroups = av.AggregateGroup.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (var group in aggregateGroups)
                {
                    if (!aggregateValues.ContainsKey(group))
                    {
                        av.Text = av.EmptyValue;
                        break;
                    }
                }
                av.Text = av.ComputeAndFormat(aggregateValues);
            }
        }
        /// <summary>
        /// Creates a flow document of the report data
        /// </summary>
        /// <returns></returns>
        /// <exception cref="ArgumentException">XAML data does not represent a FlowDocument</exception>
        /// <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>
        internal FlowDocument CreateFlowDocument()
        {
            MemoryStream mem = new MemoryStream();

            byte[] buf = Encoding.UTF8.GetBytes(_xamlData);
            mem.Write(buf, 0, buf.Length);
            mem.Position = 0;
            FlowDocument res = XamlReader.Load(mem) as FlowDocument;

            if (res == null)
            {
                throw new ArgumentException("XAML data does not represent a FlowDocument");
            }

            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);
            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;
                }

                // 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);
                }
            }

            if (headers.Count > 0)
            {
                PageHeaderHeight = headers[0].PageHeaderHeight;
            }
            if (footers.Count > 0)
            {
                PageFooterHeight = footers[0].PageFooterHeight;
            }

            // 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 += WalkerVisualVisited;
            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);
        }