As a transitional step, this site will temporarily be made Read-Only from July 8th until the new community launch. During this time, you can still search and read articles and discussions.

While the community is read-only, if you have questions or issues requiring TIBCO review/response, please access the new TIBCO Community and select "Ask A Question."

You will need to register or log in or register to engage in the new community.

Create a Custom Panel in TIBCO Spotfire®

Last updated:
8:28am Apr 23, 2020

Back to main C# extensions page

Introduction

A custom panel is a general-purpose UI components in TIBCO Spotfire® that can be used to visualize contextual information, adding controls or other domain specific functionality. Custom panels cna be embedded in the Spotfire web clients, TIBCO Spotfire Business Author and TIBCO Spotfire Consumer, as well as in TIBCO Spotfire Analyst. All with one single implementation.

Prerequisites

  • TIBCO Spotfire® Developer (SDK), see download instructions here.
  • TIBCO Spotfire® Analyst, download from edelivery.tibco.com.
  • Microsoft Visual Studio® 2013 or higher. The free Community edition is available for download here.

See also

  • SDK example: CustomPanelExample - contains code examples related to what is described in this article.

Using the API

The following procedure describes how to create a custom panel in TIBCO Spotfire:

  1. Derive from the CustomPanel class (called MyPanel below) and register a corresponding factory instance in the CustomPanelAddin class.
  2. Register a view in the RegisterViews method., for example:
    protected override void RegisterViews(ViewRegistrar registrar)
    {
          base.RegisterViews(registrar);
          registrar.Register(typeof(CustomPanelView), typeof(MyPanel), typeof(MyPanelView));
    }
    

    where MyPanelView inherits from CustomPanelView<MyPanel>.

  3. Initialize the visualization by overriding the GetResourceCore method.
  4. Add some interaction, see Client-driven interaction and Server-driven interaction.

Initialization of the panel

Conceptually, the CustomPanelView class can be seen as an embedded web server that provides an html file, together with some resources such as JavaScript files, images, etc.

To initialize the panel, override the GetResourceCore method:

protected override HttpContent GetResourceCore(string path, NameValueCollection query, MyVisual snapshotNode)
{
    if (string.IsNullOrEmpty(path))
    {
        path = "Mypanel.html";
    }

    var bytes = GetEmbeddedResource("SpotfireDeveloper.MyPanelExample.webroot." + path);
    return new HttpContent("text/html", bytes);
}

 

Technically, when the end user opens a panel of your custom type, what will happen internally is that an "embedded web client" (an iFrame html element) will appear on the current page, and that web client will make an http request with the URL "/". That request will be routed to call the above method GetResourceCore. In the example above, Mypanel.html will be retrieved.

Note: in order to enable GetEmbeddedResource to find resources specified as in the above example, create a webroot folder in the root of the Visual Studio project. Put the html file and other embedded resources in that folder. Then make sure to set the build action for each of your files is set to "Embedded Resource":

The retrieved html file may in turn load any resource:

<script src=”http://code.jquery.com/jquery-1.11.2.min.js”></script>
<script src=”myscript.js”></script>

 

The request for myscript.js is specified with a relative path so it will be handled by the overridden GetResourceCore method. In the example above, the location of the JavaScript file in the webroot folder is looked up and the file is embedded. However, the JavaScript file could be fetched in any fashion. For example, in the case of debugging, it could be useful to fetch the file from a hardcoded path to avoid a rebuild of the project, for example:

// For easy debugging:
var bytes = File.ReadAllBytes(@"C:\Data\MyVisualStudioProject\webroot\MyFile.html");

// Guess mimetypes based on registered mimetypes
var mimeType = "application/unknown";
var ext = Path.GetExtension(path).ToLower();
var regKey = Registry.ClassesRoot.OpenSubKey(ext);
if (regKey != null && regKey.GetValue("Content Type") != null)
mimeType = regKey.GetValue("Content Type").ToString();

return new HttpContent(mimeType, bytes);

 

This example also shows one way of setting the mime type of the resource.

Client-driven interaction

Your custom panel can be regarded as if it is divided into a client side and a server side, where the client side is a "view" and the server side is a "model".

Some utilities are provided to simplify communication with the server.

Reading data

Take a look at this example:

$(window).on("SpotfireLoaded", function()
    {
        Spotfire.read("GetData", {"argument": "value"}, function(data)
        {
            if (data)
            {
                // typically re-render the panel UI, but this example
                // just displays the returned value 
                alert(data);
            }
        });
    });

 

When the "SpotfireLoaded" event is triggered, the communication channel is open between the client and the server. In this example, the Spotfire.read function fetches data from the server.

The Spotfire.read function takes three arguments:

  • A method identifier
  • An argument object
  • A callback

On the server side, the read call is handled by the ReadCore method:

protected override string ReadCore(string method, string args, MyPanel snapshotNode)
{
    if (method.Equals("GetData", StringComparison.OrdinalIgnoreCase))
    {
        // typically retrieve some data from the document, 
        // but this example just echoes the input argument
        return args;
    }
        
    return base.ReadCore(method, args, snapshotNode);
}

 

Note that when reading data, the serving code is executed on a background thread. This allows multiple simultaneous read operations.

Writing data

When the client needs to modify the document, for example, as a result of user input, use the Spotfire.modify method.

The  Spotfire.modify method takes two arguments:

  • A method identifier
  • An argument object

Example that sets a document property:

Spotfire.modify("SetProperty", {"propertyName": pName, "propertyValue": pValue});

 

On the server side, the modify call is handled by the ModifyCore method:

protected override void ModifyCore(string method, string args, MyPanel liveNode)
{
    if ("SetProperty".Equals(method, StringComparison.Ordinal))
    {
        // Call MyPanel for further processing.
        liveNode.SetProperty(/* … */);
    }
}

 

Note that the modification of the document is performed on the main thread, so it is safe to modify the document.

Server-driven interaction

From the server, it is possible to trigger event handlers on the client side. For example:

private void UpdatePanelUI()
{
    this.InvokeClientEventHandler("render", null);
}

 

At the client:

var render = function(data)
{                
    // Typically call Spotfire.read(...)
    // data will be the argument from the server invocation - null in this example
};

Spotfire.addEventHandler("render", render);