Share via


How to: Create Editors for PerformancePoint Services Tabular Data Sources

In PerformancePoint Services in Microsoft SharePoint Server 2010, custom editors enable users to set properties on custom objects. They provide editing controls and retrieve and update custom objects in the repository. For more information about editor functionality, see Editors for Custom PerformancePoint Services Objects.

Applies to: SharePoint Server 2010

The procedures and code examples in this topic are based on the SampleDataSourceEditor class from the custom objects sample. The editor is a thin Web application that enables users to modify the name and description of the data source, enter stock symbols, and to specify a proxy server address and cache file location. The complete code for the class is provided in the "Example" section in this topic.

Note

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.

You create a data source editor by performing two basic procedures, as follows:

  • Creating and configuring the editor class

  • Defining the editing functionality

To create a custom editor, start by creating the editor class.

To create and configure the editor class

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

  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.

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

    • Microsoft.PerformancePoint.Scorecards.Client.dll

    • Microsoft.SharePoint.dll (used by helper classes)

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

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

    • ExtensionRepositoryHelper.cs

    • DataSourceRepositoryHelper.cs

    • SampleDSCacheHandler.cs

  5. In your editor class, add a using directive for the Microsoft.PerformancePoint.Scorecards namespace. 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 data source 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.

After you create and configure the editor class, you must define your editor's functionality.

To define the editing functionality

  1. Declare variables for the controls that expose the properties that you want users to view or modify. The sample data source 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.

  2. 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 data source in the repository.
    string itemLocation = Request.QueryString[ClickOnceLaunchKeys.ItemLocation];
    
    // The operation to perform: OpenItem or CreateItem.
    string action = Request.QueryString[ClickOnceLaunchKeys.LaunchOperation];
    

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

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

    DataSourceRepositoryHelper = new DataSourceRepositoryHelper();
    
  4. Set the data source location based on the query string parameter, as shown in the following code example.

    RepositoryLocation repositoryDataSourceLocation = RepositoryLocation.CreateFromUriString(itemLocation);
    
  5. Retrieve the operation to perform (OpenItem or CreateItem) from the query string, and then retrieve or create the custom data source, as shown in the following code example.

    if (ClickOnceLaunchValues.OpenItem.Equals(action, StringComparison.OrdinalIgnoreCase)) 
    {
        // Use the repository-helper object to retrieve the data source.
        datasource = dataSourceRepositoryHelper.Get(repositoryDataSourceLocation);
        if (datasource == null)
        {
            displayError("Could not retrieve the data source for editing.");
            return;
        }
    }
    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.

    The sample data source editor performs steps 2 through 5 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 data source and helper objects.

  6. Update the data source with user-defined changes. The sample data source editor calls the DataSourceRepositoryHelper.Update method to update the Name, Description, and CustomData properties of the data source object in the repository. You can use CustomData to store a serialized object or string. The sample editor uses it to store user-defined stock symbols, the location of the cache file that stores stock quote values, and the address of the proxy server.

    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.

  7. Call the data source provider to define column mappings if they are not already defined.

    The sample data source editor performs steps 6 and 7 in the buttonOK_Click and CreateCacheFile methods. buttonOK_Click is also used to call the AreAllInputsValid method to validate the contents of the controls and to retrieve state information for the custom data source and the helper object.

    Next step: After you create a data source editor (including its user interface, if required) and data source provider, deploy the extension as described in How to: Manually Register PerformancePoint Services Extensions. For instructions about how to install the sample data source extension, see the "Installing the Sample Report, Filter, and Data Source Objects" section in PerformancePoint Services Code Sample: Custom Report, Filter, and Tabular Data Source Objects.

Example

The following code example retrieves and updates custom tabular data sources in the repository and provides the programming logic for the controls that are defined in an ASPX page.

Note

Before you can compile this code example, you must configure your development environment as described in To create and configure the editor class.

using System;
using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.PerformancePoint.Scorecards;
using System.Xml.Linq;

namespace Microsoft.PerformancePoint.SDK.Samples.SampleDataSource
{
    // Represents the class that defines the sample data source editor.
    public class SampleDataSourceEditor : Page
    {

        #region Members
        // Declare private variables for the ASP.NET controls defined in the user interface.
        // The user interface is an ASPX page that defines the controls in HTML.
        private TextBox textboxName;
        private TextBox textboxDescription;
        private TextBox textboxStockSymbols;
        private TextBox textboxXMLLocation;
        private TextBox textboxProxy;
        private Label labelErrorMessage;
        private Button buttonOK;
        #endregion

        #region Page methods and events
        // Make the controls available to this class.
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            if (null == textboxProxy)
                textboxProxy = FindControl("textboxProxy") as TextBox;
            if (null == textboxName)
                textboxName = FindControl("textboxName") as TextBox;
            if (null == textboxDescription)
                textboxDescription = FindControl("textboxDescription") as TextBox;
            if (null == textboxStockSymbols)
                textboxStockSymbols = FindControl("textboxStockSymbols") as TextBox;
            if (null == textboxXMLLocation)
                textboxXMLLocation = FindControl("textboxXMLLocation") 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)
        {
            // Initialize controls the first time the page loads only.
            if (!IsPostBack)
            {
                EnsureChildControls();

                DataSourceRepositoryHelper dataSourceRepositoryHelper = 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.
                    dataSourceRepositoryHelper =
                        new DataSourceRepositoryHelper();

                    // Set the data source location.
                    RepositoryLocation repositoryDataSourceLocation = RepositoryLocation.CreateFromUriString(itemLocation);

                    DataSource datasource;

                    // Retrieve the data source object by
                    // using the repository-helper object.
                    if (ClickOnceLaunchValues.OpenItem.Equals(action, StringComparison.OrdinalIgnoreCase))
                    {
                        datasource = dataSourceRepositoryHelper.Get(repositoryDataSourceLocation);
                        if (datasource == null)
                        {
                            displayError("Could not retrieve the data source for editing.");
                            return;
                        }
                    }
                    else
                    {
                        displayError("Invalid Action.");
                        return;

                    }

                    // Save the original data source and helper objects across page postbacks.
                    ViewState["action"] = action;
                    ViewState["datasource"] = datasource;
                    ViewState["datasourcerepositoryhelper"] = dataSourceRepositoryHelper;

                    // Populate the child controls.
                    if (null != datasource.Name)
                        textboxName.Text = datasource.Name.ToString();

                    if (null != datasource.Description)
                        textboxDescription.Text = datasource.Description.ToString();

                    if (null != datasource.CustomData)
                    {
                        string[] splitCustomData = datasource.CustomData.Split('&');
                        if (splitCustomData.Length > 2)
                        {
                            textboxStockSymbols.Text = splitCustomData[0];
                            textboxXMLLocation.Text = splitCustomData[1].Replace(@"\SampleStockQuotes.xml", string.Empty);
                            textboxProxy.Text = splitCustomData[2];
                        }
                    }
                }
                catch (Exception ex)
                {
                    displayError("An error has occurred. Please contact your administrator for more information.");
                    if (dataSourceRepositoryHelper != null)
                    {
                        // Add the exception detail to the server
                        // event log.
                        dataSourceRepositoryHelper.HandleException(ex);
                    }
                }
            }
        }

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

            // Verify that the required fields contain values.
            if (!AreAllInputsValid())
                return;

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

            // Retrieve the data source and helper objects from view state.
            string action = (string)ViewState["action"];
            DataSource datasource = (DataSource)ViewState["datasource"];
            DataSourceRepositoryHelper datasourcerepositoryhelper = (DataSourceRepositoryHelper)ViewState["datasourcerepositoryhelper"];

            // Update the data source object with form changes.
            datasource.Name.Text = textboxName.Text;
            datasource.Description.Text = textboxDescription.Text;

            // Define column mappings if they aren't already defined.
            if (datasource.DataTableMapping.ColumnMappings.Count <= 0)
            {
                datasource.DataTableMapping = WSTabularDataSourceProvider.CreateDataColumnMappings();
            }

            // Save the data source to the repository
            // by using the repository-helper object.
            try
            {
                CreateCacheFile(datasource);

                datasource.Validate();

                if (ClickOnceLaunchValues.OpenItem.Equals(action, StringComparison.OrdinalIgnoreCase))
                {
                    datasourcerepositoryhelper.Update(datasource);
                }
                else
                {
                    displayError("Invalid Action.");
                }
            }
            catch (Exception ex)
            {
                displayError("An error has occurred. Please contact your administrator for more information.");
                if (datasourcerepositoryhelper != null)
                {
                    // Add the exception detail to the server event log.
                    datasourcerepositoryhelper.HandleException(ex);
                }
            }
        }
        #endregion

        #region Helper methods
        // Display 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;
        }

        // Validate the text box inputs.
        bool AreAllInputsValid()
        {
            if (string.IsNullOrEmpty(textboxProxy.Text))
            {
                labelErrorMessage.Text = "The proxy server address is required.";
                return false;
            }

            if (string.IsNullOrEmpty(textboxXMLLocation.Text))
            {
                labelErrorMessage.Text = "The location to save the cache file to is required.";
                return false;
            }

            if (string.IsNullOrEmpty(textboxName.Text))
            {
                labelErrorMessage.Text = "A data source name is required.";
                return false;
            }

            if (string.IsNullOrEmpty(textboxStockSymbols.Text))
            {
                labelErrorMessage.Text = "A stock symbol is required.";
                return false;
            }

            return true;
        }

        // Create the XML cache file at the specified location and
        // store it and the stock symbols in the CustomData
        // property of the data source.
        void CreateCacheFile(DataSource datasource)
        {
            string cacheFileLocation = string.Format("{0}\\{1}", textboxXMLLocation.Text.TrimEnd('\\'),
                                                     "SampleStockQuotes.xml");

            datasource.CustomData = string.Format("{0}&{1}&{2}", textboxStockSymbols.Text, cacheFileLocation, textboxProxy.Text);

            // Check if the cache file already exists.
            if (!File.Exists(cacheFileLocation))
            {
                // Create the cache file if it does not exist.
                XDocument doc = SampleDSCacheHandler.DefaultCacheFileContent;
                doc.Save(cacheFileLocation);
            }
        }
        #endregion
    }
}

Compiling the Code

Before you can compile this code example, you must configure your development environment as described in To create and configure the editor class.

Security

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.

See Also

Tasks

How to: Create Providers for PerformancePoint Services Tabular Data Source

Concepts

Editors for Custom PerformancePoint Services Objects

Other Resources

Create Custom Objects for PerformancePoint Services

Code Samples for PerformancePoint Services in SharePoint Server 2010