Skip to main content

Sample Win 8 App: CRM 2011 Metro Style Contacts


I just got back from BUILD and I am very excited about the new possibilities of Windows 8 Metro style apps.  I was very eager to get my feet wet and see how easy it would be to pull records from CRM 2011 using HTML 5 and JavaScript and here is the result.

Disclaimer:  This is just a sample application and is not intended as a full working app.  This sample will NOT work with CRM Online and requires the device to have access to the on-premise CRM.  When the app loads it will prompt for credentials to the CRM organization.  To get around these limitations, a proxy service would need to be built to interface with CRM.

Notes:

  • Windows 8 Developer Preview and Visual Studio 11 Express for Developer Preview were used to create this Windows 8 app (http://msdn.microsoft.com/en-us/windows/apps/br229516).  
  • The source code for this solution can be found at http://crm2011metrocontacts.codeplex.com/.
  • This application will pull from the fullname, jobtitle, address1_line1, address1_city, address1_stateorprovince, address1_postalcode, address1_telephone1, and new_imageurl fields from the Contact record


First I decided to create a custom string attribute on the Contact entity called Image URL to populate the Metro style tiles with an image of the Contact:



Next I went into Visual Studio 11 Express and used the Grid Application template:



On top of the default JavaScript included in the Grid Application template, I added oDataMethods.js and jquery_1.4.1_min.js.  oDataMethods.js contains some helper methods to work with the CRM API and it also depends on jQuery.  You can find these files in the source code on CodePlex.  Once you add oDataMethods.js, you will want to update the serverUrl at the top of the file to point to your CRM Server:

var serverUrl = 'http://server/org',

At this point you can now run the app.  Here is what my app looks like with some test data set up:



And if you 'touch' a Contact tile:



Finally, here is the Contact data used for this demo:



Now to get into the code.  For the most part I left the Grid Application template alone but I will go into some depth about the changes I did make.  In landingPage.html I referenced the two scripts I added:

    <script type="ms-deferred/javascript" src="/js/jquery_1.4.1_min.js"></script>
    <script type="ms-deferred/javascript" src="/js/oDataMethods.js"></script>

The landingPage.html template just used a background color for the tiles which I switched to an image that populates from the Contact record.

    <div class="largeTileTextTemplateBackground">
       <img class="largeTileTextTemplateBackground" data-win-bind="src: largeImagePath"></img>
    </div>
    <div class="largeTileTextTemplateOverlay">
       <div class="largeTileTextTemplateLargeText" data-win-bind="textContent: title"></div>
       <div class="largeTileTextTemplateSmallText" data-win-bind="textContent: subtitle"></div>
       <div class="largeTileTextTemplateSmallText" data-win-bind="textContent: location"></div>
    </div>

By default in the Grid Application template, it uses the new data binding technique which you can learn about in this session by Chris Tavares around 35:00 -
http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-501T

Next I updated the getGroups method in landingPage.js to just return a hardcode Contacts group instead of random fake data that the template uses.

     groups.push({
       key: 'Contact',
       title: 'Contacts',
       label: 'Get Started with Contacts',
       description: 'A contact is a person you do business with. Contacts are usually associated with accounts.',
       fullDescription: '',
     });

Then I updated the getItems method to retrieve all the active contacts in my CRM organization.

First I use the template code that loops through all the groups (which is now just our hardcoded Contacts group).  Then I use oDataMethods.js to retrieve all the active contacts.  For each contact I add an item object to the items array.  The items array is what is bound to the landingPage.html which will use the properties to display in the list view.  When a contact is 'touched', it passes the specific contact object into the detailsPage.html which binds the largeImagePath and content property to display the specific information about that contact.  toStaticHTML()  is used to strip out any scripts that might be contained in the HTML to prevent malicious attacks, otherwise the app will not display the HTML.

  for (var g = 0, gl = pageData.groups.length; g < gl; g++) {
       var filter = "?$filter=StateCode/Value eq 0";
       OData.retrieveMultiple(pageData.groups[g].key + "Set", filter,
       {
              async: false,
              success: function (data, textStatus, XmlHttpRequest) {
              for (var i = 0; i < data.length; i++) {
                     if (pageData.groups[g].key == "Contact") {
                           items.push({
                                  group: pageData.groups[g],
                                  key: data[i].ContactId,
                                  title: data[i].FullName,
                                  subtitle: data[i].JobTitle,
                                  location: data[i].Address1_City + ", " + data[i].Address1_StateOrProvince,
                                  largeImagePath: data[i].new_ImageURL,
                                  content: toStaticHTML(
                                         "<table><tr><td>Fullname:</td><td>" + data[i].FullName + "</td></tr>" +
                                         "<tr><td>Job Title:</td><td>" + data[i].JobTitle + "</td></tr>" +
                                         "<tr><td>Address:</td><td>" + data[i].Address1_Line1 + "</td></tr>" +
                                         "<tr><td>City:</td><td>" + data[i].Address1_City + "</td></tr>" +
                                         "<tr><td>State:</td><td>" + data[i].Address1_StateOrProvince + "</td></tr>" +
                                         "<tr><td>Zip:</td><td>" + data[i].Address1_PostalCode + "</td></tr>" +
                                         "<tr><td>Telephone:</td><td>" + data[i].Address1_Telephone1 + "</td></tr></table>"),
                                   description: ''
                            });
                     }                      
              }
}
       });
  }

So there you have it.  Although this demo is pretty simple, it only scratches the surface of the possibilities of interacting with CRM 2011 in a Windows 8 app.  I'm excited to continue to learn about Windows 8 development and uncover more of those possibilities!

Comments

  1. hi,i hava some trouble with this demo.
    The console error:Unable to add dynamic content ' A script attempted to inject dynamic content, or elements previously modified dynamically, that might be unsafe. For example, using the innerHTML property to add script or malformed HTML will generate this exception. Use the toStaticHTML method to filter dynamic content, or explicitly create elements and attributes with a method such as createElement.

    ReplyDelete

Post a Comment

Popular posts from this blog

Announcing the New Dynamics 365 Toolbot Chrome Extension!

Today I am excited to announce the new Dynamics 365 Toolbot! This new Chrome Extension will allow you to perform commands that will help you with your development or administrative tasks. The extension can be found here - https://chrome.google.com/webstore/detail/dynamics-365-toolbot/kljiiminicfmdlplhejocopfmgmipach.

Note: Currently, due to the APIs being used, it only works on v9+.

First, navigate to the link above using Chrome and install the extension. Then, head to your Dynamics 365 environment and open a record. From there, click the little blue robot icon in the toolbar of Chrome which will pop open the Toolbot.



Click the text box and a list of commands will display. You can select or type any of the commands and hit Submit to execute them. Some commands require you to replace the default token with the desired request.


For example, select or type "display id" and click Submit and the Toolbot will display the current record's ID.



The majority of the commands should …

CRM 2011 LINQ - All Columns vs. Selected Columns

When creating LINQ queries in CRM 2011, it is easy enough to return the whole column set of the entity record without even thinking about the impact.  
Below is an example of querying all contacts from Chicago and returning all columns for each contact record.


This can be a big performance impact depending on the amount of columns that exist on the Contact entity and how many contact records exist in the system.  Another issue is that it could cause errors down the line if some of the attribute types are changed in the CRM system since the data is being bound to a model class that could be out-of-sync.
From the MSDN article on constructing LINQ queries (http://msdn.microsoft.com/en-us/library/gg328328.aspx), we can see that the select clause creates a column set:
The select clause defines the form of the data returned. The clause creates a column set based on the query expression results. You can also define an instance of a new object to work with. The newly created object using the…

CRM 2011 Form XML

Retrieving and parsing the Form XML in CRM 2011 is pretty easy.  If you ever had to do this in CRM 4.0 you would know that the Form XML was stored in the OrganizationUI table.  In CRM 2011 it is now stored in the SystemForm table.  
So first things first, we need to build a query to retrieve the Form XML for an entity from the SystemForm table.

In my case, I just have the entity's name so I need to retrieve the entity metadata and find it's type code to use to filter the query.  I also filter the SystemForm Type by 2 which means it is the Main form (http://msdn.microsoft.com/en-us/library/gg509016.aspx).
Now that we have my entity's Form XML.  We can build a method to find all the attributes' schema names that exist on the form.  This method just parses the XML string into an XElement and then selects all the id's of the "control" nodes that exist in the Form XML.  This array of id's will be all the schema names of the attributes that exist on the for…