MindFusion.Wpf Pack Programmer's Guide
Tutorial 2: Binding to a Data Source

This tutorial will demonstrate how to create and bind DataRange objects manually and how to create master-detail relationship between data-ranges representing related tables. The following steps are explained in more details below:

Prerequisites

This tutorial continues from where Tutorial 1: Getting Started has ended. It is therefore assumed that we have an existing application with a single data-bound report in it and a window with a set up DocumentViewer that displays an instance of this report.

1. Adding a new data adapter

Now that we have an existing data set and an adapter for the 'Products' table, we will expand this data source by creating an adapter for the 'Categories' table.

To do this, go to the 'Data Sources' window, select the 'nwindDataSet' and click the 'Configure DataSet with Wizard' button from the 'Data Sources' window toolbar. This will bring up the 'Data Source Configuration Wizard' that we used in Tutorial 1 in order to setup the data source initially.

 Note

To open the 'Data Sources' window, select the Data -> Show Data Sources menu command.

Expand the 'Tables' node and select the 'Categories' table by placing a check in the box to its left. Click 'Finish' to create a new table adapter for the selected table.

2. Nesting DataRange objects for master-detail implementation

We create a new DataRange object within the report's existing XAML declaration in a way similar to the one used in Tutorial 1. The new DataRange object is bound to the Categories data table and contains two elements - a Label and a Picture, bound respectively to the CategoryName and Picture fields in the Categories table. The XAML code below illustrates the code of the new DataRange object.

XAML  Copy Code

...
<r:DataRange Location="0,0" Size="100%,150" DataSource="{Binding Categories}">
  <r:DataRange.ItemTemplate>
    <DataTemplate>
      <r:ItemContainer>
        <r:Label Location="10,10" Size="190,20" Text="{Binding CategoryName}" />
        <r:Picture Location="60%,10" Size="38%,135" Image="{Binding Picture}" />
      </r:ItemContainer>
    </DataTemplate>
  </r:DataRange.ItemTemplate>
</r:DataRange>
...

Now we want to embed the old DataRange, showing products, within the newly created DataRange, showing categories and we want to establish a master-detail relationship between the two data ranges so that the result displays products, grouped by category. Here is the new XAML code of the report illustrating the embedded data ranges.

XAML  Copy Code

<r:Report x:Key="myReport">
  <r:Page>
    <r:DataRange Location="0,0" Size="100%,150" DataSource="{Binding Categories}">
      <r:DataRange.ItemTemplate>
        <DataTemplate>
          <r:ItemContainer>
            <r:Label Location="10,10" Size="190,20" Text="{Binding CategoryName}" />
            <r:Picture Location="60%,10" Size="38%,135" Image="{Binding Picture}" />

            <r:DataRange Location="0,75" Size="60%,20" Name="ProductsRange">
              <r:DataRange.HeaderTemplate>
                <DataTemplate>
                  <r:ItemContainer Size="100%,20">
                    <r:Label Text="ProductID" Location="0%,0" Size="20%,20" />
                    <r:Label Text="ProductName" Location="20%,0" Size="20%,20" />
                    <r:Label Text="QuantityPerUnit" Location="40%,0" Size="20%,20" />
                    <r:Label Text="UnitPrice" Location="60%,0" Size="20%,20" />
                    <r:Label Text="UnitsInStock" Location="80%,0" Size="20%,20" />
                  </r:ItemContainer>
                </DataTemplate>
              </r:DataRange.HeaderTemplate>
              <r:DataRange.ItemTemplate>
                <DataTemplate>
                  <r:ItemContainer>
                    <r:Label Text="{Binding ProductID}" Location="0%,0" Size="20%,20" />
                    <r:Label Text="{Binding ProductName}" Location="20%,0" Size="20%,20" />
                    <r:Label Text="{Binding QuantityPerUnit}" Location="40%,0" Size="20%,20" />
                    <r:Label Text="{Binding UnitPrice}" Location="60%,0" Size="20%,20" />
                    <r:Label Text="{Binding UnitsInStock}" Location="80%,0" Size="20%,20" />
                  </r:ItemContainer>
                </DataTemplate>
              </r:DataRange.ItemTemplate>
            </r:DataRange>

          </r:ItemContainer>
        </DataTemplate>
      </r:DataRange.ItemTemplate>
    </r:DataRange>
  </r:Page>
</r:Report>

Note that the old DataRange (the one showing products) has had its data source removed. We will supply the data through the QueryDetails event depending on the category each instance of this DataRange belongs to. Note also that the products range now has a name associated with it. This will help us identify the range within the QueryDetails event handler as illustrated later in this topic. Finally, note that the Location of the embedded DataRange has been modified so that it is positioned correctly in its parent and its size is reduced to 60% so that it doesn't overlap with the image to the right.

Navigate to the window's constructor in the code behind and add the code necessary to populate the Categories table in the data set. Additionally, expose the existing adapter variable as a class field because it will be needed in the QueryDetails event handler. The following code (in bold) demonstrates this.

C#  Copy Code

...
nwindDataSet dataset = new nwindDataSet();
adapter = new Tutorial2.nwindDataSetTableAdapters.ProductsTableAdapter();
adapter.Fill(dataset.Products);
Tutorial2.nwindDataSetTableAdapters.CategoriesTableAdapter categoriesAdapter =
    new Tutorial2.nwindDataSetTableAdapters.CategoriesTableAdapter();
categoriesAdapter.Fill(dataset.Categories);


myReport.DataContext = dataset;
...

private Tutorial2.nwindDataSetTableAdapters.ProductsTableAdapter adapter;

Visual Basic  Copy Code

...
Dim dataset As New nwindDataSet()
adapter = New Tutorial2.nwindDataSetTableAdapters.ProductsTableAdapter()
adapter.Fill(dataset.Products)
Dim categoriesAdapter As New Tutorial2.nwindDataSetTableAdapters.CategoriesTableAdapter()
categoriesAdapter.Fill(dataset.Categories)


myReport.DataContext = dataset
...

Private adapter As Tutorial2.nwindDataSetTableAdapters.ProductsTableAdapter

3. Providing details in a master-detail relationship

To provide a master-detail implementation for the two data ranges, we need to handle the QueryDetails event on the Report class. Keep in mind that the event must be handled before the Run method of the Report is invoked. The following code illustrates how the event handler should look.

C#  Copy Code

void myReport_QueryDetails(object sender, QueryDetailsEventArgs e)
{
    DataRange dataRange = sender as DataRange;
    if (dataRange != null)
    {
        if (dataRange.Name == "ProductsRange")
        {
            Tutorial2.nwindDataSet.CategoriesRow categoryRow = e.MasterRow as Tutorial2.nwindDataSet.CategoriesRow;

            e.Details = adapter.GetData().Where(
                product => product.CategoryID == categoryRow.CategoryID);
        }
    }
}

Visual Basic  Copy Code

Sub myReport_QueryDetails(ByVal sender As Object, ByVal e As QueryDetailsEventArgs)

    Dim dataRange As DataRange = sender
    If (Not dataRange Is Nothing) Then

        If (dataRange.Name = "ProductsRange") Then

            Dim categoryRow As Tutorial2.nwindDataSet.CategoriesRow = e.MasterRow

            e.Details = From product In adapter.GetData() _
                Where (product.CategoryID = categoryRow.CategoryID)

        End If

    End If

End Sub

The code above inspects the report element, which requests details. This is usually a DataRange or a PieChart object. Then, it identifies the object by inspecting its Name. If the object is the one we are expecting, we provide the necessary details by matching the CategoryID of the products in the database against the ID of the category associated with the data range.

 Note

Do not forget to include the System.Linq namespace in order the above code to compile successfully.

4. Running the application

Running the application will yield a result similar to the one illustrated by the following image: