Skip to content

Develop your own custom plugin for XrmToolBox

imranakram edited this page Jan 24, 2017 · 11 revisions

Work In Progress

In this page:

Related:

Develop your plugin for XrmToolBox v1.2015.7.6 and above

Create a Project

First, create a new Visual Studio project of type Class Library. Create Project

Add NuGet References

Open the NuGet Manager for the solution, search for XrmToolBoxPackage, and install the latest version.

Add XrmToolBoxPackage

XrmToolBox Package and dependencies such as MS CRM SDK should now have been added. Update the packages to latest version, except Microsoft.IdentityModel.Clients.ActiveDirectory, which has to be version 2.28 or earlier.

Update NuGet packages

Update Project Properties

Some of the project properties are used to display your tool in the tools list. Company is used as the author of the tool in the Plugins list. Assembly Version is used to display this version number in the Plugins list.

Project Content

You can add one or multiple XrmToolBox plugins in one assembly. One plugin is made of two classes:

  • A "plugin" class that describes the plugin.
  • A "plugin control" class that represents the user interface of the plugin.

The Plugin Class

The plugin class is a public class that implements the interface IXrmToolBoxPlugin. The XrmToolBox.Extensibility namespace provides a base class, PluginBase, that implements this interface for you. Inheriting from this base class is the recommended approach since it allows you to focus on your plugin specific implementation, and not all the methods in the interface.

The class has to be decorated with the attribute Export with the type IXrmToolBoxPlugin. All ExportMetadata arguments have to be filled with values described below:

  • BackgroundColor : The color used as background for the plugin in the XrmToolBox plugins list.
  • PrimaryFonctColor : The color used for the text displayed in the XrmToolBox plugins list.
  • SecondaryFontColor : The color used for the author of the plugins in the XrmToolBox plugins list (only used when displaying small plugins list).
  • SmallImageBase64 : A base64 encoded 32px/32px image used as the plugin logo in the XrmToolBox small plugins list.
  • BigImageBase64 : A base64 encoded 80px/80px image used as the plugin logo in the XrmToolBox big plugins list.
  • Name : The name of the plugin as displayed in XrmToolBox plugins list.
  • Description : The description of the plugin as displayed in XrmToolBox plugins list.

The only method to implement here is the method GetControl that returns a IXrmToolBoxPluginControl which is the UserControl to display the plugin user interface in the main application.

using System.ComponentModel.Composition;
using XrmToolBox.Extensibility;
using XrmToolBox.Extensibility.Interfaces;

namespace MyFirstPlugin
{
    [Export(typeof(IXrmToolBoxPlugin)),
    ExportMetadata("BackgroundColor", "MediumBlue"),
    ExportMetadata("PrimaryFontColor", "White"),
    ExportMetadata("SecondaryFontColor", "LightGray"),
    ExportMetadata("SmallImageBase64", "a base64 encoded image"),
    ExportMetadata("BigImageBase64", "a base64 encoded image"),
    ExportMetadata("Name", "My First Plugin name"),
    ExportMetadata("Description", "My First Plugin description")]
    public class Plugin : PluginBase
    {
        public override IXrmToolBoxPluginControl GetControl()
        {
            return new PluginControl();
        }
    }
}

The plugin control class

The plugin control is a class that inherits from UserControl class and implements interface IXrmToolBoxPluginControl. The XrmToolBox.Extensibility namespace provides a base class, PluginControlBase, that implements this interface for you. Inheriting from this base class is the recommended approach since it allows you to focus on your plugin specific implementation, and not all the methods in the interface.

Making Calls to CRM

The PluginControlBase provides a Service property that returns the IOrganizationService for the plugin that has been selected by the user in the XrmToolBox. If the user hasn't selected anything, it will be null. Also, All calls to CRM should be asynchronous so the XrmToolBox interface doesn't lock. Therefore, it is very important to note that any time you need to make a call to CRM (accessing the PluginControlBase Service property), your call hierarchy should always go through the ExecuteMethod, which will prompt the user for a connection to CRM if they haven't already selected one, and WorkAsync method, which uses the BackgroundWorkerProcess in order to allow notifications to the user of progress and preventing the UI thread from getting locked!

Plugin Stub

using System;
using System.Windows.Forms;
using XrmToolBox.Extensibility;
using XrmToolBox.Extensibility.Interfaces;

namespace MyFirstPlugin
{
    public partial class PluginControl : PluginControlBase
    {
        // Your code here
    }
}

Final Complete class

namespace MsCrmTools.SampleTool
{
    public partial class SampleTool : PluginControlBase
    {
        #region Base tool implementation

        public SampleTool()
        {
            InitializeComponent();
        }
        
        public void ProcessWhoAmI()
        {
            WorkAsync(new WorkAsyncInfo
            {
                Message = "Retrieving your user id...",
                Work = (w, e) =>
                {
                    var request = new WhoAmIRequest();
                    var response = (WhoAmIResponse)Service.Execute(request);

                    e.Result = response.UserId;
                },
                ProgressChanged = e =>
                {
                    // If progress has to be notified to user, use the following method:
                    SetWorkingMessage("Message to display");
                },
                PostWorkCallBack = e =>
                {
                    MessageBox.Show(string.Format("You are {0}", (Guid)e.Result));
                },
                AsyncArgument = null,
                IsCancelable = true,
                MessageWidth = 340,
                MessageHeight = 150
            });
        }

        private void BtnCloseClick(object sender, EventArgs e)
        {
            CloseTool(); // PluginBaseControl method that notifies the XrmToolBox that the user wants to close the plugin
            // Override the ClosingPlugin method to allow for any plugin specific closing logic to be performed (saving configs, canceling close, etc...)
        }

        private void BtnWhoAmIClick(object sender, EventArgs e)
        {
            ExecuteMethod(ProcessWhoAmI); // ExecuteMethod ensures that the user has connected to CRM, before calling the call back method
        }

        #endregion Base tool implementation

        private void btnCancel_Click(object sender, EventArgs e)
        {
            CancelWorker(); // PluginBaseControl method that calls the Background Workers CancelAsync method.

            MessageBox.Show("Cancelled");
        }
    }
}

Add extra features

IGitHubPlugin interface

Implements this interface to add a menu in XrmToolBox when your plugin is opened and visible. This menu allows to redirect the user to the Issues page of the specified GitHub repository.

ICodePlexPlugin interface

Implements this interface to add a menu in XrmToolBox when your plugin is opened and visible. This menu allows to redirect the user to the Issues, Discussions and Rating page of the specified CodePlex project.

IPayPalPlugin interface

Implements this interface to add a menu in XrmToolBox when your plugin is opened and visible. This menu allows to redirect the user to PayPal donation page (USD, GBP, EUR).

IHelpPlugin interface

Implements this interface to add a menu in XrmToolBox when your plugin is opened and visible. This menu allows to redirect the user to a web help page.

IStatusBarMessager interface

Implements this interface to allow your plugin to send process progress information to XrmToolBox main application. This information can be displayed as text, progress bar or both in the XrmToolBox status bar.

Use your tool

Finally, compile the project and move the resulting assembly into the folder Plugins in the XrmToolBox folder

The Plugins folder is no longer in the same folder as the XrmToolBox.exe. It is created at %AppData%\MscrmTools\XrmToolBox\Plugins when you first run the XrmToolBox application.

Develop your plugin for XrmToolBox before v1.2015.7.6

Create a Project

First, create a new Visual Studio project of type library:

VS New Project - Library

Update Project Properties

Project properties are used to display your tool in the tools list. Title, Description and Company are used.

Project Assembly Properties

Add References

Add the following references to the project:

  • Microsoft.Crm.Sdk.Proxy
  • Microsoft.Xrm.Sdk
  • System.Drawing
  • System.Runtime.Serialization
  • System.ServiceModel
  • System.Windows.Forms
  • XrmToolBox (this tool executable)

References should look like the screenshot below:

Required References

Add a User Control

Add a new user control

Add a user Control This user control must implement the IMsCrmToolsPluginUserControl interface defined in the XrmToolBox executable. The easiest way to do this is to inherit from the XrmToolBox.PluginBase. The interface contains the following elements:

using System;
using System.Drawing;
using Microsoft.Xrm.Sdk;

namespace XrmToolBox
{
    public interface IMsCrmToolsPluginUserControl
    {    
        /// <summary>
        /// Gets the organization service used by the tool
        /// </summary>
        IOrganizationService Service { get; }

        /// <summary>
        /// Gets the logo to display in the tools list
        /// </summary>
        Image PluginLogo { get; }

        /// <summary>
        /// EventHandler to request a connection to an organization
        /// </summary>
        event EventHandler OnRequestConnection;

        /// <summary>
        /// EventHandler to close the current tool
        /// </summary>
        event EventHandler OnCloseTool;

        /// <summary>
        /// Method to allow plugin to Cancel a closing event, or perform any save events required before closing.
        /// </summary>
        void ClosingPlugin(PluginCloseInfo info);

        /// <summary>
        /// Updates the organization service used by the tool
        /// </summary>
        /// <param name="newService">Organization service</param>
        /// <param name="actionName">Action that requested a service update</param>
        /// <param name="parameter">Parameter passed when requesting a service update</param>
        void UpdateConnection(IOrganizationService newService, string actionName = "", object parameter = null);
    }
}

Implement the Interface on the User Control

You may choose to "roll your own", but the simplest way to get started is to use the XrmToolBox.PluginBase. It fully implements the interface, and allows you to focus completely on your plug-in logic.

Optional – Add a Logo

By default the PluginBase will return null for the plug-in logo, which results in no logo being displayed. To add your own logo, first add an image to your project resources:

Project Properties –> Resources –> Add Resource –> Add Existing File, and select the image you’d like to use. The image must be square, with 80 pixels on each side.

Then override the PluginLogo Property to return your image:

/// <summary>
/// Gets the logo to display in the tools list
/// </summary>
public override Image PluginLogo
{
     get { return Resources.MyLogo; }
}

Final Complete Class

The final code for our sample tool is the following:

public partial class SampleTool : XrmToolBox.PluginBase
{
    private void ProcessWhoAmI()
    {
        WorkAsync("Retrieving your user id...",
            (e) => // Work To Do Asynchronously
            {
                var request = new WhoAmIRequest();
                var response = (WhoAmIResponse)Service.Execute(request);

                e.Result = response.UserId;
            },
            e =>  // Cleanup when work has completed
            {
                MessageBox.Show(string.Format("You are {0}", (Guid)e.Result));
            }
        );
    }

    #region Events

    private void BtnWhoAmI_Click(object sender, EventArgs e)
    {
        ExecuteMethod(ProcessWhoAmI);
    }

    private void BtnClose_Click(object sender, EventArgs e)
    {
        base.CloseTool();
    }

    #endregion Events
}

Use your tool

Finally, compile the project and move the resulting assembly into the same folder as the XrmToolBox executable

Common logic

Plug-in Entry and Exit Methods

You can choose to trigger any specific plug-in logic however you like. In this tutorial, we’ll use a button, BtnWhoAmI to start the logic of the plug-in, and BtnClose to close the plug-in. Add these two buttons to your plug-in using the design window. After they are added double click them to generate the Click event handlers. Implement them as follows:

private void BtnWhoAmI_Click(object sender, EventArgs e)
{
    ExecuteMethod(ProcessWhoAmI);
}
 
private void BtnClose_Click(object sender, EventArgs e)
{
    base.CloseTool();
}

The CloseTool will call an event in the XrmToolBox that will then call the ClosingPlugin method (ClosingPlugin will get called anytime a plug-in has to close).

public virtual void ClosingPlugin(PluginCloseInfo info)
{
    if (info.FormReason != CloseReason.None ||
        info.ToolBoxReason == ToolBoxCloseReason.CloseAll ||
        info.ToolBoxReason == ToolBoxCloseReason.CloseAllExceptActive)
    {
        return;
    }

    info.Cancel = MessageBox.Show(@"Are you sure you want to close this tab?", @"Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes;
}

Its uses the standard Windows Form Cancel Event to cancel the close by setting Cancel to true. The PluginBase by default will pop up a MessageBox with a standard, "Are you sure" message, whenever the user attempts to close a single plug-in. If the user selects to close multiple tabs, or closes the form itself, it won't prompt the user. If there are config settings that need to be saved in the plug-in, this method should be overriden, most likely calling any custom plug-in closing logic required, then calling base.ClosingPlugin.

The ExecuteMethod handles ensuring that the XrmToolBox has a valid open connection, before attempting to make an SDK call. If it doesn't have a valid connection, it will prompt the user to connect, and then call the method passed in, in this case ProcessWhoAmI.

Plug-in Logic

In order to simplify multithreading logic, the base provides a WorkAsync method with two different signatures. One provides the ability to perform multiple actions, and notify the user of the work that is being performed:

WorkAsync("Message To Display...",
    (w, e) => // Work To Do Asynchronously
    {
        w.ReportProgress(0, "Doing Something");
        //Do something
        w.ReportProgress(50, "Doing Something Else");
        //Do something else
 
        // Populate whatever the results that need to be returned to the Results Property
        e.Result = new object();
        w.ReportProgress(99, "Finishing");
    },
    e => // Finished Async Call.  Cleanup
    {
        // Handle e.Result
    },
    e => // Logic wants to display an update.  This gets called when ReportProgress Gets Called
    {
        SetWorkingMessage(e.UserState.ToString());
    }
);

The other, which we will use for this tutorial, assumes that one SDK call is being performed and and provides actions to perform the call, and process the result:

WorkAsync("Retrieving your user id...",
    (e) => // Work To Do Asynchronously
    {
        var request = new WhoAmIRequest();
        var response = (WhoAmIResponse)Service.Execute(request);
 
        e.Result = response.UserId;
    },
    e =>  // Cleanup when work has completed
    {
        MessageBox.Show(string.Format("You are {0}", (Guid)e.Result));
    }
);

It is very important to note that any time you access the Service property, your call hierarchy should always go through the ExecuteMethod, and WorkAsync method!

Clone this wiki locally