Monday, August 18, 2014

Resizing 2011 entity forms

This is a small issue, and will be a non-issue once everyone move to 2013.
Until then if you need to control the size of your entity forms here is what you would expect to work.

//Resize to screen: 
xrmForm.FitToWindow();

// your SDK resource 
function XrmForm() {
     var xfrm = this;
     
  xfrm.ResizeTo = xfrm.FitToWindow = function () {
        window.resizeTo(arguments[0] || screen.availWidth, 
                        arguments[1] || screen.availHeight);
        return frm;
    }
}

var xrmForm = new XrmForm();


This would work in IE but won’t work in chrome. The reason for this is that chrome has strict rules regarding from where resizeTo works. Since you’re resizing the
window of an IFRAME (edit.aspx – this is where you script runs) and not the main window it won’t resize.
To overcome this obstacle you need to resize the window at the top.

The following will work in all browsers.
//your SDK resource
function XrmForm() {
     var xfrm = this;
    //resize top window
  xfrm.ResizeTo = xfrm.FitToWindow = function () {
        window.top.resizeTo(arguments[0] || screen.availWidth, 
                            arguments[1] || screen.availHeight);
        return frm;
    }
}

var xrmForm = new XrmForm();
Changed window.resizeTo > window.top.resizeTo

Sample Usage
//your entity.js resource

//Resize to screen: 
xrmForm.FitToWindow();
//Resize to give width and height
xrmForm.ResizeTo(800, 600);


Of course in 2013 everything opens in the same browser window (tab) so again this would be a non-issue.
I personally don’t like not being able to choose how my apps behaves. I also here the same voices from others in my radius.
My thoughts are that the CRM team need to create different themes for different mediums and not impose the tablet/mobile look on office applications.

Cheers



Friday, August 15, 2014

Disabling the Lookup View Picker in CRM 2013


One of the shortcomings of lookup controls in CRM 2011 and 2013 is that you can’t disable the View Picker once you enter a new custom view. Although you can set it as default and put it first on the list, users can still select records related to other views.



When 2011 was introduces it seemed that this missing SDK ability was no more than a glitch.
Unfortunately it’s still an issue in 2013 and yet again we are required to find undocumented features to enforce this type of business rule.

Luckily both 2011 and 2013 has this feature built into the product. We just needed to find a way to activate it without hindering existing CRM functionality.

In order to handle all the lookup internals, supported and unsupported, I’ve created an XrmLookupField wrapper. The wrapper enables me to manipulate the control with ease and I suggest you do the same in your code.

There is one thing you need to remember about this feature. Once you disable the View Picker you can’t add custom views to it. You must enable the View Picker before you add new customs views and disable it again if you so require.

If you take a closer look at the AddLockedView method in the code sample you’ll notice that this is exactly what I’m doing before adding a new custom view.
i.e. EnableViewPicker(); > AddCustomView() > DisableViewPicker()

This is how we did it in 2011
Note: This XrmLookupField example only includes methods that are relevant to this post.

//SDK wrapper for lookup field 2011
function XrmLookupField(sId) {
    var xlf = this;
    //control instance
    xlf.Ctl = Xrm.Page.getContorl(sId);
    //dom instance
    xlf.Dom = document.getElementById(sId);
    //jquery instance
    xlf.$ = $(xlf.Dom);

    //use that to disable the view picker
    xlf.DisableViewPicker = function () { 
        xlf.SetParameter("disableViewPicker", "1"); 
    }
    //use that to enable the view picker
    xlf.EnableViewPicker = function () { 
        xlf.SetParameter("disableViewPicker", "0");  
    }
    //set undocumented attributes
    xlf.SetParameter = function (sName, vValue) { 
        xlf.$.attr(sName, vValue);  
    }   
    //add locked view
    xlf.AddLockedView = function (sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout) {
        //first enable the view picker
        xlf.EnableViewPicker();
        //add the custom view (last parameter set the view as default)
        xlf.Ctl.addCustomView(sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout, true);
        //lock the view picker
        xlf.DisableViewPicker();
    }
    //create new guid
    xlf.NewGuid = function () {
        var d = new Date().getTime();
        var guid = '{xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx}'.replace(/[xy]/g, function (c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
        });
        return guid;
    }
}


And here is a usage example. Note that I’m using the dynamic FetchXml builder to construct my Layouts and FetchXml queries.
You can read more about it here or use regular xml in your code.

//entity onload js
var myLookup1;

//call this function from OnLoad handler
function OnCrmPageLoad() {
    myLookup1 = new XrmLookupField("new_lookup1");
    myLookup1.AddLockedView(
        //sViewId
        myLookup1.NewGuid() ,
        //sEntityName
        "account",
        //sViewDisplayName
        "My Locked Custom View",
        //sFilterXml
        fetch()
            .entity("account")
                .attribute("name")
                    .filter()
                        .condition("name" , fetch.op.Eqaul, "My Company")
        .toString(),
        //sFilterLayout
        layout(1, "name", "accountid")
            .column("name", 200)
        .toString()
    );
}


CRM 2013 introduced some interesting changes. Controls now have a double layout state, one for printing or a read-only state and another for inline editing.
This also means that the built-in feature that controls the View Picker changed.
Luckily, now that we have our XrmLookupField wrapper, we only need to make slight modification in order to make this feature work again and apply it in all our forms.

Following are the required XrmLookupFIeld additions for 2013
I added a comment above each addition to make it noticeable



//SDK wrapper for lookup field 2013
function XrmLookupField(sId) {
    var xlf = this;
    //control instance
    xlf.Ctl = Xrm.Page.getContorl(sId);
    //dom instance
    xlf.Dom = document.getElementById(sId);
    //jquery instance
    xlf.$ = $(xlf.Dom);
 
    /* 2013 addition --- Inline Control instance --- */
    xlf.$i = $("#" + sId + "_i");

    //use that to disable the view picker
    xlf.DisableViewPicker = function () { 
        /* 2013 addition --- The attribute capitalization changed */
        xlf.SetParameter("disableviewpicker", "1"); 
    }
    //use that to enable the view picker
    xlf.EnableViewPicker = function () { 
        /* 2013 addition --- The attribute capitalization changed */
        xlf.SetParameter("disableviewpicker", "0");  
    }
    
    //set undocumented attributes
        xlf.SetParameter = function (sName, vValue) { 
        xlf.$.attr(sName, vValue);  

        /* 2013 addition --- Also change the inline contorl value */
        xlf.$i.attr(sName, vValue);
    }   
    
    //add locked view
    xlf.AddLockedView = function (sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout) {
        //first enable the view picker
        xlf.EnableViewPicker();
        //add the custom view (last parameter set the view as default)
        xlf.Ctl.addCustomView(sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout, true);
        //lock the view picker
        xlf.DisableViewPicker();
    }
    //create new guid
    xlf.NewGuid = function () {
        var d = new Date().getTime();
        var guid = '{xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx}'.replace(/[xy]/g, function (c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
        });
        return guid;
    }
}

And finally , the usage is exactly the same as above saving us a tone of work 
Fill free to comment

Cheers,
Adi

Tuesday, August 12, 2014

detachCloseAlert 2013

Although the detach Close Alert method exist in 2013 code it doesn’t work.
Instead you should use the following peace of code.

//this seems to work only in chrome
Mscrm.RefreshPageHandler.set_forceNavigationAway(true);
//this seems to work both in IE and chrome 
Mscrm.ReadFormUtilities.set_forceNavigationAway(true);

If you need to check the value of the force Navigation flag you can use:

Mscrm.RefreshPageHandler.get_forceNavigationAway(true);

If you what to change the actual text that appear inside the close alert you can change the
Following variable LOCID_FORMS_SAVE_CONFIRM_TITLE. e.g.

LOCID_FORMS_SAVE_CONFIRM_TITLE  = "There are unsaved changes ... "

The downside to this is that you can only assign a static string to LOCID_FORMS_SAVE_CONFIRM_TITLE.
This of course is totally unsupported so use it at your own risk.

Cheers,