Create report editors for PerformancePoint Services in SharePoint

Learn how to create the editor component of a custom report extension for PerformancePoint Services.

What are custom report editors for PerformancePoint Services?

In PerformancePoint Services, custom report editors enable users to set properties on custom reports. Report editors also initialize the report endpoint, which receives parameter values from scorecard and filter providers. For more information about editor requirements and functionality, see Editors for Custom PerformancePoint Services Objects.

The following procedures and examples are based on the SampleReportViewEditor class from the custom objects sample. The editor is a thin web application that enables users to modify the report's name and description. For the complete code for the class, see Code example: Create, retrieve, and update custom PerformancePoint Services reports in SharePoint.

We recommend that you use the sample editor as a template. The sample shows how to call objects in the PerformancePoint Services API, provides helper objects that simplify calls for repository operations (such as creating and updating objects), and demonstrates best practices for PerformancePoint Services development.

Create editors for custom PerformancePoint Services reports

  1. Install PerformancePoint Services, or copy the DLLs that your extension uses (listed in step 3) to your computer. For more information, see DLLs with Class Libraries.

  2. In Visual Studio, create a C# class library. If you have already created a class library for your extension, add a new C# class.

    You must sign your DLL with a strong name. In addition, ensure that all assemblies referenced by your DLL have strong names. For information about how to sign an assembly with a strong name and how to create a public/private key pair, see How to: Create a public/private key pair.

  3. Add the following DLLs as assembly references to the project:

    • Microsoft.PerformancePoint.Scorecards.Client.dll
    • Microsoft.PerformancePoint.Scorecards.ServerCommon.dll
    • Microsoft.PerformancePoint.Scorecards.Store.dll (used by helper classes)
    • Microsoft.SharePoint.dll (used by helper classes)

    The sample editor also contains assembly references to System.Web.dll and System.Web.Services.dll. Depending on your extension's functionality, other project references may be required.

  4. Add the following classes from the sample to the project. The editor uses these helper classes to interact with the PerformancePoint Services repository:

    • DataSourceConsumerHelper.cs
    • ExtensionRepositoryHelper.cs
    • ReportViewRepositoryHelper.cs
    • IDataSourceConsumer.cs

    Note

    The sample report obtains data from a filter, so it does not use DataSourceConsumerHelper or IDataSourceConsumer objects. However, if your report obtains data from a PerformancePoint Services data source, you can use the methods that are exposed by the DataSourceConsumerHelper class to retrieve data sources as described in Create filter editors for PerformancePoint Services in SharePoint.

  5. In your editor class, add using directives for the following PerformancePoint Services namespaces:

    • Microsoft.PerformancePoint.Scorecards
    • Microsoft.PerformancePoint.Scorecards.ServerCommon

    Depending on your extension's functionality, other using directives may be required.

  6. Inherit from the base class that supports your editor implementation. Because the sample report editor is a web application, it inherits from the Page class. Other implementations can derive from base classes such as the UserControl or WebPart class.

  7. Declare variables for the controls that expose the properties that you want users to view or modify. The sample report editor first declares variables for the web server controls that are defined in the user interface component, which is an ASPX page. The sample editor also defines a button control that enables users to submit changes. Then, the editor calls the CreateChildControls() method to make the controls available on the page.

    Note

    The editor defines programming logic separately from the user interface. Instructions for creating the user interface component of the editor are beyond the scope of this documentation.

    The sample report editor performs steps 8 through 12 in the Page_Load method. Page_Load is also used to initialize and validate variables and controls, populate controls, and save state information for the custom report and helper objects.

  8. Set the AllowUnsafeUpdates property to true. This enables the report editor to write data to the repository without using form POST operations.

  9. Retrieve the parameters from the query string and set them as values for local variables, as shown in the following code example.

    // The URL of the site collection that contains the PerformancePoint Services repository.
    string server = Request.QueryString[ClickOnceLaunchKeys.SiteCollectionUrl];
    
    // The location of the report in the repository.
    string itemLocation = Request.QueryString[ClickOnceLaunchKeys.ItemLocation];
    
    // The operation to perform: OpenItem or CreateItem.
    string action = Request.QueryString[ClickOnceLaunchKeys.LaunchOperation];
    

    Note

    For information about the query string parameters, see Editors for Custom PerformancePoint Services Objects.

  10. Retrieve the ReportViewRepositoryHelper object, which is used to make calls to the repository, as shown in the following code example.

    reportviewRepositoryHelper = new ReportViewRepositoryHelper();
    
  11. Set the report location based on the query string parameter, as shown in the following code example.

    RepositoryLocation repositoryReportViewLocation = RepositoryLocation.CreateFromUriString(itemLocation);
    
  12. Retrieve the operation to perform ( OpenItem or CreateItem) from the query string, and then retrieve or create the custom report.

    • To retrieve the custom report, use the ReportViewRepositoryHelper.Get method.
    • To create the custom report, use the ReportView() constructor and then define the report's Name , RendererClassName , and SubTypeId properties.

    SubTypeId is the unique identifier for the report, and it must match the subType attribute that you specify for your custom report in the PerformancePoint Services web.config file. RendererClassName is the fully qualified name of the class that defines the renderer web server control. If not defined in the editor, this value defaults to the renderer class specified in the web.config file.

    if (ClickOnceLaunchValues.OpenItem.Equals(action, StringComparison.OrdinalIgnoreCase))
    {
      // Use the repository-helper object to retrieve the report.
      reportview = reportviewRepositoryHelper.Get(repositoryReportViewLocation);
      if (reportview == null)
      {
        displayError("Could not retrieve the report view for editing.");
        return;
      }
    }
    else if (ClickOnceLaunchValues.CreateItem.Equals(action, StringComparison.OrdinalIgnoreCase))
    {
      reportview = new ReportView
      {
        RendererClassName = typeof(SampleReportRenderer).AssemblyQualifiedName,
        SubTypeId = "SampleReportView"
      };
    }
    else
    {
      displayError("Invalid Action.");
      return;
    }
    

    Note

    By default, users can create custom objects from PerformancePoint Dashboard Designer only. To enable users to create a custom object outside of Dashboard Designer, you must add a menu item that sends a CreateItem request to your editor from the content type in the repository. For more information, see Editors for Custom PerformancePoint Services Objects.

  13. Define the report's endpoint, which enables the report to receive data from filters and scorecards. The sample report editor defines the required properties for the endpoint, as shown in the following code example.

    if (0 == reportview.EndPoints.Count)
    {
      EndPoint endpoint = new EndPoint
      {
        Category = EndPointCategory.None,
        UniqueName = "SampleReportView_EndPoint",
    
        // The display name is shown to users in Dashboard Designer.
        // It represents the endpoint that can be connected
        // to a filter or scorecard.
        DisplayName = "Sample Report View EndPoint"
      };
    
      reportview.EndPoints.Add(endpoint);
    }
    

    The sample editor defines the endpoint in the VerifyReportView method. It also uses VerifyReportView to verify that required properties are set and to define the optional CustomData property, which you can use to store information for your report.

  14. Update the report with user-defined changes. The buttonOK_Click method in the sample report editor calls the ReportViewRepositoryHelper.Update method to update the report's Name and Description properties in the repository. buttonOK_Click is also used to validate the contents of the controls and retrieve state information for the custom report and the helper object.

    Note

    Users can edit a custom object's Name , Description , and Owner ( Person Responsible) properties and delete custom objects directly from Dashboard Designer and the PerformancePoint Services repository.

Code example: Create, retrieve, and update custom PerformancePoint Services reports in SharePoint

The following code example creates, retrieves, and updates custom reports. This code is from the editor's code-behind class, which provides the programming logic for controls that are defined in an ASPX page.

Before you can compile this code example, you must configure your development environment as described in Create editors for custom PerformancePoint Services reports.

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.PerformancePoint.Scorecards;
using Microsoft.PerformancePoint.Scorecards.ServerCommon;

namespace Microsoft.PerformancePoint.SDK.Samples.SampleReport
{

    // Represents the class that defines the sample report editor.
    public class SampleReportViewEditor : Page
    {

        // Declare private variables for the ASP.NET controls defined in the user interface.
        // The sample's user interface is an ASPX page that defines the controls in HTML.
        private TextBox textboxName;
        private TextBox textboxDescription;
        private Label labelErrorMessage;
        private Button buttonOK;

        // Make the controls available to this class.
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            if (null == textboxName)
                textboxName = FindControl("textboxName") as TextBox;
            if (null == textboxDescription)
                textboxDescription = FindControl("textboxDescription") as TextBox;
            if (null == labelErrorMessage)
                labelErrorMessage = FindControl("labelErrorMessage") as Label;
            if (null == buttonOK)
                buttonOK = FindControl("buttonOK") as Button;
        }

        // Handles the Load event of the Page control.
        // Methods that use a control variable should call the Control.EnsureChildControls
        // method before accessing the variable for the first time.
        protected void Page_Load(object sender, EventArgs e)
        {

            // Required to enable custom report and filter editors to
            // write data to the repository.
            ServerUtils.AllowUnsafeUpdates = true;

            // Initialize controls the first time the page loads only.
            if (!IsPostBack)
            {
                EnsureChildControls();
                ReportViewRepositoryHelper reportviewRepositoryHelper = null;
                try
                {

                    // Get information from the query string parameters.
                    string server = Request.QueryString[ClickOnceLaunchKeys.SiteCollectionUrl];
                    string itemLocation = Request.QueryString[ClickOnceLaunchKeys.ItemLocation];
                    string action = Request.QueryString[ClickOnceLaunchKeys.LaunchOperation];

                    // Validate the query string parameters.
                    if (string.IsNullOrEmpty(server) ||
                        string.IsNullOrEmpty(itemLocation) ||
                        string.IsNullOrEmpty(action))
                    {
                        displayError("Invalid URL.");
                        return;
                    }

                    // Retrieve the repository-helper object.
                    reportviewRepositoryHelper =
                        new ReportViewRepositoryHelper();

                    // Set the report location by using the location from the query string.
                    RepositoryLocation repositoryReportViewLocation = RepositoryLocation.CreateFromUriString(itemLocation);

                    ReportView reportview;

                    // Retrieve or create the report object, depending on the operation
                    // passed in the query string (OpenItem or CreateItem).
                    if (ClickOnceLaunchValues.OpenItem.Equals(action, StringComparison.OrdinalIgnoreCase))
                    {

                        // Retrieve the report object by using the repository-helper object.
                        reportview = reportviewRepositoryHelper.Get(repositoryReportViewLocation);
                        if (reportview == null)
                        {
                            displayError("Could not retrieve the report view for editing.");
                            return;
                        }
                    }
                    else if (ClickOnceLaunchValues.CreateItem.Equals(action, StringComparison.OrdinalIgnoreCase))
                    {

                        // Create a report view.
                        // CreateItem requests can be sent from a SharePoint list, but
                        // you must create a custom menu item to send the request.
                        // Dashboard Designer can send edit requests only.
                        reportview = new ReportView
                        {
                            RendererClassName = typeof(SampleReportRenderer).AssemblyQualifiedName,
                            SubTypeId = "SampleReportView"
                        };
                    }
                    else
                    {
                        displayError("Invalid Action.");
                        return;
                    }

                    VerifyReportView(reportview);

                    // Save the original report and helper objects across page postbacks.
                    ViewState["action"] = action;
                    ViewState["reportview"] = reportview;
                    ViewState["reportviewrepositoryhelper"] = reportviewRepositoryHelper;
                    ViewState["itemlocation"] = itemLocation;

                    // Populate the child controls.
                    textboxName.Text = reportview.Name.ToString();
                    textboxDescription.Text = reportview.Description.ToString();
                }
                catch (Exception ex)
                {
                    displayError("An error has occurred. Please contact your administrator for more information.");
                    if (reportviewRepositoryHelper != null)
                    {

                        // Add the exception detail to the server event log.
                        reportviewRepositoryHelper.HandleException(ex);
                    }
                }
            }
        }

        // Handles the Click event of the buttonOK control.
        protected void buttonOK_Click(object sender, EventArgs e)
        {
            EnsureChildControls();

            // Verify that the textboxName control contains a value.
            if (string.IsNullOrEmpty(textboxName.Text))
            {
                labelErrorMessage.Text = "A report view name is required.";
                return;
            }

            // Clear any pre-existing error message.
            labelErrorMessage.Text = string.Empty;

            // Retrieve the report and helper objects from view state.
            string action = (string)ViewState["action"];
            string itemLocation = (string) ViewState["itemlocation"];
            ReportView reportview = (ReportView)ViewState["reportview"];
            ReportViewRepositoryHelper reportviewRepositoryHelper = (ReportViewRepositoryHelper)ViewState["reportviewrepositoryhelper"];

            // Update the report object with form changes.
            reportview.Name.Text = textboxName.Text;
            reportview.Description.Text = textboxDescription.Text;

            // Save the report object to the PerformancePoint Services repository.
            try
            {
                reportview.Validate();
                if (ClickOnceLaunchValues.CreateItem.Equals(action, StringComparison.OrdinalIgnoreCase))
                {
                    reportview.CreatedDate = DateTime.Now;
                    ReportView newReportView = reportviewRepositoryHelper.Create(
                        string.IsNullOrEmpty(reportview.Location.ItemUrl) ? itemLocation : reportview.Location.ItemUrl,
                        reportview);
                    ViewState["reportview"] = newReportView;
                    ViewState["action"] = ClickOnceLaunchValues.OpenItem;
                }
                else
                {
                    reportviewRepositoryHelper.Update(reportview);
                }
            }
            catch (Exception ex)
            {
                displayError("An error has occurred. Please contact your administrator for more information.");
                if (reportviewRepositoryHelper != null)
                {
                    // Add the exception detail to the server event log.
                    reportviewRepositoryHelper.HandleException(ex);
                }
            }
        }

        // Displays the error string in the labelErrorMessage label.
        void displayError(string msg)
        {
            EnsureChildControls();

            labelErrorMessage.Text = msg;

            // Disable the OK button because the page is in an error state.
            buttonOK.Enabled = false;
            return;
        }

        // Verifies that the properties for the report object are set.
        static void VerifyReportView(ReportView reportview)
        {

            // Verify that all required properties are set.
            if (string.IsNullOrEmpty(reportview.SubTypeId))
            {

                // This value must match the subType attribute specified
                // in the web.config file.
                reportview.SubTypeId = "SampleReportView";
            }

            if (string.IsNullOrEmpty(reportview.RendererClassName))
            {
                reportview.RendererClassName = typeof (SampleReportRenderer).AssemblyQualifiedName;
            }

            // Reports are consumers and do not use provider endpoints.
           reportview.BeginPoints.Clear();

            // If there are no consumer endpoints, create one so we can connect a filter or a scorecard to the report.
            if (0 == reportview.EndPoints.Count)
            {
                EndPoint endpoint = new EndPoint
                {
                    Category = EndPointCategory.None,
                    UniqueName = "SampleReportView_EndPoint",

                    // The display name is shown to users in Dashboard
                    // Designer to represent the endpoint that can be
                    // connected to a filter or scorecard.
                    DisplayName = "Sample Report View EndPoint"
                };

                reportview.EndPoints.Add(endpoint);
            }

            // Set optional properties for reports.
            reportview.CustomData = "You can use this property to store custom information for this filter as a string or a serialized object.";
        }
    }
}

Next steps

After you create a report editor (including its user interface, if required) and a report renderer, deploy the extension as described in How to: Manually Register PerformancePoint Services Extensions.

See also