Creating the View in Code

First, let’s add some member variables to the CustomUI class to keep track of the underlying Silverlight controls:

C#
Copy Code
C1OlapPanel _olapPanel;     // created in code
C1OlapGrid _olapGrid;       // custom control in designer
C1OlapChart _olapChart;     // custom control in designer
bool _loaded = false;       // true if screen data is available

In this sample, we will create an instance of C1OlapPanel in the screen’s Created method. In previous examples the C1OlapPanel part is visible to the end-user. But in this sample we use it behind-the scenes, so users won’t ever see it. This invisible control is used as a data source for the grid and the chart, and is responsible for filtering and summarizing the data. Both the grid and the chart have their DataSource property set to the C1OlapPanel.

C#
Copy Code
partial void CustomUI_Created()
{
    // create hidden olap panel on UI thread
    Dispatchers.Main.BeginInvoke(() =>
    {
        _olapPanel = new C1OlapPanel();
        _olapPanel.ShowZeros = false;
    });

    // wait for olap grid to become available
    IContentItemProxy gridProxy = this.FindControl("OlapGrid");
    gridProxy.ControlAvailable += new EventHandler<ControlAvailableEventArgs>(gridProxy_ControlAvailable);

    // wait for olap chart to become available
    IContentItemProxy chartProxy = this.FindControl("OlapChart");
    chartProxy.ControlAvailable += new EventHandler<ControlAvailableEventArgs>(chartProxy_ControlAvailable);
}

Note that we must create the C1OlapPanel on the UI thread. Next, we set up proxies for the C1OlapGrid and C1OlapChart controls added to the screen in the designer. We can only access the controls themselves when the corresponding ControlAvailable event handler fires.

C#
Copy Code
void gridProxy_ControlAvailable(object sender, ControlAvailableEventArgs e)
{
    // bind grid to olap panel
    _olapGrid = e.Control as C1OlapGrid;
    _olapGrid.DataSource = _olapPanel;
    _olapGrid.SortedColumn += delegate { _olapChart.UpdateChart(); };
    Bind();
}

void chartProxy_ControlAvailable(object sender, ControlAvailableEventArgs e)
{
    // bind chart to olap panel
    _olapChart = e.Control as C1OlapChart;
    _olapChart.DataSource = _olapPanel;
    Bind();
}

In each case, we set the DataSource property of the control to the C1OlapPanel instance created earlier. For the grid, we also handle the SortedColumn event to keep the chart in sync whenever the user changes the sort order by clicking a grid column header. Since we need to pass the screen’s data (the Invoices collection) to the C1OlapPanel for analysis, we use the Loaded method to receive notification that the data was successfully loaded (or not).

C#
Copy Code
partial void Invoices_Loaded(bool succeeded)
{
    _loaded = succeeded;
    Bind();
}

The following helper functions are used to initialize (and modify) the OLAP view definition:

C#
Copy Code
bool CanBind()
{
    return _loaded && _olapGrid != null && _olapChart != null;
}

void Bind()
{
    ViewBy("Country");
}

void ViewBy(string fieldName)
{
    if (CanBind())
    {
        Dispatchers.Main.BeginInvoke(() =>
        {
            if (_olapPanel.DataSource == null)
            {
                _olapPanel.DataSource = this.Invoices;
            }

            BuildView(fieldName);
        });
    }
}

CanBind returns true if all preconditions have been met; that is, if the Silverlight controls specified in the designer are ready and the Invoices collection has been loaded successfully. Bind is used in several places to set the initial view definition. The code is written so that no assumptions are made about the order in which the preconditions are satisfied. The ViewBy helper method ensures proper threading and performs a one-time assignment of the Invoices collection to the C1OlapPanel control.

The BuildView helper method configures the OLAP field collections. The string argument determines the row field:

C#
Copy Code
void BuildView(string fieldName)
{
    // get olap engine
    var olap = _olapPanel.OlapEngine;

    // stop updating until done
    olap.BeginUpdate();

    // clear all fields
    olap.RowFields.Clear();
    olap.ColumnFields.Clear();
    olap.ValueFields.Clear();

    // format order dates to group by year
    var f = olap.Fields["OrderDate"];
    f.Format = "yyyy";
    f.Caption = "Year";

    // rename value field
    olap.Fields["ExtendedPrice"].Caption = "Sales";

    // build up view
    olap.ColumnFields.Add("OrderDate");
    olap.RowFields.Add(fieldName);
    olap.ValueFields.Add("ExtendedPrice");

    // restore updates
    olap.EndUpdate();

}

The BuildView method gets a reference to the C1OlapEngine object provided by the C1OlapPanel and immediately calls the BeginUpdate method to stop updates until the new view has been completely defined. This is done to improve performance. The code then sets the format of the “OrderDate” field to “yyyy” so that sales are grouped by year. It rebuilds the view by clearing the engine’s RowFields, ColumnFields, and ValueFields collections, then adding the fields that should be displayed. The “fieldName” parameter passed by the caller contains the name of the only field that changes between views in this example. When all of this is done, the code calls EndUpdate so the C1OlapPanel will update the output table. At this point, the code is sufficient to display a grid with countries (rows) and years (columns), along with a bar chart that plots the same values.

In the next step, we will add LightSwitch buttons to the screen command bar for switching and filtering OLAP views.

 

 


Copyright (c) GrapeCity, inc. All rights reserved.

Product Support Forum  |  Documentation Feedback