One of the most typical “somewhat” unsupported customisations on Dynamics CRM is using IFrames to show associated views. This technique enables a nicer user interface showing more information about related records on the main entity form without having to click through the left hand side navigation panel.

I was doing some customisations the other day for a xRM demo we had with a customer, and I thought that it would be nice to have a two IFrames containing associated views but in a different way. Think on the typical Invoice and detail lines situation, wouldn’t it be nice to have all that information directly available in the customer form without having to navigate around to find it?

As usual, with some bits of JavaScript and imagination everything is possible on the Dynamics CRM UI, so let’s think on the basics behind this (full code at the end). First thing that we need is the ability to load on an IFrame the desired associated view for invoices instead of having it on the left hand side navigation panel. This is quite easy and there are plenty of blogs about around the internet, we basically need to find out the page that renders the view for that associated view. You could use the Internet Explorer Developer Toolbar (which is now integrated on  IE8) to inspect the page properties and find the URL for the I Frame containing the associated view.

image

This url is always going to be of the type:

http://crm/org /area/entity/areas.aspx?oId={...}&oType={...} &Security=...&tabSet=...

The most important parameters here are tabSet, which specifies the associated view to be loaded (usually the relationship name), and the oId, which specifies the foreign key value for the relationship. Security controls the rendering of the toolbar buttons and oType helps to identify the foreign key object type.

I said that tabSet normally refers to the relationship name, and this is true for all custom entities and works for most of the system ones. However, in some system entities there are special associated views (i.e. activity history, existing products, write-in products, related invoices, etc.) that apply some more restrictions than just the pure relationship associated view, or that include some filtering capabilities (via special drop down list and similar) on the associated view. In those cases you will find that the url tabSet name has some special names like “areaExistingProducts” instead of just the relationship name “invoice_details” or “areaInvoices” instead of “invoice_customer_accounts”.

In our case we are looking for the one to many Account to invoice relationship which is called “invoice_customer_accounts”, but instead of using the relationship name we are going to use the system area name “areaInvoices” because it provides more filtering capabilities than the pure associated view and it also shows the invoices regarding related records. So, the URL would be something like http://crm/org/sfa/account/areas.aspx?oId{...}&tabSet=areaInvoices&Security=..&oType=..

image

Then we need to repeat the process of finding out the URL but for the detail I Frame, which will be the the one to many relationship between Invoice and Invoice Detail. We could use the special name of the are like “areaExistingProducts” instead of the relationship here, but we will use the relationship name as we want in our master-detail view to have both, write-in and existing product types, available. Therefore for our master frame URL we will have something like http://crm/org/sfa/invoice/areas.aspx?oId{...}&tabSet=invoice_details&Security=..&oType=..

image

Now, that we have both view that we need to use for the master and the detail. How we link them? How we make that selecting a record on the master view grid changes the detail view to show the related detail items? The answer is JavaScript, and the full code is below. In short, we need to hook some JavaScript to the on click event of the master Iframe and the retrieve the select id from the grid to allow us to compose the url for the detail view. In this process we need to account for the delays on the loading of IFrame DOM objects and also for false click that had not selected any item as you can appreciate on the code.

////////////////// Master Detail View ///////////////////////////

///// SETUP ////////

var IframeMaster = "IFRAME_Invoices"; //IFrame name for the master view
var IframeDetail = "IFRAME_InvoiceDetails"; //IFrame name for the details view
var MasterUrl = "../../sfa/accts/areas.aspx"; //It has to be the full URL from the root element but using relative urls, thus the use of "..". For a custom entity "../UserDefined/areas.aspx";
var MasterTabSet ="areaInvoice"; //Area or relationship name for the master
var DetailUrl = "../../sfa/invoice/areas.aspx"; //Same conditions as the master Url
var DetailTabSet = "invoice_details"; //Area or relationship name for the deatil

////////////////////

var LastSelection = null;

function Initialise() {
//Init Master Iframe
document.frames(IframeMaster).frameElement.attachEvent("onreadystatechange", OnReadyStateChange);
document.frames(IframeMaster).frameElement.src = GetFrameSource(MasterUrl, MasterTabSet, crmForm.ObjectId, crmForm.ObjectTypeCode, crmFormSubmit.crmFormSubmitSecurity.value);
}

function OnChangeMaster() {


if (document.frames(IframeMaster).document.all['crmGrid'].InnerGrid.SelectedRecords.length > 0) {

var masterId = document.frames(IframeMaster).document.all['crmGrid'].InnerGrid.SelectedRecords[0][0];
var masterTypeCode = document.frames(IframeMaster).document.all['crmGrid'].InnerGrid.SelectedRecords[0][1];
//Update detail view
if (masterId != LastSelection) {
document.frames(IframeDetail).frameElement.src = GetFrameSource(DetailUrl, DetailTabSet, masterId, masterTypeCode, crmFormSubmit.crmFormSubmitSecurity.value);
LastSelection = masterId;
}
}
}

function OnReadyStateChange() {
if( document.frames(IframeMaster).frameElement.readyState == 'complete' ) {
document.frames(IframeMaster).document.all['crmGrid'].attachEvent("onclick", OnChangeMaster);
OnChangeMaster();
}
}

function GetFrameSource(url, tabSet, oId, oType, security) {
var result = url + "?oId=" + oId + "&oType=" + oType + "&security=" + security + "&tabSet=" + tabSet;
return result;
}

Initialise();

 

 

 

Here is the final look. Remember that you need to create two IFrames on the form to host the master and the detail views, the Tab where you choose to put them doesn’t matter.

Selecting Invoice 1:

image

Selecting Invoice 2:

image

Hope it helps! If you find it useful, leave me a comment :o)

Marco Amoedo