Skip to main content
A newer version of this page is available. .

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

  • 8 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

For the XtraReport to work correctly with a master-detail data source, which consists of objects created at runtime, it’s required that this data source implements the ITypedList interface. The code below demonstrates how to implement the ITypedList interface for a 3-level 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 which were declared above, and fill 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-level master-detail report at runtime, add all necessary band and controls to it, and bind it to an ITypedList data source, which was created by the above code.

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();
    // Specify the label's binding depending on the data binding mode.
    if (Settings.Default.UserDesignerOptions.DataBindingMode == DataBindingMode.Bindings)
        label.DataBindings.Add(new XRBinding("Text", null, dataMember));
    else label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", dataMember));
    label.BackColor = backColor;
    label.Location = new Point(offset, 0);

    return label;
}

Get the Result

The following code creates a report, and shows its Print 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