1

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

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

一点背景知识,环境是 VS2010 上的 C# 和 Crystal Reports 2011。

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

所有的“表格”看起来都很好,我可以像往常一样制作和添加链接、设计和预览报告。

当谈到运行时测试时,我从这个 StackOverflow 问题的代码开始:

.NET - 将通用集合转换为 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 的数据源的多级对象。
4

1 回答 1

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;

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

于 2012-11-26T04:14:57.757 回答