Building XrmToolBox Tools, Part 2

Finally getting a chance to finish up my Part 2 post! This post is a bit long as we discuss tools and templates used to build XrmToolBox Tools, how to get started with debugging, a review of the sample code, and step through constructing our own Tool. Note that for this post, I am assuming that you have some development background and are familiar with Visual Studio and C#.

In our last episode…

Or rather, in our last post Building XrmToolBox Tools, we began the discussion of building XrmToolBox Tools.  We talked about general building blocks of an XrmToolBox Tool and the components developers must implement.

We also decided on a sample Tool as a demonstration and our sample should solve the following problem: For a given security role, how can I see a list of users that that role assigned?  This new Tool will allow us to load a list of Security Roles from our Dynamics CRM connection and when I select the role, load the users assigned to this role.

Some documentation

Before diving into code, we should look at the XrmToolBox portal documentation for developers. The developer documentation provided by Tanguy Touzard and team is solid and provides a great starting point for developers.  This section of the portal highlights the available tools, some of the components mentioned in our previous post, and some tips on debugging. 

Visual Studio Project Template

First thing we will do when building the tool is install the VS Project Template.  You can either install this template manually by downloading from the Microsoft VS Marketplace here: XrmToolBox Plugin Project Template.  You can also install the project template within Visual Studio by searching for “XrmToolBox” in the New Project dialog under Online.


XrmToolBox Visual Studio Project Template

This template provides several key elements for our new XrmToolBox Tool project:

  • NuGet references – XrmToolBox extensibility assemblies and the latest Dynamics CRM SDK references are included with the template
  • Class Templates – the two default classes are created for us, Plugin.cs and PluginControl.cs, each deriving from their respective base classes
  • Sample Code – both the Plugin.cs and PluginControl.cs classes include examples that demonstrate basic usage of the base class properties and methods

So with the template, we start with a fully functioning XrmToolBox Tool!

Since this sample project is a working XrmToolBox Tool, we can jump in and start debugging as a way to review. First I am naming our sample Tool Project MyXrmToolBoxPlugin. Because each Tool is a windows user control loaded by XrmToolBox and not a standalone executable, we need to set up some debugging settings in our project and we can see our sample tool in action.

Set up Debugging

Fortunately, a copy of the XrmToolBox is included with the Visual Studio Template. When you build your project, the XrmToolBox.exe application is places into the output folder. Since we have a copy of the XrmToolBox and its related assemblies, we only need to set a few values in the project to enable debugging.

First, in the Debug section of the project Properties, we need to set the following values:

Start Action -> Start External Program: Set this value to the XrmToolBox.exe in your debug output folder. This is typically your project folder plus \bin\debug. For example:

C:_source\Sample Code\MyXrmToolBoxPlugin\MyXrmToolBoxPlugin\bin\Debug\XrmToolBox.exe

Start Options -> Command Line Arguments: Set this value to

/overridepath:.

This instructs the XrmToolBox to look in the current folder path for settings, connections, and available Tools to be loaded.

In the Build settings, we have another setting to update to ensure that the XrmToolBox can find our new Tool. In the Build Events tab, update the Post-build event command line, update the value to the following

IF NOT EXIST Plugins mkdir Plugins
move /Y $(TargetFileName) Plugins
move /Y $(TargetName).pdb Plugins

Our Post-build event commands first create a Plugins folder and then move our new Tool assembly and associated PDB file to the Plugins folder. This is where the XrmToolBox.exe will look for Tools to load because we included the /overridepath command line argument above.

When we start the debugger, XrmToolBox loads our newly compiled Tool. Because we have no other Tools in the Plugin folder, we have a clean instance of XrmToolBox for our testing and development:

Our new Tool on display

The Sample Code

Now we can take a look at the code in our sample Tool and break down the classes and starter code included with the template project.

In the previous post, we discussed the Plugin and PluginControl classes.
In my our new sample project, my new Plugin and PluginControl classes are named MyPlugin.cs and MyPluginControl.cs respectively. These two classes for the foundation of our Tool and the project template includes some default code and settings for these classes.

MyPlugin Class

Looking at MyPlugin.cs, we can see that the class derives from PluginBase and the ExportAttribute provides the metadata used by XrmToolBox to display the our cool icon and Tool details. For example, I’ve updated the template with my information for this sample project:

[Export(typeof(IXrmToolBoxPlugin)),
    ExportMetadata("Name", "FutureZ Sample XrmToolBox Tool"),
    ExportMetadata("Description", "Testing a new Control!"),
    ExportMetadata("SmallImageBase64", "iVBORw0KGgoAAAANSUhEUgAAA..."),
    ExportMetadata("BigImageBase64", "iVBORw0KGgoAAAANSUhEUgAAAFA..."),
    ExportMetadata("BackgroundColor", "Lavender"),
    ExportMetadata("PrimaryFontColor", "Black"),
    ExportMetadata("SecondaryFontColor", "Gray")]

The rest of the class is relatively straight forward, a single method that provides an instance of our UserControl class to the host XrmToolBox:

public override IXrmToolBoxPluginControl GetControl()
{
    return new MyPluginControl();
}

The PluginBase class offers a few additional methods for us but for most Tools, this is usually all that the Plugin class offers. The real work for our Tool lives in the PluginControl class!

MyPluginControl Class

The PluginControl class is our Tool user interface, providing the canvas on which we present controls and information to the user. This control is instantiated by the Plugin class via the GetControl() method we viewed above and it implements the IXrmToolBoxPluginControl interface. Instead of implementing this interface ourselves, we will derive from the PluginControlBase class in our MyPluginControl class.

Note that XrmToolBox also offers an additional base class called MultipleConnectionsPluginControlBase that, as you might guess, allow you to leverage multiple Service connections in your Tool. For more detail on this, see my post My first XrmToolBox Portal contribution!. In the meantime, our sample will require only one connection.

Opening the designer for the new control, we have a blank canvas on which to work. The only visual element provided is a toolbar strip with two buttons:

Blank Canvas for our new Tool!

Well, that’s not really too exciting.

Looking at the code, we actually find a solid example to get us started. This PluginControlBase class provides several properties and methods as described in our previous post, such as the Service property, the WorkAsync method, and the ExecuteMethod methods. The short sample code provided by the template demonstrates of how to leverage these base properties and methods. Let’s start with the control Load method.

Notifications

In the MyPluginControl_Load method, the first thing we see is a call to the base control method ShowInfoNotification. This displays a user notification in the shared space provided by the XrmToolBox. For example:

ShowInfoNotification("This is a notification that can lead to XrmToolBox repository", 
new Uri("https://github.com/MscrmTools/XrmToolBox"));

A variety of these methods are available, such as ShowWarningNotification and ShowErrorNotification offer a common method for providing user feedback.

Settings

The next thing we see available is the ability to save settings for your Tool. The SettingsManager offers methods to serialize and deserialize your custom objects to and from disk. These serialized objects are saved in your local Plugins folder and you have full control over what you decide to store in this object. This is a very simple but a powerful method for saving Tool settings such as a list of Entity names, a FetchXml snippet, or a serialized metadata object.

Logging

We also have methods for logging information to the shared log for XrmToolBox. As with notifications, we have a variety of log methods, such as LogInfo, LogWarning, and LogError.

Service Connections

In the XrmToolBox allows users to change a connection at any time and when the connection is changed, it will notify the already loaded Tools. The UpdateConnection method meant to be overridden by your tool to handle this connection change.

How you handle this can be critical depending on the Tool you are building. For example, the connection could change and you may update the incorrect instance of CRM with bad data!

ExecuteMethod

Now we are getting into more complex features provided by the XrmToolBox base classes! The ExecuteMethod method is a helper that will ensure that a connection is established before calling the method you provide. For example, if you had a method called LoadEntities, a valid connection via the Service reference would be required or an error would occur.

In the template sample code includes a method called GetAccounts. This does what you would expect and it will invoke RetrieveMultiple on the Service instance. If the Service instance is not valid, the user will see an error… not a good user experience. Looking at the Try me tool strip button click event handler, we can see the ExecuteMethod being used to invoke the GetAccounts method (including some helpful comments!):

private void tsbSample_Click(object sender, EventArgs e)
{
    // The ExecuteMethod method handles connecting to an
    // organization if XrmToolBox is not yet connected
    ExecuteMethod(GetAccounts);
}

As the comment says, ExecuteMethod will first check for a valid Service organization connection before calling the GetAccounts method. If the reference is not valid, XrmToolBox will launch the standard dialog for connecting to an organization and once connected, the GetAccounts method will be invoked.

Async methods

Looking at the GetAccounts method in more detail, we can see another critical method offered by the XrmToolBox for Tool developers: the WorkAsync method in the first line. This method allows us to execute code without locking up the XrmToolBox user interface. Under the hood, this method leverages the BackgroundWorker class for “executing a time-consuming operation asynchronously “.

WorkAsync(new WorkAsyncInfo
{
    Message = "Getting accounts",
    Work = (worker, args) => {
        args.Result = Service.RetrieveMultiple(new QueryExpression("account") {
            TopCount = 50
        });
    },
    PostWorkCallBack = (args) =>
    {
        if (args.Error != null) {
            MessageBox.Show(args.Error.ToString(), "Error",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        var result = args.Result as EntityCollection;
        if (result != null) {
            MessageBox.Show($"Found {result.Entities.Count} accounts");
        }
    }
});

The actual work being done is to make a simple call to retrieve the top 50 Accounts and show a message box. But it illustrates that the WorkAsync method is made up of a few key elements:

  • WorkAsync takes a single parameter of type WorkAsyncInfo, the object with properties and methods required for the background thread call
  • WorkAsyncInfo properties
    • Message – the message that will display in the XrmToolBox standard message banner while this block of code executes
    • Work – a callback that will include the code that can potentially lock up the main user interface. This work will be performed on a different thread, so we need to be careful what we do in this section.
    • PostWorkCallBack – a callback that will include code that can execute once your Work callback completes

The WorkAsyncInfo object includes a few additional properties which we will discuss soon. I plan on a follow up post soon to provide more detail around these asynchronous methods. You may not make heavy use of asynchronous methods but we will definitely need these when making calls to the server.

And now, our Role Assignment Checker Tool

Now that we have covered the building blocks provided by the XrmToolBox base classes and we have seen some examples on usage, let’s build a new Tool. In the example above, we have already updated the MyPlugin.cs class attributes with our tool information, including our cool icons. So we can now focus on the user interface and business logic in the MyPluginControl.cs class.

First, let’s outline the tasks we wish to accomplish as described at the beginning of the post. Broken down into features, we want to:

  • Display a list of Security Roles in the system
  • Allow the user to select a Security Role from the list
  • When the Security Role is selected, display a list of System Users to which this role is assigned

List the Security Roles

We need some code to retrieve the list of Security Roles in the system and then display them on screen. A very simple method for displaying a list of items and allowing a single selection is the WinForm ListBox control. We can place the ListBox control on the form with its default of SelectionMode set to One and update the name to listBoxSecurityRoles.

For populating the ListBox, we will need a helper class that allows us to bind our list of Security Roles using the listBoxSecurityRoles DataSource property.

private class ListBoxDisplayItem
{
    internal ListBoxDisplayItem(string name, string value)
    {
        _name = name;
        _value = value;
    }

    private string _name;
    private string _value;

    public string Name { get => _name; set => _name = value; }
    public string Value { get => _value; set => _value = value; }
}

The ListBoxDisplayItem class is not absolutely necessary, but it can make things simpler for us if we need to also save a copy of a complex object with a ListBox item. In this example, it allows us to save a Name and Value pair as ListBox item.

Now that we know how to call asynchronous methods, we can follow the pattern in the GetAccounts method with a new method named GetRoles

private void GetRoles()
{
    WorkAsync(new WorkAsyncInfo()
    {
        Message = "Retrieving Roles",
        AsyncArgument = null,
        Work = (worker, args) =>
        {
            var QErole = new QueryExpression("role");
            QErole.ColumnSet.AddColumns("name", "businessunitid", "roleid");
            args.Result = Service.RetrieveMultiple(QErole);
        },
        PostWorkCallBack = (args) =>
        {
            if (args.Error != null)
            {
                MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            var result = args.Result as EntityCollection;

            listBoxSecurityRoles.Items.Clear();

            if (result != null)
            {
                var items = new List();
                foreach (var ent in result.Entities)
                {
                    items.Add(new ListBoxDisplayItem((string)ent["name"], ent.Id.ToString()));
                }
                listBoxSecurityRoles.DataSource = items.OrderBy(e => e.Name).ToList();
                listBoxSecurityRoles.DisplayMember = "Name";
                listBoxSecurityRoles.ValueMember = "Value";
            }
        }
    });
}

Our GetRoles method is simple but it illustrates the use of PluginBase properties and methods. The Work callback method on our WorkAsyncInfo object will first retrieve the list of Security Roles using a RetrieveMultiple call using the Service property of the PluginBase class. Because this is being invoked in the Work callback, we can be sure that if the Service connection is slow, our Tool will not lock up the XrmToolBox user interface.

Once the list of Security Roles completes, notice that we place the results into the args.Result property. This allows us to pass our data between the background thread to our PostWorkCallBack method without issue.

In the PostWorkCallBack method, we are taking the result of the RetrieveMultiple which is a list of entities and we are adding them to the a new generic List of ListDisplayItem objects. This generic list is then set as the DataSource for the listBoxSecurityRoles. This allows us to add both the Security Role name and id attribute values to the ListBox, making the values available when the user makes a selection.

List the System Users

When the user selects an item in the ListBox, we want to then load the list of System Users to which the selected Security Role has been applied. To display the list of System Users, we will use a control called the CRMGridView provided by Jonas Rapp.

Not only does Jonas build some killer XrmToolBox Tools, but he also shares some of his cool controls for our use! You can install the control using the Cinteros.Xrm.CRMWinForm NuGet package. The CRMGridView control will allow us to simply bind an EntityCollection as a data source and the control will handle rendering.

We need a new method named LoadUsers to load the EntityCollection for the new CRMGridView. Like our LoadRoles method, LoadUsers will leverage the WorkAsync method and the WorkAsyncInfo class:


private void LoadUsers()
{
    var role = listBoxSecurityRoles.SelectedItem as ListBoxDisplayItem;

    if (role == null)
        return;

    var id = role.Value;

    WorkAsync(new WorkAsyncInfo
    {
        Message = "Getting System Users",
        AsyncArgument = id,
        Work = (worker, args) =>
        {
            var roleId = (string)args.Argument;

            // Instantiate QueryExpression QEsystemuser
            var QEsystemuser = new QueryExpression("systemuser");

            // Add columns to QEsystemuser.ColumnSet
            QEsystemuser.ColumnSet.AddColumns("internalemailaddress", "lastname", "firstname", "domainname");

            // Add link-entity QEsystemuser_systemuserroles
            var QEsystemuser_systemuserroles = QEsystemuser.AddLink("systemuserroles", "systemuserid", "systemuserid");
            QEsystemuser_systemuserroles.EntityAlias = "sur";

            // Add link-entity QEsystemuser_systemuserroles_role
            var QEsystemuser_systemuserroles_role = QEsystemuser_systemuserroles.AddLink("role", "roleid", "roleid");
            QEsystemuser_systemuserroles_role.EntityAlias = "role";

            // Add columns to QEsystemuser_systemuserroles_role.Columns           QEsystemuser_systemuserroles_role.Columns.AddColumns("name");

            // Define filter QEsystemuser_systemuserroles_role.LinkCriteria
            QEsystemuser_systemuserroles_role.LinkCriteria.AddCondition("roleid", ConditionOperator.Equal, roleId);

            args.Result = Service.RetrieveMultiple(QEsystemuser);
        },
        PostWorkCallBack = (args) =>
        {
            if (args.Error != null)
            {
                MessageBox.Show(args.Error.Message, "Oh crap", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (args.Result is EntityCollection views)
            {
                crmGridView.DataSource = views;
            }
        }
    });
}

There are a few things going on in this method:

  • We check for the selected ListBox item. If we have one, we grab the value of the underlying data source, which is our Security Role id
  • The Security Role id is passed into our Work callback method using the AsyncArgument property of the WorkAsyncInfo class. Similar to how we passed data between threads with the PostAsyncCallBack args.Result object, we are passing the id to the Work callbackso that we can retrieve users
  • Another call to RetrieveMultiple, this time returning users to which the Security Role has been assigned
  • Once returned, we load the resulting EntityCollection into the crmGridView control

I wanted to point out something cool about the code in these methods. I’ve left the comments in the LoadUsers snippet and they may seem familiar to some. I created that code using an XrmToolBox Tool built by Jonas named the FetchXmlBuilder. So… we are using XrmToolBox Tools to build new XrmToolBox Tools!

Check out my post Some Useful XrmToolbox Plugins for some notes on the excellent FetchXmlBuilder tool and how I used it for the RetrieveMultiple snippets in the LoadRoles and LoadUsers methods.

Try it out!

Before we can test, we have a few minor things to wire up. First we update the Try Me button event handler to call our LoadRoles method, and then we add an event handler to the ListBox for each selected item change event:

private void listBoxSecurityRoles_SelectedIndexChanged(object sender, EventArgs e)
{
    ExecuteMethod(LoadUsers);
}

private void tsbSample_Click(object sender, EventArgs e)
{
    // The ExecuteMethod method handles connecting to an
    // organization if XrmToolBox is not yet connected
    ExecuteMethod(GetRoles);
}

Once these two event handlers are wired up, we can load our control, click Try Me and see the list of Security Roles load. Then, we can select a Security Role from the ListBox, and we should see our crmGridView load with the list of System Users assigned the role!

Role Assignment Checker Tool in Action!

I connected to a demo instance and selected the System Administrator role, and you can see all of the standard sample System Users with the System Administrator Security Role assignment. Although this is a relatively simple tool, it can come in handy for a System Admin who needs a quick check of Security Role assignments.

Summing up

So this Tool took me about an hour or two to build. I have some experience building tools and controls over the past year and I am already familiar with the framework. I was also able to leverage the CRMGridControl and other code snippets that saved me a significant amount of time. But I think it’s clear that the XrmToolBox framework is simple and flexible enough for us to start building our own Tools!

Where can you go from here? I left many of the defaults in place from the template, such as the generic Settings object and we did not review every line of code in the sample. For example, we did not look at the event handlers attached to our crmGridControl instance for opening the Dynamics CRM record for the System User (pretty cool). That is an area that can be extended, such as displaying different System User attributes or offering an export to CSV. You could add the ability to Add a System User to or Remove a System User from the selected Security Role, either individually or in bulk. There are lots of options from event this simple example!

The next step after you’ve added polish to your Tool is to publish it to the XrmToolBox Plugin Store. This will allow you to share your cool Tool with the rest of the community! This is a fantastic way to gather feedback and make your Tool even more useful. I’ll be writing up notes on this in another post soon!

I hope that this and the prior post offer some insight into how you can create your own XrmToolBox Tool, however simple or complex!

I’ll be presenting a session at 365 Saturday Boston this week on this topic, so I’ve added this this sample and slides to a shared GitHub repository. You can download the sample we just built from the GitHub repository here: Building Xrm Toolbox Tools. I’ll try and keep this up to date. Maybe we can publish it as a new Tool soon!

As always, comments, corrections, and questions are all welcome!

0 Points