Category Archives: Customization

Tips and Tricks for customizing Microsoft CRM 3.0

Repurposing Dynamics CRM Fields. Just Say No!

I was preforming a pre-upgrade cleanup for a customer when I ran into this:


As you can see, they took a relatively unused field, Address 1: County, and repurposed it and called it Current Software.

This is a very, very, bad thing.

For one, it is totally unnecessary.  Maybe back in the 1980’s when we measured things in kilobytes and megabytes; where gigabytes was just something on a mathmagician’s chart.

But that is not today, when people regularly, and with a straight face, speak of and use petabytes and beyond.

Don’t let me catch you doing this. It is just plain bad design and can lead to fascinatingly odd problems to fix.

Now Available: Fun with fields, a Dynamics CRM Customization Training Guide

Happy Friday Everyone,

I have just released a new training guide for those of you interested in growing your Microsoft Dynamics CRM knowledge.

The purpose is to walk you through the creation of every type of data entry field, format, and behavior available within the Dynamics CRM system.

It was written against Dynamics CRM 2016, so some of the newer features will not be available within earlier versions, but much of the basics will.

Check it out here.

Have a great weekend.


Readonly Fields and The Dynamics CRM Workflow Editor

I ran across an issue this week creating a workflow for an entity that had fields set to read-only.

If you’ve never run across this before, setting the Read Only property on a field will not only restrict the modification of field data to the user, but also when working with the CRM workflow editor.

There are several ways around this type of problem:

Option 1: JavaScript

The first option you have is to use JavaScript to set the fields in question to disabled, using the Form Type to determine when that piece of code should work.

While this works, there is now an extra piece of code that must be maintained by someone.

Option 2: Manually change the fields

The second option is for you to remove the read only property from the fields, publish the entity, create your workflow, then reverse the process and make the fields again read only.

While this works, it is a serious pain in the neck and not very maintainable, should you need to make frequent changes.

Option 3: Business rule

I have not tried this option, but I think it is possible, but I don’t have direct knowledge of the interaction between the business rules and the workflow editor.

Another topic for a future blog.

Option 4: Administration Form

This is, by far, the most elegant solution I think, and the one I chose to implement:

Tip #265: Administrator updates of read-only fields

Dynamics CRM JavaScript: Ensure the current record is Saved before opening a new record

In yesterday’s post, Tracing the Dynamics CRM Form Data save Operation, I discussed identifying data that would be sent to the database when you saved a Dynamics CRM record.

Part of the issue I was facing was related to the form not completing the save operation before I opened a new form record. Since Dynamics CRM 2013 and 2015 user interface rarely opens a new window, the user was getting a warning that there was unsaved data when the page was being switched between the Account and, in this case, the Opportunity record.

So to prevent this from happening, I had to wait for the save to complete then open the new Opportunity record.

Here is the code where I do that:


var parameters = { customer_id: customerId, customer_name: customerName, customer_type: customerType, name: Xrm.Page.getAttribute("name").getValue() }; var windowOptions = { openInNewWindow: true }; setTimeout(function () { openNewForm(parameters); }, 2000); } function openNewForm(parameters) { var windowOptions = { openInNewWindow: true }; Xrm.Utility.openEntityForm("opportunity", null, parameters, windowOptions); }

The Details

We are using the JavaScript method setTimeout to wait for 2,000 milliseconds (2 seconds for those of us who are not computers).

At the end of the wait period, we will call a function called openNewForm which will use the Dynamics CRM method Xrm.Utility.openEntityForm, which opens a new Opportunity form.

You may need to adjust the wait period up or down, depending on your environment. The idea is to wait long enough for the save to complete, but not long enough to irritate the user.

Dynamics CRM JavaScript: Working with Tabs and Sections-The right way

Back in March I wrote the following article: Dynamics CRM Developer Tip O’ the Day: Working with Tabs.

Today I was working on some pre-existing customer JavaScript and ran across this:

Xrm.Page.ui.tabs.get(0).sections.get(6).setVisible(false); // Hide section

Don’t ever do that! Bad developer! Bad!

So again, never, ever use the numerical value to reference a Tab or a Section, unless you have no choice. 

We don’t want to use numbers because if you move or add Tabs or Sections, your code will no longer work.

Do this instead:

Xrm.Page.ui.tabs.get("General").sections.get("Details").setVisible(false); // Hide section

As long as your Tabs and Sections are named properly, you should not have an issue.

Manually Repairing a Sitemap. Part 1

As I mentioned a couple of week ago, my upgrade from CRM 2013 to CRM 2015 did not go well. One of the issues is my SiteMap was totally broken and I had to replace it with the “default.”

The only problem was I wiped out the custom entries for my marketing package, Click Dimensions.

No problem, I thought, I’ll just add the solution back.  Except that did not work.  I ran into ghost SiteMap entries from a previously remove solution. This required a call to Microsoft Support to get them corrected.

Once corrected, I was successfully able to import the solution.  Except there were no custom SiteMap entries.

Only only solution in this case is to manually add them, which involves this process:

The Process

1. Create a new solution.

2. Add the SiteMap to the solution.

3. Export the solution as unmanaged.

4. Unzip the solution files.

5. Take the solution file from the package you are attempting to import and unzip it as well.

6. Open the customization.xml file from step 4 in an editor like Visual Studio or Notepad++.

7. Open the customization.xml file from step 5 in an editor like Visual Studio or Notepad++.

8. Copy the information from the step 7 SiteMap and paste it into the proper location within the step 6 SiteMap.

9. You will need to remove some XML attributes that are specific to the Solution Import process. Here is an example:

<Group Id="Extensions" ResourceId="Group_Extensions" ordinalvalue="5" solutionaction="Added" />

You need to remove the any reference to ordianvalue and solutionaction.

10. Save the step 6 customization.xml file.

11. Re-Zip the files from step 4.

12. Import the solution back into CRM.

13. Refresh your browser and see if the SiteMap changes worked.

Things to Know

I did not get into great detail about the SiteMap itself because you really have to understand how it is organized and all of the components that are involved. If you do not understand these things, you can damage the SiteMap and render your CRM system inaccessible.

Before you start anything like this, please know and understand what you are doing.  See the references below.

If all else fails, open a case with Microsoft.


Edit the site map

SiteMap XML reference

CRM JavaScript Conversion Strategies Summary

Over on my main company site, CRM Accelerators, I have concluded a series on strategies related to converting your JavaScript from Dynamics CRM 4.0 to 2011+.


Here is the summary:

Webinar Recording

I also conducted a free webinar for MSDynamicsWorld recently and you may view that recording here:

Other Resources

Finally, I have a page dedicated to the topic of upgrading from Dynamics CRM 4.0 to 2011+:

It contains links to all of my articles and tools, plus links to articles written by others that I thought would be beneficial.

Setting a Section Label based on a Field Value

I was adding some “fancy” to a Contact form today that I thought I would share with you:


The Concept

On the Contact form, I have added the two sets of addresses that are built into Contact and am calling them Primary Address and Secondary Address.  I thought it would enhance the user experience to change the label when a user specifies the actual type of address.

The result is something like this:



The Implementation

Follow these steps to implement this solution:

Step 1

1. Create two sections on the form to hold the fields, one for Address1 fields, the second for Address2 fields. Each should be configured like this:


2. Add the address fields to the form, to their respective sections.


Step 2

Add the JavaScript above to the web resource for Contact.  If you already have an onLoad method, just add those two lines of code to your method.


Step 2:

Add events to each of the Address Type fields:


Notice that the Pass execution context as first parameter is checked. This is vitally important and the process will not work without it..

Save and publish the form.


Step 3:

Testing using the following protocol:

1. Open a new Contact.

2. Change the value of the Primary Address type.

3. You should see the Primary Address section label change.

4. Repeat steps 1-3 for the Secondary address.

5. Save and close the record.

6. Reopen the record.

7. Verify that the section headers change.


Code Review

Let’s take a deeper dive into the code.  I could have hard-coded the section values into our JavaScript but I thought that it would be better to make a fairly generic version of our code.

Take a look at the beginning of function.

function addressType_onChange(executionContextObj) {
    var field = executionContextObj.getEventSource();

This code gets a handle to the attribute which fired the change event.

Since we are working with an OptionSet, we need to get the label for the currently selected value:

var addressType = field.getText();

Next, a check to make sure we actually have a value:

if (addressType != null && addressType != "") {

And finally, the tricky part:

field.controls.get(0).getParent().setLabel(addressType + " Address")


field is a handle to the attribute, but we need to get to the parent Section where the attribute resides. Unfortunately, the getParent() method is only associate with a form control. 

.controls.get ( 0 ).getParent() will get the parent of the first control associated with the attribute, with the parent being the Section itself

Note: This is making an assumption that the field is not on the form more than once.

Finally, setLabel() is actually going to reset the label for the Section using the value from the Address Type OptionSet and the word Address.



You can use the exact same technique with other field types, but you will need to modify the .getText method to .getValue and change the validity check statement to match the data type being retrieved.

Other than that, this is a pretty simple piece of code.

CRM SDK Nugget: Metadata Browser

Way back when, like in version 3.0 and 4.0, Dynamics CRM had a hidden page that would display a list of entities and attributes in a concise list.

That went away in version 2011 but the good folks on the CRM SDK team (Jim specifically), created a managed solution that was included in the SDK.

The main purpose of the tool is to show you information about CRM entities that does not show up on the normal CRM user interface through the dialogs associated with solutions.  This information is available, but only available via programmatic access.

My utility SnapShot! for Dynamics CRM uses exactly the same techniques to produce reports showing information not normally seen through the CRM UI.



After you have downloaded and installed the SDK, you can find the Metadata Browser here:


Install the Metadata Browser as you would any other managed solution. When finished, open the solution and go to the Configuration page, where you can launch the different Metadata Browser components.


Metadata Browser

The first tool is the main Metadata Browser itself, which you activate by clicking the Open Metadata Browser button:


The interface looks like this:


As you click on an entity on the left-hand side, it’s properties will be displayed on the right.

At this point you can either view or edit the entity and its details.


Entity Metadata Browser

The second function will display a dialog that will allow you to edit an entity. This is the same dialog that is shown when you click the Edit Entity button on the Metadata Browser.

Here is what that interface looks like:


As you can see, you can edit or view any of the properties of the entity.


In conclusion, this is a simple yet powerful tool and one that is often overlooked but one which is great to have in your toolbox, should you need it.

Updated: How much space are my Attachments using? (OnPremise)

I was digging through a customer’s CRM database today to determine where all of the space had gone and thought I would share a couple of SQL scripts I created to help.

Note: You’ll need Administrator access to SQL to run these scripts.

This script will give you a summary of all Annotations, broken down by MimeType, which is the format of the file as it was explained to Dynamics CRM when added to the database.


    COUNT(Mimetype) AS Count,
    SUM(CAST(FileSize / (1024)AS DECIMAL(12,2))) AS 'Size (KB)',
    CAST(SUM(CAST(FileSize AS DECIMAL)) / (1024*1024) AS DECIMAL(12,2)) 
         AS 'Size (MB)'
    MimeType IS NOT NULL

Here are the results:


As you can see, most of the attachments are PDF files.

If you would like to see where all of your attachments reside, try this script:

    COUNT(Annotation.objecttypecode) as Count
    join MetadataSchema.Entity on 
        MetadataSchema.Entity.ObjectTypeCode = annotation.ObjectTypeCode 
group by
order by


Here are the results:


As you can see, most of my attachments are associated with Fax activities. This is expected, in this particular case, because most of the customer’s inbound communications is still via fax.

To locate attachments that are e-mail related, use this script:

    COUNT(Mimetype) AS Count,
    SUM(CAST(FileSize / (1024)AS DECIMAL(12,2))) AS 'Size (KB)',
    CAST(SUM(CAST(FileSize AS DECIMAL)) / (1024*1024) AS DECIMAL(12,2)) AS 'Size (MB)'
    MimeType IS NOT NULL

Which produces the following results:


To see where these attachments reside, run this script:

    COUNT(ActivityAttachment.objecttypecode) as Count
    join MetadataSchema.Entity on 
        MetadataSchema.Entity.ObjectTypeCode = ActivityAttachment.ObjectTypeCode 
group by
order by


Which produces these results: