CRM 2011 Grid with Preview Form

A sample of this html file can be downloaded from my Skydrive here http://sdrv.ms/RbwsKr

 

 

This week I was asked about showing an associated grid view and preview form within a Silverlight application similar to the way Outlook has a grid/preview layout. There are a couple of ways to do this and the one most obvious is to use the Silverlight controls and simply bind the CRM data to them. One alternative I pondered was to reuse the existing CRM Grid and CRM Form in IFrames on a HTML Page. This page could then be loaded into CRM as a Web Resource and opened by a Silverlight application or linked to in SiteMap, etc…

The main advantage to this approach is that any changes to the CRM form fields or grid columns would not require any change to be implemented to this html page, where as using a full Silverlight app you would have to update the application aswell. I’m going to show you the pieces of the prototype I built and hopefully you may be able to take this further for your own benefit. Here is a screenshot of the layout of the html page.

The html body consists of 2 iframes, one for the grid and the other for the form. In this example the grid is loading the Contact’s Activities associated grid view.

<body onload="Initialise()">

</body>

I have a number of constants for this prototype demo but some of these you will want to determine from a querystring or ClientGlobalContext.js.aspx file,

// You should collect the the server address and org values using the ClientGlobalContext.js.aspx file <a href="http://msdn.microsoft.com/en-us/library/gg328541.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/gg328541.aspx</a>
var server = "http://crm:5555"; // server url with port, if port required
var orgName = "Playground";

//  grid related settings
var entityId = "<A Guid Value goes here>"; // e.g. 00000000-0000-0000-0000-00000000000
var entityType = "2"; // Entity type for Contact
var areaView = "areaActivities"; // Activities associated view id found in Left hand Nav Pane by viewing DOM
//var areaView = "areaOpps"; // Opportunities associated view id found in Left hand Nav Pane by viewing DOM
var internalGridElement = "crmGrid_Contact_ActivityPointers"; // Activity Grid found in DOM
//var internalGridElement = "crmGrid_opportunity_customer_contacts"; // Opportunity Grid found in DOM

The body onload simply calls Initialise which sets the uxGrid iframe.

// Initialise page, set grid iframe
function Initialise() {
    document.getElementById("uxGrid").src = server + "/" + orgName + "/userdefined/areas.aspx?oId=%7b" + entityId + "%7d&oType=" + entityType + "&security=000000&tabSet=" + areaView;
}

When the grid loaded and when I selected a new view on the grid I received the ‘Error on Page’ and after tracing through the script I found that I could simply provide some stubs which handle the calls executed by the grid. I return null and I haven’t seen any functional issues arise from this during my testing.

// Stubs to handle grid in an IFRAME, errors occur otherwise
var Mscrm = new Object();
Mscrm.PageManager = new Object();
Mscrm.PageManager.get_instance = function() { return null; }

var crmRibbonManager = null;

$find = function(e) {
    return null;
}
// End Stubs

The following functions provide the functionality for the form preview to load. LoadPreviewPane is called when you single click a row in the grid. CleanFormWindow removes the form ribbon, left hand nav pane, footer and also disables all fields on the form.

// Show form in preview area
function LoadPreviewPane(entity) {
if (entity != null) {
document.getElementById("uxPreview").style.visibility = "hidden";
document.getElementById("uxPreview").src = server + "/" + orgName + "/main.aspx?etn=" + entity.TypeName + "&extraqs=" + escape("&id=" + entity.Id) + "&pagetype=entityrecord";
}
else {
document.getElementById("uxPreview").src = "about:blank";
}
}

// Remove chrome from around form in preview area
function CleanFormWindow() {
    if (event.srcElement.readyState == "complete") {
        var frameDoc = document.getElementById("uxPreview").contentWindow.document;

        if (frameDoc && frameDoc.getElementById("contentIFrame")) {
if(frameDoc.getElementById("perceivedRibbonId")) frameDoc.getElementById("perceivedRibbonId").style.display = "none";
frameDoc.getElementById("crmTopBar").style.display = "none";
 frameDoc.getElementById("crmContentPanel").style.top = "0px"; // Move Form Content area up to top of window, initial style.top is 135px
 frameDoc.getElementById("crmContentPanel").style.height = "100%";
 frameDoc.getElementById("contentIFrame").style.height = "100%";

 frameDoc.getElementById("contentIFrame").onreadystatechange = function() {

 if (frameDoc.getElementById("contentIFrame").readyState == "complete") {
 try {
 // Hide Left Hand Nav bar / pane
 frameDoc.getElementById("contentIFrame").contentWindow.document.getElementById("crmNavBar").parentElement.style.display = "none";
 frameDoc.getElementById("contentIFrame").contentWindow.document.getElementById("tdAreas").parentElement.parentElement.parentElement.parentElement.colSpan = 2;
 frameDoc.getElementById("contentIFrame").contentWindow.document.getElementById("tdAreas").parentElement.parentElement.parentElement.parentElement.style.height = "100%";

 // Hide the Breadcrumb and Record Set Toolbar
 frameDoc.getElementById("contentIFrame").contentWindow.document.getElementById("recordSetToolBar").parentElement.style.display = "none";

 // Hide the Form Footer Bar
 frameDoc.getElementById("contentIFrame").contentWindow.document.getElementById("crmFormFooter").parentElement.style.display = "none";

 // make everything readonly on the form using Xrm.Page controls
 frameDoc.getElementById("contentIFrame").contentWindow.Xrm.Page.ui.controls.forEach(SetControlDisabled);

 // Everything is finished so show the form
 document.getElementById("uxPreview").style.visibility = "visible";
 } catch (ex)
 { }
 }
 }
 }
 }
}

// Delegate function for disabling control in form control collection
function SetControlDisabled(control,index)
{
control.setDisabled(true);
}

The AttachGridEvent function attaches the GridClick function to the onselectionchange event of the internalGridElement defined in the constants earlier. A line of code sets the grid to single row selection to prevent a user from selecting multiple rows.

function AttachGridEvent() {

if (document.getElementById("uxGrid").readyState == "complete") {
var frameDoc = document.getElementById("uxGrid").contentWindow.document;
if (frameDoc.getElementById(internalGridElement) != null) {
frameDoc.getElementById(internalGridElement).attachEvent("onselectionchange", GridClick);
// Set grid to single select
if(frameDoc.getElementById(internalGridElement).document.getElementById("max"))
frameDoc.getElementById(internalGridElement).document.getElementById("max").value = 1;
}
}
}
// GridClick function for onselectionChanged event
var bFired = false;
function GridClick() {
selectedItems = new Array();

var grid = null;

//    get array of selected records
var frameDoc = document.getElementById("uxGrid").contentWindow.document;

if (bFired == false) {

if (frameDoc.getElementById(internalGridElement)) {
grid = frameDoc.getElementById(internalGridElement).control;
if (grid.get_selectedRecordCount() > 0) {
var records = grid.get_selectedRecords();
// record object fields
// Id
// Name
// TypeCode
// TypeName
LoadPreviewPane(records[0]);
}
else {
LoadPreviewPane(null);
}
}
bFired = true;
}
else {
bFired = false;
}
}

Hopefully you are able to reconstruct a html page from these examples and remember you must import the html page as a Web Resource otherwise you will end up with a cross domain error.

About these ads

About Rhett Clinton MVP
Dynamics CRM MVP

8 Responses to CRM 2011 Grid with Preview Form

  1. Dean Terry says:

    Hi,
    Would it be possible to make this solutions available for download

  2. Joeri Blootacker says:

    Hi Rhet,

    this is a awesome post !
    i have been looking for something like this for a very long time now, and thanks to your post i could do it.
    i’m wondering if it would be possible to make the used view selectable or to have it use a personalised view ?

    entityid and entitytype i get dynamically now, so it doesn’t really matter where i am, it always gets the related records now.
    however having the option to use a personalised view, rather than getting it from the left hand nav pane would really make it fully dynamic across all entities.

    i might give it a try and if i find it i will send you the adapted code.
    if you want to give it a try, please let me know.

    Regards
    Joeri

  3. LM says:

    HI,

    Great post !
    I tested this solution, but the standard entity ribbon (export, edit, send direct email…) disappears.
    Do you have the same problem?

    Regards,
    LM

  4. Uday says:

    can someone help in configuring to the server please??? Am using CRM 2011 On premise.

  5. Geert says:

    The onselectionchange is not working for rollup 12 & 13 (cross browser functionality).
    Any fix for this?

  6. Nathaniel MacArthur says:

    Any idea how to get this to work for Chrome Version 31.0.1650.63m? All the code works for IE10; none of it works in Chrome.

    Also, I’d like to hide all the ribbon areas above the contentIFrame except for the Save section. Any idea why the crmTopBar seems to be accessible only as a text node?

    • Hi Nathaniel, this was for a specific IE purpose long before cross browser Dynamics CRM was available. It would require reworking the code to get it to work cross browser, potentially a big task if you are up for it.

      Regards,
      Rhett

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: