基于XML的Crystal Report在刷新时不更新子对象

时间:2012-11-22 01:10:59

标签: c# xml crystal-reports datatable

我正在开发一个应用程序,它需要几种类型的Crystal Reports,“标准”的Crystal Reports,数据库表,更新连接字符串并刷新报告,完成。另一种类型的Crystal Report基于常规(稍微复杂)的类对象或POCO对象。

我面临的问题是报告的数据源基于我们创建的类,它的属性也是我们创建的类。刷新报表时,对象中的数据会更新,但子属性对象中的数据不会更新。它们仍然是我创建报告时设置的值。

有一些背景知识,环境是VS2010上带有Crystal Reports 2011的C#。

为了创建报告,我创建了一个对象并使用相关数据(包括子对象)填充每个属性,然后将对象导出到XML并将其输出到文件中。然后,我创建了一个新报告,并添加了“ADO.NET(XML)”类型的数据源。

所有“表格”都显示正常,我可以像往常一样制作和添加链接,设计和预览报告。

在进行运行时测试时,我从这个StackOverflow问题的代码开始:

.NET - Convert Generic Collection to DataTable

为了将我的对象列表转换为DataTable并将其指定为报表的数据源。如前所述,它适用于第一级,但不适用于子属性。

当子属性是我们创建的类之一而不仅仅是CLR数据类型时,我已修改该代码以创建新的DataTable,但现在只留下一个空报告。该代码如下:

public static class CollectionExtensions
{
    /// <summary>Converts to.</summary>
    /// <typeparam name="T">The type value</typeparam>
    /// <param name="list">The list value.</param>
    /// <returns>The data table.</returns>
    public static DataTable ConvertTo<T>(this IList<T> list)
    {
        var entityType = typeof(T);
        var table = CreateTable(entityType);

        foreach (var item in list)
        {
            var row = ConvertToRow(table, item, entityType);
            table.Rows.Add(row);
        }

        return table;
    }

    /// <summary>
    /// Converts to table.
    /// </summary>
    /// <param name="table">The table.</param>
    /// <param name="item">The item value.</param>
    /// <param name="type">The type value.</param>
    /// <returns>returns a data table</returns>
    public static DataRow ConvertToRow(DataTable table, object item, Type type)
    {
        var properties = TypeDescriptor.GetProperties(type);

        var row = table.NewRow();

        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.PropertyType.IsAssignableFrom(typeof(AbstractEntity)))
            {
                var subTable = CreateTable(prop.PropertyType);

                if (prop.GetValue(item) != null)
                {
                    var subRow = ConvertToRow(subTable, prop.GetValue(item), prop.PropertyType);
                    subTable.Rows.Add(subRow);
                }

                row[prop.Name] = subTable;
            }
            else
            {
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            }
        }

        return row;
    }

    /// <summary> Creates the table. </summary>
    /// <param name="type">The type value.</param>
    /// <returns>The datatable</returns>
    public static DataTable CreateTable(Type type)
    {
        var table = new DataTable(type.Name);
        var properties = TypeDescriptor.GetProperties(type);

        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.PropertyType.IsAssignableFrom(typeof(AbstractEntity)))
            {
                table.Columns.Add(prop.Name, typeof(DataTable));
            }
            else
            {
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
        }

        return table;
    }
}

我认为问题在于,我在创建报告的方式与数据源更新时数据在运行时的应用方式之间存在脱节。

但之前从未做过基于XML的Crystal Report,我不确定如何解决这个问题。我对专家的问题是:

  1. 我是否在正确的轨道上,首先创建报告?
  2. 鉴于我是如何创建报告的,我是否正在更新报告?
  3. 有没有更好的方法来实现同样的结果?对于多级对象是基于XML的Crystal Report的数据源。

1 个答案:

答案 0 :(得分:1)

解决方案是将对象序列化为MemoryStream并将其反序列化为DataSet,Crystal Report能够更新所有表(我到目前为止已经测试过)。

我在测试工具应用程序中使用了以下代码,这解决了这个问题:

        var xmlDocument = new XmlDocument();
        var serializer = new XmlSerializer(typeof(MyObjectClass));
        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, myObjectInstance);
            stream.Flush();
            stream.Seek(0, SeekOrigin.Begin);
            xmlDocument.Load(stream);
        }

        var data = new DataSet();
        var context = new XmlParserContext(null, new XmlNamespaceManager(new NameTable()), null, XmlSpace.None);
        var reader = new XmlTextReader(xmlDocument.OuterXml, XmlNodeType.Document, context);
        data.ReadXml(reader);

        var report = new ReportDocument();
        report.Load(@"C:\Reports\TestReport.rpt", OpenReportMethod.OpenReportByTempCopy);

        report.SetDataSource(data);

        this.crystalReportsViewer.ViewerCore.ReportSource = report;

我希望它可以帮助其他人。