Silverlight + XRM Book by David Yack MVP

Building the next generation of business applications using Microsoft Silverlight and Dynamics CRM 2011

Fellow MVP David Yack has produced the perfect guide to Silverlight with Dynamics CRM 2011. I highly recommend this book for people new to Silverlight or Dynamics CRM but I also think  those already working with either Silverlight or Dynamics CRM should check it out. I’ve been reading through it this past week and have been learning key things that will help during the design and implementation of my latest projects. I wish I had this book earlier, as my CRM tools on Codeplex could have really benefited from the details in this book too. It shows the knowledge and understanding David has of Microsoft’s latest incarnation of CRM along with Silverlight and how to integrate them.

David Yack MVP is the CTO of Colorado Technology Consultants based in Colorado. He is a Microsoft Regional Director and a Microsoft MVP for Silverlight. This guys knows his stuff…

For an overview of the book’s chapters check it out here, and here is a quick summary of chapter titles.

Chapter 1 – Using Silverlight with CRM 2011 
Chapter 2 – Getting Started
Chapter 3 – XAML 101 and Basic Layout
Chapter 4 – Silverlight Controls
Chapter 5 –Data Binding Basics
Chapter 6 – Application Navigation
Chapter 7 – Out of Browser Support
Chapter 8 – Application Composition with MEF
Chapter 9 – Other Silverlight Business Application Features
Chapter 10 – Enhancing the User Experience
Chapter 11 – Discovering SketchFlow
Chapter 12 –Interacting with CRM Form
Chapter 13 –OData Basics
Chapter 14 –OData Beyond the Basics
Chapter 15 – Using the WCF Service
Chapter 16 – Application Architectures
Chapter 17 –Silverlight Debugging

There is now no excuse for not producing high quality Silverlight apps when developing for Dynamics CRM.

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.

CRM 2011 OData Query Designer

This project is superceded by the new application Dynamics XRM Tools which includes improved features http://dynamicsxrmtools.codeplex.com/

 

I’ve developed the CRM 2011 OData Query Designer as a Silverlight 4 application that is packaged as a Managed CRM 2011 Solution. This tool allows you to build OData queries by selecting filter criteria, select attributes and order by attributes. The tool also allows you to Execute the query and view the ATOM and JSON data returned.  Once the managed solution is imported into CRM 2011 it is accessible from the Settings area under Customizations .

You can download the CRM 2011 OData Query Designer  from codeplex.

[UPDATE]

Latest Update 8th March 2011
1. Added dropdown list of available OptionSet values for a filter when of type OptionSet, Boolean, Status and State.
2. Attribute lists now show the UserLocalizedLabel Display Name instead of Schema Name.
3. Resolved issues with some attribute types not generating correctly when used in a filter.

Has this Tool saved you time…

If you have benefited from downloading and using this tool, please feel free to donate an amount of your choice. Thankyou

GBP – Donate via PayPal - GBP>

USD – Donate via PayPal - USD>

Euro – Donate via PayPal - Euro>