HOME PAGE | DOWNLOAD | TUTORIALS | XtraReports
Devexpress

Sunday, July 22, 2012

How to: Bind a Report to a Collection of Custom Objects Implementing the ITypedList Interface

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

To bind a report to a collection of custom objects, do the following.

Implement the ITypedList Interface to Data Objects

For the XtraReport to work correctly with a master-detail datasource, which consists of objects created at runtime, it's required that this datasource implements the System.ComponentModel.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 datasource, which was created by the above code.

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();
 
    label.DataBindings.Add(new XRBinding("Text", null, 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();
 
    report.ShowPreview();
}

The resulting report is shown in the image below.

Show Me

The complete sample project is available in the DevExpress Code Central database at http://www.devexpress.com/example=E137. Depending on the target platform type (ASP.NET, WinForms, etc), you can either run this example online or download an auto-executable sample.

See Also

 

No comments:

Post a Comment