BPC-Components

Home > Products > BPC PowerForms - Silverlight > Knowledge Base > General > Building Custom Controls

Extending PowerForms : Developing custom form controls

Power Forms is designed with extensibility in mind.

There are some cases where you need some special customization for your form. For these cases, developers can build and register their custom controls (using Visual Studio & Silverlight). Since any Silverlight control can be registerer inside Power Forms, the possibilities are endless.

For this example, we will use MS Visual Studio 2010 to implement an alternative control for boolean fields. Instead of the default checkbox control, we will create a control that will display radio buttons for TRUE and FALSE boolean values.

In order to develop your own custom controls, you will have to build a Silverlight Control that implements the IInputControl interface (BPC.PowerForms.Core.IInputControl). Some of the interface methods are only required for lookup controls. 

Note: This procedure requires a reference to the BPC.PowerForms.Core.dll library that contains the required interfaces. Please contact us to request for the SDK containing the dll.  

The interface definition follows:

    public interface IInputControl

    {

        /// <summary>

        /// The event should be raised whenever the

        /// value of the control changes

        /// </summary>

        event EventHandler InputControlValueChanged;

        /// <summary>

        /// In case of Lookup controls, this event should be raised

        /// at the end of the data-loading procedure.

        /// The dynamic form will then take control

        /// and set the control value

        /// </summary>

        event EventHandler LookupLoaded;

 

        /// <summary>

        /// In case of choice controls containing static values,

        /// the datasource (a string array) will be provided

        /// </summary>

        /// <param name="datasource"></param>

        void SetDataSource(IEnumerable datasource);

        /// <summary>

        /// This method is called to set the value to the control.

        /// The value provided has the format provided by Sharepoint.

        /// </summary>

        /// <param name="value"></param>

        void SetValue(object value);

        /// <summary>

        /// This method should be implemented to provide values back to the form

        /// </summary>

        /// <returns></returns>

        string GetValue();

        /// <summary>

        /// This function should return a human friendly representation of the control value.

        /// You should implement this method because there is a system function

        /// (FDV = FieldDisplayValue) that may be used during calculation

        /// for formulas that retrieves the display value of the control

        /// For example, you may have a combo loookup, persisting the ID and displaying

        /// the title of countries and another one for cities

        /// If want to create a new calculated field containing a friendly representation

        /// of the address, you could use the following formula:

        /// FV("c_Address") & " " & FDV("c_City") & " " & FDV("c_Country")

        /// </summary>

        /// <returns></returns>

        string GetDisplayValue();

        /// <summary>

        /// This method should be implemented to clear the value of the control

        /// </summary>

        void ClearValue();

        /// <summary>

        /// This method should only be implemented for lookup controls and

        /// should query the undelying datasource to fill its data

        /// Filter criteria are provided

        /// </summary>

        /// <param name="queryFieldName"></param>

        /// <param name="queryFieldValue"></param>

        void LoadLookupData(string queryFieldName, string queryFieldValue);

        /// <summary>

        /// This method should only be implemented for lookup controls and

        /// should query the undelying datasource to fill its data

        /// Filter criteria are provided

        /// </summary>

        /// <param name="queryFieldName"></param>

        /// <param name="queryFieldValue"></param>

        /// <param name="queryType"></param>

        /// <param name="op"></param>

        void LoadLookupData(string queryFieldName, string queryFieldValue, string queryType, SearchOperators op);

        /// <summary>

        /// Implement to enable or disable the control

        /// </summary>

        /// <param name="enable"></param>

        void SetEnable(bool enable);

        /// <summary>

        /// Implement to show or hide the control

        /// </summary>

        /// <param name="visible"></param>

        void SetVisible(bool visible);

        /// <summary>

        /// Implement to set or clear the readonly status of the control

        /// </summary>

        /// <param name="readOnly"></param>

        void SetReadOnly(bool readOnly);

        /// <summary>

        /// Implement to set the display properties of the control

        /// </summary>

        /// <param name="thickness"></param>

        void SetBorderThickness(Thickness thickness);

        /// <summary>

        /// Implement to set the display properties of the control

        /// </summary>

        /// <param name="brush"></param>

        void SetBorder(Brush brush);

        /// <summary>

        /// Implement to set the display properties of the control

        /// </summary>

        /// <param name="brush"></param>

        void SetForeground(Brush brush);

        /// <summary>

        /// Implement to set the display properties of the control

        /// </summary>

        /// <param name="brush"></param>

        void SetBackground(Brush brush);

        /// <summary>

        /// Implement to set the display properties of the control

        /// </summary>

        /// <param name="weight"></param>

        void SetFontWeight(FontWeight weight);

        /// <summary>

        /// This method is called during form initiation.

        /// </summary>

        /// <param name="Url">The Url of the customized list.</param>

        /// <param name="control">The control properties as defined in the form designer</param>

        /// <param name="template">The complete form template containing definitions

        /// for all the available controls of the form</param>

        void Init(string Url, IFormControl control, IFormTemplate template);

        /// <summary>

        /// The constraint value for cascading lookups is provided in this field.

        /// For example in controls like lookup controls that perform the

        /// loading at a later stage (like popup lookup pickers)

        /// the value of a constraint field is stored here.

        /// </summary>

        string ConstraintFieldValue { get; set; }

            /// <summary>
        /// Gets whether the control's value type is complex and cannot be represented by plain text.
        /// XML is used for this kind of detail information, when you need to store more than one
        /// records or an entire structure.
        /// Usually the GetDisplayValue() method has to return HTML when IsValueXML=true.
        /// </summary>
        bool IsValueXML { get; }
        /// <summary>
        /// Indicates whether the control stores a value or is used to perform actions
        /// in the form (i.e. a button control)
        /// </summary>
        bool IsActionControl { get; }

 

    }

Using Visual Studio 2010, create a new Silverlight Class Library Project called MyControlPack.

Choose Silverlight 4 for the target version.

Add a Reference to the BPC.PowerForms.Core.dll library that contains the required interfaces. Please contact us to request for the SDK containing the dll.  

Create a new UserControl  (Project > Add New Item > Silverlight User Control) and name it MyRadioBooleanControl.xaml

Next, design the user interface of the control.

We want to keep the control simple so a simple grid with one row and two columns will do.

<Grid x:Name="LayoutRoot" Background="Transparent">

        <Grid.ColumnDefinitions>

            <ColumnDefinition />

            <ColumnDefinition />

        </Grid.ColumnDefinitions>

        <RadioButton x:Name="rd_True" GroupName="main" Grid.Column="0" HorizontalAlignment="Center" IsChecked="False" Checked="RadioButton_Checked" Unchecked="RadioButton_Checked" >True</RadioButton>

        <RadioButton x:Name="rd_False" GroupName="main" Grid.Column="1" HorizontalAlignment="Center" IsChecked="False" Checked="RadioButton_Checked" Unchecked="RadioButton_Checked" >False</RadioButton>

    </Grid>

 

Now turn to the code behind and implement the IInputControl Interface.

ClearValue method should clear the check state of both controls.

public void ClearValue()

{

this.rd_True.IsChecked = false;

this.rd_False.IsChecked = false;

}

 

GetValue function should return 1 (one), 0 (zero) or nothing, as these are the values that sharepoint expects for Boolean fields.

public string GetValue()

{

if (this.rd_True.IsChecked.Value) return "1";

if (this.rd_False.IsChecked.Value) return "0";

return "";

}

 

GetDisplayValue is optional and could return something like YES when true and NO when false.

 

public string GetDisplayValue()

{

if (this.rd_True.IsChecked.Value) return "Yes";

if (this.rd_False.IsChecked.Value) return "No";

return "";

}

 

SetValue must be implemented to fill the control.

 

public void SetValue(object value)

{

string val = "" + value;

this.rd_True.IsChecked = (val == "1");

this.rd_False.IsChecked = (val == "0");

}

 

We should also raise the InputControlValueChanged event whenever the user changes value in the control so we handle the “Checked” and “UnChecked” events of both controls.

 

private void RadioButton_Checked(object sender, RoutedEventArgs e)

{

this.rd_False.FontWeight = this.rd_False.IsChecked.Value ? FontWeights.Bold : FontWeights.Normal;

this.rd_True.FontWeight = this.rd_True.IsChecked.Value ? FontWeights.Bold : FontWeights.Normal;

if (this.InputControlValueChanged != null)

                this.InputControlValueChanged(this, null);

}

 

 

The complete code behind follows:

 

public partial class MyRadioBooleanControl : UserControl, IInputControl

    {

        public MyRadioBooleanControl ()

        {

            InitializeComponent();

        }

 

        #region IInputControl Members

 

        public event EventHandler InputControlValueChanged; 

        public event EventHandler LookupLoaded;

        public void SetDataSource(System.Collections.IEnumerable datasource)

        {          

        }

 

        public void SetValue(object value)

        {

            string val = "" + value;

            this.rd_True.IsChecked = (val == "1");

            this.rd_False.IsChecked = (val == "0");

        } 

        public string GetValue()

        {

            if (this.rd_True.IsChecked.Value) return "1";

            if (this.rd_False.IsChecked.Value) return "0";

            return "";

        } 

        public string GetDisplayValue()

        {

            if (this.rd_True.IsChecked.Value) return "Yes";

            if (this.rd_False.IsChecked.Value) return "No";

            return "";

        } 

        public void ClearValue()

        {

            this.rd_True.IsChecked = false;

            this.rd_False.IsChecked = false;

        } 

        public void LoadLookupData(string queryFieldName, string queryFieldValue)

        {           

        } 

        public void LoadLookupData(string queryFieldName, string queryFieldValue, string queryType, SearchOperators op)

        {           

        }

         public void SetBorderThickness(Thickness thickness)

        {

            this.rd_True.BorderThickness = thickness;

            this.rd_False.BorderThickness = thickness;

        } 

        public void SetBorder(Brush brush)

        {

            this.rd_False.BorderBrush = brush;

            this.rd_True.BorderBrush = brush;

        } 

        public void SetForeground(Brush brush)

        {

            this.rd_True.Foreground = brush;

            this.rd_False.Foreground = brush;

        } 

        public void SetBackground(Brush brush)

        {

        } 

        public void SetFontWeight(FontWeight weight)

        {

            this.rd_False.FontWeight = weight;

            this.rd_True.FontWeight = weight;

        } 

        public void Init(string Url, IFormControl control, IFormTemplate template)

        {           

        }

        public string ConstraintFieldValue {get; set;}

 

        public void SetEnable(bool enable)

        {

            this.rd_False.IsEnabled = enable;

            this.rd_True.IsEnabled = enable;

        } 

        public void SetVisible(bool visible)

        {

            this.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;

        } 

        public void SetReadOnly(bool readOnly)

        {   

            this.SetEnable(!readOnly);        

        } 

        public bool IsValueXML

        {

            get { return false; }

        }

        public bool IsActionControl

        {

            get { return false; }

        }

      

        #endregion

 

        private void RadioButton_Checked(object sender, RoutedEventArgs e)

        {

            this.rd_False.FontWeight = this.rd_False.IsChecked.Value ? FontWeights.Bold : FontWeights.Normal;

            this.rd_True.FontWeight = this.rd_True.IsChecked.Value ? FontWeights.Bold : FontWeights.Normal;

            if (this.InputControlValueChanged != null)

                this.InputControlValueChanged(this, null);

        }

    }

 

Build the project and we are ready to attach the control to our form.

 

First you should upload the class library at the path that the xap file resides.

 

If you use a document library for the xap file, then the class library should be renamed, since SharePoint by default, blocks *.dll files.

Rename the binary file to "MyControlPack.dl_" for example and upload.

 

Using the form designer, go to the Assemblies tab and add a new item called MyControlPack and set the filename to MyControlPack.dl_.

Next go to the Control Types tab and add a new control type.

 

Name is the required name for your control. Class Name is the full name (Namespace + ClassName) of your control. Assembly Name is the name of the assembly you previously registered.

Save the form and enter design mode again.
The new control type will be available in the "Control Type" drop down for use.

 

The final result is a new functional and reusable control type for your dynamic forms: