Using Business Objects

Attachment

Download the sample application: UserDataSource.zip

In order to make custom data source represent data, it should implement System.Collections.IEnumerable or System.ComponentModel interface (IEnumerable interface is implemented by many standard classes, for example, System.Array, System.Collections.ArrayList, System.Collections. CollectionBase and many others). IListSource interface defines two members: bool ContainsListCollection {get;} property, it doesn’t influence work of SharpShooter Reports; and GetList() method getting reference to System.Collections.IList interface that in its turn implements IEnumerable interface.

In any case, work with custom data is executed via the IEnumerable interface that defines a single GetEnumerator() method getting reference to the System.Collections.IEnumerator interface. This interface enables you to logically view all elements of the collection. IEnumerator interface defines object Current {get;} property representing current object in the collection and two methods: bool MoveNext() and void Reset(). MoveNext() method executes move to the next collection element and gets true if successful, and false, if the current element is last in the collection. Reset() method moves numerator to initial state, i.e. current position is a position before the first collection element.

All properties will be available as data fields of the objects stored in custom data arrays. In its turn, if the property has a type implementing IListSource or IEnumerable interface, it is considered as custom data array. It allows the creation of complicated reports with master-detail relations using custom data sources.

If data source implements neither IListSource nor IEnumerable interface, then all its properties will be available as data. So, we can implement one record from the table.

Now we can create custom data sources for the authors and books data sources considered in the section “Using ADO.NET objects”.

Create the following class to store information on a single book:

public class Book
{
    public Book()
    {
    }
    private string name = string.Empty;
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
    private decimal price;
    public decimal Price
    {
        get
        {
            return price;
        }
        set
        {
            price = value;
        }
    }
}

This class contains two properties that will be available during report creation.

Now add the class to the BookCollection to store instances of the Book class implementing IEnumerable interface. We will store data in the ArrayList class instance. We will also implement two methods: Add() – to add element to the collection, GetEnumerator() - to get access to the IEnumerator interface, and implement IEnumerable interface in this way. Code of this class is shown below:

public class BookCollection : IEnumerable
{
  private ArrayList list = new ArrayList();
  public BookCollection()
  {
  }    
  public void Add(Book b)
  {
    list.Add(b);
  }
  public IEnumerator GetEnumerator()
  {
    return list.GetEnumerator();
  }
}

Further you need to create class to store information on a single author. As author can have multiple books, the property storing the books should be a collection. So, we realize master-detail relation in custom data sources.

public class Author
{
  public Author()
  {
  }
  private string name = string.Empty;
  public string Name
  {
    get
    {
      return name;
    }
    set
    {
      name = value;
    }
  }
  private BookCollection books = new BookCollection();
  public BookCollection Books
  {
    get
    {
      return books;
    }
  }
}

And finally, you need to create a collection to store authors. For example, this class can be a descendant from System.Collections.CollectionBase that is a base class for strongly typed collections and that implements IEnumerable interface.

public class AuthorCollection : CollectionBase
{
  public Author Add(Author value)
  {
    base.List.Add(value as object);
    return value;
  }
  public void Remove(Author value)
  {
    base.List.Remove(value as object);
  }
  public void Insert(int index, Author value)
  {
    base.List.Insert(index, value as object);
  }
  public Author this[int index]
  {
    get
    {
      return (base.List[index] as Author );
    }
    set
    {
      base.List[index] = value;
    }
  }
}

In this sample, BookCollection class implements IEnumerable interface only to show that there is such ability. Of course, collections can be stored in arrays and in ArrayList class instances, but it is better to use strongly typed collections (descendants of CollectionBase).

Finally, you need to write the method to fill the data source and add it to the DataSources collection of the ReportManager.

    private void Init()
    {
      Author a = new Author();
      a.Name = "Kent Beck";
      Book b = new Book();
      b.Name = "Extreme Proggramming";
      b.Price = 7.45M;
      a.Books.Add(b);
      b = new Book();
      b.Name = "Extreme Proggramming Explained";
      b.Price = 11.33M;
      a.Books.Add(b);
      authorDS.Add(a);

      a = new Author();
      a.Name = "Craig Larman";
      b = new Book();
      b.Name = "Applying UML and Patterns";
      b.Price = 15.80m;
      a.Books.Add(b);
      authorDS.Add(a);
      reportManager1.DataSources.Add("Authors",authorDS);
    }

To create Master-Detail report template, in Report Designer put one DataBand inside other DataBand. Set dataBand1.DataSource property to “Authors” and dataBand2.DataSource property to “Authors.Books”.

To get access to data source fields set textBox.Value = <DataBand name>[“<Field name>”].

This sample is located under the {SharpShooter Reports Installation Directory}\Samples\SharpShooter Reports\CSharp\UserDataSource folder.

Non-Standard Ways of Using Business Objects

In case you need to change standard mechanism of working with custom data source so that some dynamically calculated properties (not object properties) are available as fields, you need to implement ICustomTypeDescriptor interface for the object representing a single record in the data source and ITypedList interface for the collection storing these objects.

Let’s consider this possibility by example. Suppose that you develop application for the company selling parts all over the world. So, in different countries, you will need to output price in different currencies calculating them from the base one. This sample is located in the CustomTypeDescriptorExample folder.

Currency class is used to store information on all possible currencies. Class contains two properties that set name and exchange rate. Static array SystemCurrencies is declared in the class; this array is used to store all currencies used in the system. In the sample, array has definite value, but in real applications, nothing prevents you from loading this array from the database.

To store a single record about the part, Path class is used; this class implements ICustomTypeDescriptor interface. Below you can see code of the static methods forming collection of dynamic properties of this class.

public static PropertyDescriptorCollection GetPartProperties()
{
  PropertyDescriptorCollection props = 
  new PropertyDescriptorCollection(null);
  foreach(Currency c in Currency.SystemCurrencies)
  {
    props.Add(new CurrencyPropertyDescriptor(c));
  }
  props.Add(TypeDescriptor.CreateProperty(typeof(Part), 
  "Name", typeof(string)));
  return props;
}

This very method is called by the interface methods:

ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
ICustomTypeDescriptor.GetProperties()

In order to get collection of dynamic properties, as you can see from code, at the beginning of the cycle CurrencyPropertyDescriptor class instances for every currency type are created from the Currency.SystemCurrencies array and then part name is added.

CurrencyPropertyDescriptor class is inherited from the PropertyDescriptor class that defines abstraction for the class property definition. Pay attention to implementation of the PropertyType and ComponentType properties of this class that gets type of the property and type of the class implementing this property as well as to two methods: GetValue and SetValue getting ans setting property value correspondingly.

And finally, let’s consider PartCollection class used to store parts collection.This class implements ITypedList interface. This interface contains two methods: GetListName getting list name and GetItemProperties getting array of PropertyDescriptor objects defining dynamic properties of the list objects. This method just gets result of the work of static Part.GetPartProperties() method.

Add Feedback