Wednesday, February 1, 2012

Pass Custom Parameters to an entity form through a URL

In your applications, you may want to pass custom query string parameters to an entity form.
Let me describe the way in which we can define a set of specific parameter names and data types that can be accepted for a specific entity form.

Define Allowed Query String Parameters:
There are two ways to specify which query string parameters will be accepted by the form:
• Edit form properties
• Edit form XML

Edit Form Properties:
When you edit an entity form, on the Home tab in the Form group, click Form Properties. In the Form Properties dialog box, select the Parameters tab. Use this tab to modify the names and data types that the form allows as given in the below screen shot.


The following describes the querystringparameter element attributes, name and type:Name: Each name attribute must contain at least one underscore ('_') character, but the name of the query string parameter cannot begin with an underscore. The name also cannot start with 'crm_'. We strongly recommend that you use the customization prefix of the solution publisher as the naming convention. If you don’t follow the naming conventions the alert message as given in the below screen shot will be shown to the user.


Type: Following types of parameters can be passed to the form. Match the data type values with the parameter values so that invalid data is not passed with the parameter. The following are valid data types:
• Boolean
• DateTime
• Double
• EntityType
• Integer
• Long
• PositiveInteger
• SafeString
• UniqueId
• UnsignedInt

Let me define the parameters for the form and access them through Query string parameters.

- We have created two parameters for the Account Form as given in the below screen shot.


- We have opened the form through the below given script with parameters appended to the URL.
var extraqs = "Parameter_Source=Hello";
extraqs += "parameter_Source2=8";
//Set features for how the window will appear.
var features = "location=no,menubar=no,status=no,toolbar=no";
// Open the window.
window.open(Xrm.Page.context.getServerUrl() +"/main.aspx?etn=account&pagetype=entityrecord&extraqs=" + encodeURIComponent(extraqs), "_blank", features, false);

- After that we have checked it through the query string parameters.
// Get the Query String Parametrs passedthrough the URL
var param=context.getQueryStringParameters();
// Get the Value of the Source through the Key Parameter Name
var source=param["Parameter_Source"];
var source2=param["parameter_Source2"];
// if Source is Undefined i.e. the URL Doesn't contain the key
if (source==undefined){ return; }
else{ // Perform the operation
}

This will be helpful in the scenario suppose if are opening the Form through the different sources such as Silverlight, report etc. and on the form load you want to perform some operation based on the source then you can define parameter for the form and can pass the source name in the extraqs of the URL as mentioned above and can check the source name on the form load and can perform the operation accordingly.

Monday, January 23, 2012

Update the Parent Silverlight Grid from the CRM form that is spawns.

We were working on the Silverlight REST Editor sample that is available in SDK.


We added a hyperlink to that grid that would open the CRM Contact form for the selected contact. This would allow users to check in further details of the contact. The users can also update the fields on the contact form there. Say the user edits the Phone on the Contact form instead of in the editable grid that we have presented them with… (users always tend to do what they are not supposed to do )

Now the issue raised was… after closing the CRM contact form, the changes made on the contact were not reflected in the grid. The user did not want to perform the search again to check the updated values…

How do we fix this…

1. Modify the Silverlight controls class and qualify it with the [ScriptableType] attribute.

[ScriptableType]
public partial class MainPage : UserControl
{

public MainPage( )
{
InitializeComponent( );

HtmlPage.RegisterScriptableObject("MainPage", this);

}

Register the control for access through script.

2. Next create qualify a method to be [ScriptableMember]

[ScriptableMember]
public void RefreshContactGrid( )
{
//Here write the code to rebind the Grid
}

Now we can access this method through jscript.

3. In the HTML page add a function to call the RefreshContractGrid method

function reloadContactGrid( ) {

try {
var control = document.getElementById("silverlightControl");
control.Content.MainPage.RefreshContactGrid( );
}
catch (e) {
alert(e.Description );
}
}

Silverlightcontrol refers to the object id of the silverlight control on the HTML page.
<object id="silverlightControl" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

And “MainPageis the class name, which we have registered as Scriptable Object.

4. As we would like the grid to be auto-refreshed on the close of the CRM form. We need to write the code on the CRM form OnSave event to call the reloadContactGrid function of the HTML page. This function internally calls the Silverlight method that actually binds the grid once again and refreshes the datagrid.

function reloadParentGrid( ) {
try {

if (window.parent != null
&& window.parent.opener != null
&& window.parent.opener.document != null
&& window.parent.opener.document.nameProp != null
&& window.parent.opener.document.nameProp == "RESTContactEditor") {

window.parent.opener.reloadContactGrid( );

} //End of IF

} catch (e) { }
}

With these 4 steps you should be able to auto-refresh the grid after the user saves the CRM form that they opened from within the grid.


Wednesday, January 18, 2012

Dynamically control Form Navigation

CRM 2011 introduced the feature to design more than one form for an entity and assign form to users based on their role (Role Based Form). It has also included a feature where by it would automatically open the form that was last used by the logged in user for that entity. If you would like the entity to dynamically load a particular form you can use the following piece of code

Xrm.Page.ui.formSelector.items.get(formId).navigate( );

Formid – Guid of the form that you would like to navigate to.

You can add this code on the Load event of the form. But make sure you compare the Current form unique id with the id of the form you wish to navigate to. Failure to do this would result in an infinite loop of form navigation.

Var CurrentFormId = Xrm.Page.ui.formSelector.getCurrentItem( ).getId( );

if( formId!='' && CurrentFormId !='' && formId!=CurrentFormId )
{
Xrm.Page.ui.formSelector.items.get(formId).navigate( );
}

While at this, you can use the following code to get a list of all the forms that the user has access to for the current entity

var items = Xrm.Page.ui.formSelector.items.get( );
for (var i in items)
{
var item = items[i];
// Get the Id of the Form
var itemId = item.getId( );

// Get the Label of the Form
var itemLabel = item.getLabel( );
}


Note: Since this code is written in the Onload event, the form is loaded before the script is executed and the user is navigated to the requested form. It would be a good idea to hide the controls on the form and show them in the Load Event.

Tuesday, January 10, 2012

Tip - Hide fields on CRM form without messing up the form layout

Generally if you hide a field through script, CRM does not redesign the layout to use up the blank space left from the hiding of a field on the form. This makes it very obvious that there must have been some control that is now not visible to the user. To avoid this you can make use of the CRM 2011 ability to add the same field multiple times on a form.

Say for example you would like to hide the Estimate Revenue field from the form and make it available conditionally.



Simple script to hide the field would display the form as shown below.





To make sure that such empty spaces are not visible on the form, you can instead create 2 sections on the form, one with the Est. Revenue field and the other without the field.





Through script you can then show/hide the entire section. This way the form layout is not affected.


Friday, January 6, 2012

Read Form Id or Form XML of a specified entity form

CRM 2011 now allows multiple forms to be designed for an entity and based on the security roles display the appropriate form.

During the development we once came across the need to get the Formid for each of the forms created for an entity. We did not want to hard code the formid in the ribbon button as the id’s may change between test and production environments.

We can programmatically retrieve the FormId of an entity through Jscript.

The schema name for the entity that stores the information about the various forms is SystemForm. The code below retrieves the id for the specified form based on the form name.


function RetrieveGetFormIDRecord(formName)
{
var cols="";
try
{
var xml = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>"+
" <soapenv:Body>"+
" <RetrieveMultiple xmlns='http://schemas.microsoft.com/xrm/2011/Contracts/Services' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>"+
" <query i:type='a:QueryExpression' xmlns:a='http://schemas.microsoft.com/xrm/2011/Contracts'>"+
" <a:ColumnSet>"+
" <a:AllColumns>false</a:AllColumns>"+
" <a:Columns xmlns:b='http://schemas.microsoft.com/2003/10/Serialization/Arrays'>"+
" <b:string>formid</b:string>"+
" </a:Columns>"+
" </a:ColumnSet>"+
" <a:Criteria>"+
" <a:Conditions>"+
" <a:ConditionExpression>"+
" <a:AttributeName>name</a:AttributeName>"+
" <a:Operator>Equal</a:Operator>"+
" <a:Values xmlns:b='http://schemas.microsoft.com/2003/10/Serialization/Arrays'>"+
" <b:anyType i:type='c:string' xmlns:c='http://www.w3.org/2001/XMLSchema'>"+formName+"</b:anyType>"+
" </a:Values>"+
" </a:ConditionExpression>"+
" </a:Conditions>"+
" <a:FilterOperator>And</a:FilterOperator>"+
" <a:Filters />"+
" </a:Criteria>"+
" <a:Distinct>false</a:Distinct>"+
" <a:EntityName>systemform</a:EntityName>"+
" <a:LinkEntities />"+
" <a:Orders />"+
" <a:PageInfo>"+
" <a:Count>0</a:Count>"+
" <a:PageNumber>0</a:PageNumber>"+
" <a:PagingCookie i:nil='true' />"+
" <a:ReturnTotalRecordCount>true</a:ReturnTotalRecordCount>"+
" </a:PageInfo>"+
" <a:NoLock>false</a:NoLock>"+
" </query>"+
" </RetrieveMultiple>"+
" </soapenv:Body>"+
"</soapenv:Envelope>";


// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", Xrm.Page.context.getServerUrl() + "/XRMServices/2011/organization.svc/web", false);
xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/RetrieveMultiple");
xHReq.setRequestHeader("Content-type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);

// Capture the result.
var resultXml = xHReq.responseXML;

var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0)
{
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert(msg);
return;
}

// Parse and display the results.
var results = resultXml.getElementsByTagName('a:Entity');
//if only one zip code records found
if (results.length != 0 )
{
//alert("Lenght: "+results.length);
var attribs = results[0].getElementsByTagName('a:Attributes/a:KeyValuePairOfstringanyType');
if (attribs.length == 0)
{
alert("No data found in enitty.");
return;
}

if(attribs[0].selectSingleNode('./b:key').nodeTypedValue =="formid" )
{
//set the field values
var value= attribs[0].selectSingleNode('./b:value').nodeTypedValue;
alert("FormID:"+value);
}
}

}//end of try
catch(e)
{
alert("RetrieveGetFormIDRecordErr: "+e.description);
}
}//end of RetrieveGetFormIDRecord

</Snip>

In the above code, we just need to pass the form name and it will return the form id. In the above query we can add the condition to filter by an entity like account forms as well by setting the “objecttypecode”.

Once you have the formid you can open the form using the following url

var accountPage= Xrm.Page.context.getServerUrl()+ "/main.aspx?etn=account&extraqs=formid%3D"+value+"%0D%0A&pagetype=entityrecord";
window.open(accountPage);