Bind a Report to a Collection that Implements the ITypedList Interface (Runtime Sample)

  • 7 minutes to read

This tutorial demonstrates how to create a hierarchical master-detail data source at runtime and bind a report to it.

Implement the ITypedList Interface to Data Objects

If a report's data source consists of objects created at runtime, it should implement the ITypedList interface. The code below demonstrates how to implement the ITypedList interface for a 3-tier Supplier-Product-OrderDetail hierarchical data source.

using System;
using System.Collections;
using System.ComponentModel;
// ...

public class SupplierCollection : ArrayList, ITypedList {
    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) {
        if (listAccessors != null && listAccessors.Length > 0) {
            PropertyDescriptor listAccessor = listAccessors[listAccessors.Length - 1];
            if (listAccessor.PropertyType.Equals(typeof(ProductCollection)))
                return TypeDescriptor.GetProperties(typeof(Product));
            else if (listAccessor.PropertyType.Equals(typeof(OrderDetailCollection)))
                return TypeDescriptor.GetProperties(typeof(OrderDetail));
        }
        return TypeDescriptor.GetProperties(typeof(Supplier));
    }
    string ITypedList.GetListName(PropertyDescriptor[] listAccessors) {
        return "Suppliers";
    }
}

public class Supplier {
    static int nextID = 0;
    int id;
    string name;
    ProductCollection products = new ProductCollection();

    public ProductCollection Products { get { return products; } }
    public int SupplierID { get { return id; } }
    public string CompanyName { get { return name; } }

    public Supplier(string name) {
        this.name = name;

        this.id = nextID;
        nextID++;
    }
    public void Add(Product product) {
        products.Add(product);
    }
}

public class ProductCollection : ArrayList, ITypedList {
    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) {
        return TypeDescriptor.GetProperties(typeof(Product));
    }
    string ITypedList.GetListName(PropertyDescriptor[] listAccessors) {
        return "Products";
    }
}

public class Product {
    static int nextID = 0;

    OrderDetailCollection orderDetails = new OrderDetailCollection();
    int suppID;
    int prodID;
    string name;

    public int SupplierID { get { return suppID; } }
    public int ProductID { get { return prodID; } }
    public string ProductName { get { return name; } }
    public OrderDetailCollection OrderDetails { get { return orderDetails; } }

    public Product(int suppID, string name) {
        this.suppID = suppID;
        this.name = name;

        this.prodID = nextID;
        nextID++;
    }
}

public class OrderDetailCollection : ArrayList, ITypedList {
    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) {
        return TypeDescriptor.GetProperties(typeof(OrderDetail));
    }
    string ITypedList.GetListName(PropertyDescriptor[] listAccessors) {
        return "OrderDetails";
    }
}

public class OrderDetail {
    int prodID;
    short quantity;
    public int ProductID { get { return prodID; } }
    public short Quantity { get { return quantity; } }

    public OrderDetail(int prodID, int quantity) {
        this.prodID = prodID;
        this.quantity = Convert.ToInt16(quantity);
    }
}

Create Data

The following code demonstrates how to create the data objects declared in the previous section and populate them with data:

private SupplierCollection CreateData() {
    SupplierCollection suppliers = new SupplierCollection();

    Supplier supplier = new Supplier("Exotic Liquids");
    suppliers.Add(supplier);
    supplier.Add(CreateProduct(supplier.SupplierID, "Chai"));
    supplier.Add(CreateProduct(supplier.SupplierID, "Chang"));
    supplier.Add(CreateProduct(supplier.SupplierID, "Aniseed Syrup"));

    supplier = new Supplier("New Orleans Cajun Delights");
    suppliers.Add(supplier);
    supplier.Add(CreateProduct(supplier.SupplierID, "Chef Anton's Cajun Seasoning"));
    supplier.Add(CreateProduct(supplier.SupplierID, "Chef Anton's Gumbo Mix"));

    supplier = new Supplier("Grandma Kelly's Homestead");
    suppliers.Add(supplier);
    supplier.Add(CreateProduct(supplier.SupplierID, "Grandma's Boysenberry Spread"));
    supplier.Add(CreateProduct(supplier.SupplierID, "Uncle Bob's Organic Dried Pears"));
    supplier.Add(CreateProduct(supplier.SupplierID, "Northwoods Cranberry Sauce"));

    return suppliers;
}

static Random random = new Random(5);

private Product CreateProduct(int supplierID, string productName) {
    Product product = new Product(supplierID, productName);

    product.OrderDetails.AddRange(new OrderDetail[] { 
        new OrderDetail(product.ProductID, random.Next(0, 100)), 
        new OrderDetail(product.ProductID, random.Next(0, 100)),
        new OrderDetail(product.ProductID, random.Next(0, 100)) });

    return product;
}

Create a Report

The following code demonstrates how to create a 3-tier master-detail report at runtime, add bands and controls to it, and bind the report to an ITypedList data source that was created in the previous sections.

using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.Configuration;
// ...

private XtraReport CreateReport() {
    XtraReport report = new XtraReport();

    DetailBand detail = new DetailBand();
    detail.Height = 30;
    report.Bands.Add(detail);

    DetailReportBand detailReport1 = new DetailReportBand();
    report.Bands.Add(detailReport1);

    DetailBand detail1 = new DetailBand();
    detail1.Height = 30;
    detailReport1.Bands.Add(detail1);

    DetailReportBand detailReport2 = new DetailReportBand();
    detailReport1.Bands.Add(detailReport2);

    DetailBand detail2 = new DetailBand();
    detail2.Height = 30;
    detailReport2.Bands.Add(detail2);

    report.DataSource = CreateData();
    detailReport1.DataMember = "Products";
    detailReport2.DataMember = "Products.OrderDetails";

    detail.Controls.Add(CreateBoundLabel("CompanyName", Color.Gold, 0));
    detail1.Controls.Add(CreateBoundLabel("Products.ProductName", Color.Aqua, 100));
    detail2.Controls.Add(CreateBoundLabel("Products.OrderDetails.Quantity", Color.Pink, 200));

    return report;
}

private XRLabel CreateBoundLabel(string dataMember, Color backColor, int offset) {
    XRLabel label = new XRLabel();
    // Bind the label to data.
    label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", dataMember));
    label.BackColor = backColor;
    label.Location = new Point(offset, 0);

    return label;
}

View the Result

The following code creates a report and shows its preview:

private void button1_Click(object sender, EventArgs e) {
    XtraReport report = CreateReport();

    // Show the report's print preview.
    ReportPrintTool printTool = new ReportPrintTool(report);
    printTool.ShowPreview();
}

The resulting report is shown in the image below.

HowTo_ITypedList1

See Also