PerformancePointy: Wade Dorrell's MSDN Blog

PerformancePoint Monitoring + Silverlight: KPIs and Scorecards

PerformancePoint Monitoring doesn't have as robust a prescription as Silverlight Blueprint for SharePoint, but yes, PerformancePoint Monitoring & Silverlight can be used together.

PerformancePoint dashboards are SharePoint web part pages, so web parts or master pages using Silverlight for menus, visualization, charting, or any other thing, are right at home in (or surrounding) a PerformancePoint dashboard.

Another integration point is the web service PmService.asmx. You can learn more about this service through PerformancePoint SDK documentation of its client proxy. In this article we’ll call the GenerateView method of this service to retrieve and work with a scorecard/KPI data set, and visualize that data in Silverlight 2.

The Objective

Here’s a simple scorecard containing everyone’s favorite KPI, “Freight Cost Below 29”.

 image

We’ll render this same data lovingly within a Silverlight 2 application that looks like:

image

If a scorecard contained additional cells, we could obviously do a lot more… a value trend sparkline, or multiple KPI slices in a ticker-style UI… but we’ll start with a simple <Button/>.

Step 1: PmService.asmx & Cross-Domain Callers

Here’s an essential development/security tip: If our Silverlight application isn’t hosted at the same host/port as PmService.asmx, we’ll need to deal with cross-domain calling issues. Learn more at MSDN: Make a Service Available Across Domain Boundaries or Tim Heuer's excellent Silverlight blog.

Step 2: Creating/Configuring The Service Reference

Configuring a service reference for a Silverlight 2 project is familiar for those who've built .NET client/service applications, but perhaps not for PerformancePoint Monitoring developers… we can't use the previously mentioned client proxy because its assembly references the .NET client runtime, not the Silverlight runtime.

So we generate new, Silverlight-specific, client proxy and associated classes from WSDL by using Visual Studio’s “Add Service Reference” against http://myperformancepointserver:40000/WebService/PmService.asmx?wsdl.

The generated proxies contains references to “ArrayOfXElement”, which keeps them from compiling. For the purposes of this article, you can remove any code that references ArrayOfXElement until the proxy does compile.

Step 3: Call GenerateView

We create an instance of PmServiceSoapClient, the client proxy generated from WSDL, and call the service method GenerateView. The GUID parameters to GenerateView are Scorecard ID and ConfiguredView ID. (We find these in a.bswx file containing the scorecard.)

PmServiceSoapClient client = new PmServiceSoapClient();            
client.GenerateViewCompleted += new EventHandler<GenerateViewCompletedEventArgs>(client_GenerateViewCompleted);
client.GenerateViewAsync(
new Guid("3449fb9f-458b-44d4-8509-f75e1ad99e78"),
new Guid("7bb670e0-2ad8-4ed9-a1e1-0e63003d4402"),
null,
null);

Step 4: Get The Scorecard Data Set

Silverlight forces the asynchronous calling pattern, and so the handler for GenerateViewCompleted’s called on a non-UI thread. It's important that changes to elements within the visual be made on the UI thread.  Our method UpdateVisualization updates the visual using data from e.Result, so we use Dispatcher.BeginInvoke to be sure it’s called on the UI thread:

void client_GenerateViewCompleted(object sender, GenerateViewCompletedEventArgs e)
{
Dispatcher.BeginInvoke(() => UpdateVisualization(e.Result));
}

Step 5: Bind the GridViewData to the Visuals

Here's UpdateVisualization, which grabs the (0,0) GridCell and binds a few values to a button through a data model class:

        public void UpdateVisualization(GridViewData gridViewData)
        {
            // This example works off the 1-row header, 1-column header
//
structure of our simple example scorecard. IEnumerable<GridHeaderItem> coordinates = new[] {
gridViewData.RootRowHeader.Children[0],
gridViewData.RootColumnHeader.Children[0] };







// GetCell is an extension method, see Appendix GridCell cell = gridViewData.GetCell(coordinates);
string text = cell.DisplayElements[0].Description; GridColor gridColor = cell.FormatInfo.BackColor; button.DataContext = new DataModel { Description = text, StatusColor = new SolidColorBrush(Color.FromArgb(gridColor.A, gridColor.R, gridColor.G, gridColor.B)) }; }

Here’s the data model class:

        public class DataModel
        {
            public string Description { get; set; }
            public SolidColorBrush StatusColor { get; set; }
        }

And the relevant XAML:

    <Button x:Name="button" Height="21" Width="90">
        <Button.Content>
            <StackPanel Orientation="Horizontal">
                <Ellipse Width="12" Height="12" Fill="{Binding StatusColor}" VerticalAlignment="Center"/>
                <TextBlock Text="{Binding Description}" VerticalAlignment="Center"/>                
            </StackPanel>
        </Button.Content>        
    </Button>

And the result:

image

This isn’t fancy, but hopefully it gets you past the major hurdles, and on to a much more interesting dynamic visualization using Silverlight 2.

Appendix: GetCell for GridViewData

Here's an extension method for the WSDL-generated GridViewData, GetCell. This is used to look up a cell for a given combination of headers.

The cell keying algorithm could change in future versions of PerformancePoint. Please let me know if you run into issues using this.

        /// <summary>
        /// Returns the GridCell for the coordinates defined by the GridHeaderItems. Returns
        /// null if the coordinates refer to an empty/out-of-range cell.
        /// </summary>
        public static GridCell GetCell(this GridViewData t, IEnumerable<GridHeaderItem> coordinates)
        {
            string key = generateKey(coordinates);

            DictionaryWrapperOfStringGridCell table = t.Cells.Table;
            for (int i = 0; i < table.Keys.Length; i++)
            {
                if (table.Keys[i] == key)
                {
                    return table.Values[i];
                }
            }

            return null;            
        }

        private static string generateKey(IEnumerable<GridHeaderItem> coordinates)
        {
            return coordinates
                .Where(coordinate => null != coordinate.DimensionName)
                .OrderBy(coordinate => coordinate.DimensionName)
                .Select(coordinate => string.Format("{0}:{1}{2}_", coordinate.DimensionName, coordinate.DimensionValue, coordinate.Id))
                .Aggregate(new StringBuilder(), (stringBuilder, keyPart) => stringBuilder.Append(keyPart), sb => sb.ToString());
        }
Published Monday, November 17, 2008 10:00 AM by Wade Dorrell
Filed under:

Attachment(s): PerformancePointSilverlightGenerateView1.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Wade Dorrell said:

I will attach the full source for this, as soon as I remember how to attach! :)

November 17, 2008 2:32 PM
 

Wade Dorrell said:

Added full source & the .bswx file I was using. You'll defintely have to replace the reference to "cubes4" in the .bswx and "ppssdk:40000" in ServiceReferences.ClientConfig with your own names. The cube being reference by the .bswx is Adventure Works 2008 data warehouse from SQL Server 2008 samples.

November 24, 2008 5:12 PM
 

Rocky said:

Can you please give an example on how to provide 3rd parameter to GenerateViewAsync function.

As per msdn this parameter is MemberCollection pageFilterOverrides

I couldn't find any example to provide this parameter.

February 19, 2009 2:25 PM

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

About Wade Dorrell

I'm a Program Manager on the SharePoint BI team. I'm passionate about business intelligence blue oceans & giving business decision makers the tools + data to support decisions.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker