Developer FAQ

How to add a custom widget to C1?

Answer:

Apart from a wide range of built-in C1 widget options, there is a possibility to add your own custom widget.

The main task is creation and registration of a Widget Provider.

The Widget Providers can expand on the list of visual widgets that Composite C1 supports in the Data Type editor and other guided scenarios. Widget Providers do not provide actual user interface elements and they are not a necessary step when creating new UI artifacts. Instead, they can emit Form UI Control markup which is used by Composite C1 when auto-generating forms.

Let’s look at the sample that adds a custom widget for fields in date and time format.

Note: Before compiling this sample, add references to the following components:

  • System.Configuration
  • Composite
  • Microsoft.Practices.EnterpriseLibrary.Common
  • Microsoft.Practices.ObjectBuilder

Here is the sample:

using System;
using System.Collections.Generic;
using System.Xml.Linq;

using Composite.Functions;
using Composite.Functions.Plugins.WidgetFunctionProvider;
using Composite.C1Console.Security;
using Composite.Core.Xml;
using Composite.C1Console.Elements;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.ObjectBuilder;

namespace SampleProvider
{
    [ConfigurationElementType(typeof(MyWidgetFunctionProviderData))]
    class MyWidgetFunctionProvider : IWidgetFunctionProvider, IDynamicTypeWidgetFunctionProvider
    {
        // Called by the system to provide you with an object you can use to
        // Notify the system about ad hoc introduction of new widget functions
        // Not used in this sample.
        public WidgetFunctionNotifier WidgetFunctionNotifier
        {
            set { }
        }
        public IEnumerable<IWidgetFunction> Functions
        {
            get
            {
                // Return just one widget function here.
                yield return new SampleWidgetFunction();
            }
        }

        public IEnumerable<IWidgetFunction> DynamicTypeDependentFunctions
        {
            get { yield break; }
        }

    }

    [Assembler(typeof(MyWidgetFunctionProviderAssembler))]
    public class MyWidgetFunctionProviderData : WidgetFunctionProviderData
    {
        // No custom configuration in this sample...
    }

    public sealed class MyWidgetFunctionProviderAssembler
        : IAssembler<IWidgetFunctionProvider, WidgetFunctionProviderData>
    {
        public IWidgetFunctionProvider Assemble(IBuilderContext context,
            WidgetFunctionProviderData objectConfiguration, IConfigurationSource configurationSource,
            ConfigurationReflectionCache reflectionCache)
        {
            return new MyWidgetFunctionProvider();
        }
    }

    /// <summary>
    /// This class is a concrete windget function, returned by the provider.
    /// </summary>
    public class SampleWidgetFunction : IWidgetFunction
    {
        // Called by the system when Form UI Control markup is required from this widget function.
        // The parameters contain your "widget configuration". You can define your parameters using
        // the ParameterProfiles properties below.
        public XElement GetWidgetMarkup(ParameterList parameters, string label,
            HelpDefinition helpDefinition, string bindingSourceName)
        {
            return new XElement(Namespaces.BindingFormsStdUiControls10 + "DateTimeSelector",
                new XAttribute("Label", label),
                new XAttribute("Help", helpDefinition.HelpText),
                    new XElement(Namespaces.BindingForms10 + "bind",
                        new XAttribute("source", bindingSourceName)));
        }

        public string Name
        {
            get { return "SampleWidgetFunction"; }
        }

        public string Namespace
        {
            get { return "Sample.Widget.Namespace"; }
        }

        // This sample widget can handle string data
        public Type ReturnType
        {
            get { return typeof(string); }
        }

        public string Description
        {
            get { return "This is the description of the sample widget from our sample provider"; }
        }

        // No prameters used in this sample
        public IEnumerable<ParameterProfile> ParameterProfiles
        {
            get { yield break; }
        }

        // This returns a "security ID" for this widget function.
        // It permissions are attached to this widget function the object returned here is used to
        // identify those settings.
        public EntityToken EntityToken
        {
            get { return new SampleWidgetFunctionEntityToken(this.Name); }
        }
    }

    /// <summary>
    /// This class describes the sample widget functions "entity token" - an artifact 
    /// used to identify data and elements in the security system.
    /// </summary>
    [SecurityAncestorProvider(typeof(NoAncestorSecurityAncestorProvider))]
    public class SampleWidgetFunctionEntityToken : EntityToken
    {
        private string _id;

        internal SampleWidgetFunctionEntityToken(string id)
        {
            _id = id;
        }

        public override string Id { get { return _id; } }
        public override string Source { get { return ""; } }
        public override string Type { get { return ""; } }

        // Responsible for serializing the state of this EntityToken as a string
        public override string Serialize()
        {
            return _id;
        }

        // Respinsible for deserializing the string return by the serializer above
        public static EntityToken Deserialize(string serializedEntityToken)
        {
            return new SampleWidgetFunctionEntityToken(serializedEntityToken);
        }
    }

    internal sealed class NoAncestorSecurityAncestorProvider : ISecurityAncestorProvider
    {
        public IEnumerable<EntityToken> GetParents(EntityToken entityToken)
        {
            yield return AttachingPoint.PerspectivesRoot.EntityToken;
        }
    }
}
Download the sample code

Code sample highlights

The provider class returns information about its widget functions through the Functions property. A widget function is not known if it is not returned here.

The widget functions must have a unique namespace / name combination. If the system finds two or more functions with the same name, they are both ignored and a warning is written to the system log.

The GetWidgetMarkup function is the “heart” of the widget function class. While all the properties defined on IWidgetFunction serve declarative purposes, the GetWidgetMarkup function “executes” the widget function. GetWidgetMarkup must return an XElement representing valid Form UI Control markup.

In the GetWidgetMarkup function you can use some of already existing UI controls to build your widget.

In this example we've used the DateTimeSelector control to build a widget for a String field.

Please, refer to the Composite configuration in ~\App_Data\Composite\Composite.config:

<Namespace name="http://www.composite.net/ns/management/binding...">

To add a custom widget to C1 UI, take the following steps:

  1. Compile your code and place the assembly in ~\Bin directory.
  2. Register your provider in the Composite configuration in ~\App_Data\Composite\Composite.config (see below).

Registering the provider in the Composite configuration

To register your provider in the Composite configuration file, add an <add /> element to the plug-ins list of the Composite.Functions.Plugins.WidgetFunctionProviderConfiguration element. Below is how the sample provider shown above would look like.

<Composite.Functions.Plugins.WidgetFunctionProviderConfiguration>
  <WidgetFunctionProviderPlugins>
    <add type="SampleProvider.MyWidgetFunctionProvider, WidgetProvider" name="WidgetProvider" />
  </WidgetFunctionProviderPlugins>
</Composite.Functions.Plugins.WidgetFunctionProviderConfiguration>

 Note: Please, make sure to set a correct value for the type attribute of the add tag.

type="Namespace_name.Class_name, Assembly_name” and name is an assembly name.