{Dynamics 365/ CRM forms} Programmatically access controls from a system form of an entity in Dynamics CRM

It’s been sometime I have published a blog article as I am in neck deep work in some tool development. But still I could not resist myself from penning down this interesting stuff which I came across recently and share with all my readers.

Recently I came across a customer requirement where they needed to fetch all the controls in a tab in on a form for contact entity and show them in one of their external applications which is interacting with Dynamics CRM. I think an example here would explain the scenario.

I have a tab with display name – ‘DEMO TAB’ and name – ‘DEMO_TAB’ on the ‘Contact’ form of the contact entity.

image

The DEMO TAB has the following controls of the specified type

First Name Single line of text (type = simple)
Last Name Single line of text (type=simple)
Lead Source Optionset
Birthday DateTime
Originating Lead Lookup
Do not allow faxes two options
Credit Limit Currency

 

I haven’t taken all the data types and surely at the end of this blog you will get an idea on how to proceed with other data types as well.

So let’s see what are the  steps here to achieve the customer requirement.

  • Query the system form entity for contact entity with form name = ‘Contact’
  • Get the formxml
  • Parse the formxml to get all the controls within the DEMO TAB of the form
  • Finally render the controls in the custom application as per the control type. So First name should be rendered as textbox, Birthday probably as DatePicker control and Lead Source as dropdown.

For readers who are aware of ADX portals and how they work, you might have a question coming, isn’t is available OOB? Well it is and ADX is much more than this. We tried to put our point through for it but this is one off requirement for the customer and they have been using their custom application for pretty long time and hence did not want to discontinue the same.

Well, so we are back to our custom development.

Let’s complete our requirement step by step. So lets first write the piece of code to get the controls within the ‘DEMO TAB’ of the ‘CONTACT’ form of contact entity.

 

var systemForm = GetFormByName("contact", 2, proxy);
var controls = GetTabControls("DEMO_TAB", systemForm.GetAttributeValue<string>("formxml"), proxy);

private static Entity GetFormByName(string formName, int entityTypeCode, IOrganizationService service)
        {
            var query = new QueryExpression("systemform");
            query.Criteria.AddCondition("name", ConditionOperator.Equal, formName);
            query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, entityTypeCode);
            query.ColumnSet.AddColumn("formxml");

            var results = service.RetrieveMultiple(query);

            return results.Entities[0];
        }

        private static List<XElement> GetTabControls(string tabName, string formXml, IOrganizationService service)
        {
            var controlsList = new List<XElement>();
            var formDoc = XDocument.Parse(formXml);
            var tabs = formDoc.Descendants("tab");

            foreach (var tab in tabs)
            {
                if(tab.Attribute("name").Value.Equals(tabName, StringComparison.OrdinalIgnoreCase))
                {
                    // get all the controls.
                    controlsList = tab.Descendants("control").ToList();
                    break;
                }
            }

            return controlsList;
        }

 

The first method retrieves the form for the contact entity and the second method extracts all the controls within the ‘DEMO_TAB’.

I have used System.Xml.Linq and believe me, it is much cleaner way to parse your xml than using the traditional System.Xml.Xmldocument methods.

So we now have the controls. Now how to get the field name and the type of the control? To explore this, lets see what the form xml looks like. I have pasted a portion of the tab xml

<tab name="DEMO_TAB" id="{596d8090-3b20-77ad-3c36-77442a42835c}" IsUserDefined="0" locklevel="0" showlabel="true" expanded="true">
            <labels>
                <label description="DEMO TAB" languagecode="1033" />
            </labels>
            <columns>
                <colum
n width="100%">
                    <sections>
                        <section name="tab_4_section_1" showlabel="false" showbar="false" locklevel="0" id="{a64c108b-c1d1-1d90-ebd0-e2c4ab7ced3d}" IsUserDefined="0" layout="varwidth" columns="11" labelwidth="115" celllabelalignment="Left" celllabelposition="Left">
                            <labels>
                                <label description="Section" languagecode="1033" />
                            </labels>
                            <rows>
                                <row>
                                    <cell id="{2b74bc94-1e03-4ab7-428e-8283732c57ca}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="First Name" languagecode="1033" />
                                        </labels>
                                        <control id="firstname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="firstname" disabled="false" />
                                    </cell>
                                    <cell id="{b6d61c95-0c1d-4cae-2e7a-1210e4d7a1cd}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Lead Source" languagecode="1033" />
                                        </labels>
                                        <control id="leadsourcecode" classid="{3EF39988-22BB-4f0b-BBBE-64B5A3748AEE}" datafieldname="leadsourcecode" disabled="false" />
                                    </cell>
                                </row>
                                <row>
                                    <cell id="{b06bd53b-c215-d372-0265-e3da223d9bd5}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Last Name" languagecode="1033" />
                                        </labels>
                                        <control id="lastname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="lastname" disabled="false" />
                  
                  </cell>
                                    <cell id="{6780cace-3301-540e-0425-543755db1a18}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Originating Lead" languagecode="1033" />
                                        </labels>
                                        <control id="originatingleadid" classid="{270BD3DB-D9AF-4782-9025-509E298DEC0A}" datafieldname="originatingleadid" disabled="false" />
                                    </cell>
                                </row>
                                <row>

See for the section highlighted in yellow. Let’s see what we can get from there.

  • description – the display name for the field
  • datafieldname – the logical name of the field
  • disabled – value to indicate whether the field is enabled or disabled on the form.

So far so good. We have all the necessary information. But wait? How do we get the type of the field and decide whether to render it as textbox or dropdown or datepicker?

You have two methods here. One is to query the metadata based on the field name (datafieldname) and then determine the type and render the control accordingly. If you ask me, this is probably the best way to determine to do since it would be independent of any version changes or upgrades in CRM.

But one disadvantage is the amount of time it takes to query the metadata is significant and your page rendering time could go for a toss depending on how many controls you are querying for.

The other and probably faster way to do this is to use the classid attribute of the control. Check for the stuff highlighted in green. If you observe carefully both first name and last name have the same data type and their classid is same as well. It’s more like each control type being identified by a separated id. However this method has major caveats

  • You would need to identify the classid of all type and probably use them in your code, perhaps as switch-case.
  • If for some reason, Microsoft changes the classid of the control types, your code would go for a toss.
  • Finally, with new releases, Dynamics is coming up with new field types and hence you need to keep your code changing for the new types as they come.

Wondering how to get the attributes of the controls. Well below is the code to help you on that.

foreach (var control in controls)
            {
                var isDisabled = control.Attribute("disable").Value;
                var classId = control.Attribute("classid").Value;
                var datafieldName = control.Attribute("datafieldname").Value

                // parse the other attributes
            }

 

Isn’t it easy with System.Xml.Linq?

 

Hope you find this useful.