Featured Post

Web API Requests Series

Web API Series:  Create and Retrieve , Update and Delete , Retrieve Multiple , Associate/Disassociate , Impersonation , Run Workflows

20 October 2011

13 September 2011

Interact with CRM form objects from Silverlight through the HTML Bridge

In this post I will show you how we can access attributes on the form via Silverlight managed code.

When you host a Silverlight application in a HTML page you can access the HTML Document Object Model (DOM) from the managed code, and call managed code from JavaScript. This is possible using the HTML Bridge technology available in Silverlight.
In the same way you can access the xrm objects on a Dynamics Crm form using managed code and vice versa when a Silverlight application is embedded in a Crm form.
In this post I will show you a simple example of how to do this.

Recipe for this example:
  • I will create a simple Silverlight application
  • Create a Silverlight web resource
  • Add the web resource to a Crm form
  • Verify
The Silverlight application consists of a Textbox and a Button as shown below:

<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Stretch">
            <TextBox x:Name="InputText" Width="200" Height="23"/>
            <Button Content="Go" Width="22" Height="20" Click="Button_Click" Margin="4,0,0,0"/>
</StackPanel>

After having created the Silverlight application let's add the following code to the Button_Click  event handler:

add this a reference to the "System.Windows.Browser" to your Silverlight project and the using statement in order to be able to use the HTML Bridge,

using System.Windows.Browser;

private void Button_Click(object sender, RoutedEventArgs e)
        {
            //Get the xrm object using the dynamic type
            dynamic xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
            //this is the number of employees attribute
            var numEmployees = xrm.Page.data.entity.attributes.get("numberofemployees");

            try
            {
               
                if (!string.IsNullOrEmpty(InputText.Text))
                {
                    //convert to int the input from SL text box
                    var employees = Convert.ToInt32(InputText.Text);

                    //set the attribute on the Crm form
                    numEmployees.setValue(employees);
                }
                else
                {
                    //set the attribute on the form
                    numEmployees.setValue(null);
                }
            }
            catch(System.FormatException fe)
            {
                HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
            }
            catch(System.OverflowException ofe)
            {
                HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));
            }
        }

After having added the Silverlight web resource (see this post) we can now embed it in a form (the account form in this example), thje result is shown below:







To test the application enter a value into the textbox and enter "Go", you should see the number of employee populated with the value you have inserted in alternative you can leave the text box empty.
If you try to enter a value that is too large, too small or is not a number an exception will be thrown and a message will be displayed (see below) . Note that the messages are once again shown using the HTML Bridge through the following lines of code:

HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));


Alternatively we could have used a Silverlight Child window or simple MessageBox.Show("")

Well that's all really! Next time we will see how to call a Silverlight method from Crm form.

Stay tuned.

Luciano.




02 September 2011

12 August 2011

How to open a Silverlight application from the ribbon menu

In this post I am going to show you how to open a Silverlight application by clicking a custom ribbon button.

Steps:
  1. Create a new ribbon button
  2. Create the Silverlight application
  3. Create the XAP web resource
  4. Create a HTML web resource for the page that  hosts the Silverlight app
First of all let's create a new custom button, see this post.
Create your Silverlight application, if you want to use the REST endpoint see this post.
Create a Silverlight web resource and the HTML web resource.

Follow this guidelines when naming the web resources:

  1. HTML Web resource name = /MyApplicationName.html, this will result,after saving, in    customizationprefix_/MyApplicationName.html.
  2. HTML Web resource display name = MyApplicationName.html.
  3. XAP Web resource name = /ClientBin/MyApplicationName.xap, this will result,after saving, in                customizationprefix_/ClientBin/MyApplicationName.xap.
  4. XAP Web resource display name = MyApplicationName.xap.
Enjoy!


How to add a custom button in the ribbon menu.

In this post I will walk you through the process of creating a new ribbon button that will be able to open a HTML page from which we can access some crm context parameter such as language id and record id.
In this particular example I will be using the HTML page to host a Silverlight application.
First of all we need to export the solution that we want to customise, so navigate to settings-> Solutions and select the solution you want to export.
Once downloaded the solution open the customizations.xml file with your favourite editor.
Here is my customization:
<RibbonDiffXml>
        <CustomActions>
                <CustomAction Id="Imp.Form.Account.ShowHierarchy" Sequence="51" Location="Mscrm.Form.account.MainTab.ExportData.Controls._children">
                  <CommandUIDefinition>
                    <Button Id="Imp.Form.Account.ShowHierarchyButton" Image16by16="$webresource:new_Hierarchy16by16.gif" Image32by32="$webresource:new_Hierarchy32by32.gif" TemplateAlias="o1" LabelText="$LocLabels:Imp.Form.Account.ShowHierarchyTitle" ToolTipTitle="$LocLabels:Imp.Form.Account.ShowHierarchyTitle" ToolTipDescription="$LocLabels:Imp.Form.Account.ShowHierarchyTooltip" Command="Imp.Form.Account.ShowAccountHierarchyCommand" />
                  </CommandUIDefinition>
            </CustomAction>
                </CustomActions>
                <Templates>
                  <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
                </Templates>
                <CommandDefinitions>
                <CommandDefinition Id="Imp.Form.Account.ShowAccountHierarchyCommand">
                  <EnableRules></EnableRules>
                  <DisplayRules></DisplayRules>
                  <Actions>
                    <Url Address="$webresource:new_/SilverlightFromRibbonTestPage.html" PassParams="true"></Url>
                </Actions>
                </CommandDefinition>
                </CommandDefinitions>
                <RuleDefinitions>
                  <TabDisplayRules />
                  <DisplayRules />
                  <EnableRules />
                </RuleDefinitions>
                <LocLabels>
                <LocLabel Id="Imp.Form.Account.ShowHierarchyTooltip">
                    <Titles>
                      <Title languagecode="1033" description="This will open a HTML page hosting a Silverlight application." />
                    </Titles>
                </LocLabel>
                <LocLabel Id="Imp.Form.Account.ShowHierarchyTitle">
            <Titles>
              <Title languagecode="1033" description="Open Page" />
            </Titles>
        </LocLabel>
        </LocLabels>
      </RibbonDiffXml>

Import the new customization.xml file and if you don't have any error you should be able to see a new button on the account form.









Hope you find this post useful.
Cheers , Luciano.

30 May 2011

Silverlight Bing Maps in Crm 2011 with REST endpoint

In this example I have used the CRM REST endpoint, see this post to learn how to use the CRM endpoint with a Silverlight project in Visual Stuido 2010.

The objective of this example is to create a Silverlight Web resource that can be included in a Microsoft Dynamics CRM solution and installed in any organization and any kind of deployment. Authentication is not an issue because the Silverlight Web resource will be included in the application.


...recently I happened to play a bit with the Bing Maps API for Silverlight (you can play as well from here) and thought it was a good idea to use it within the contact or account form entity to show a map based on the post code field of the entity. You can download the Bing Maps SDK from here.
So basically what I have done is to create a simple Silverlight application, to host the Bing Map, created a Silverlight web resource in CRM, added it to an entity form, remember to tick the "Pass record object-type code and unique identifier as parameter" checkbox when you create the web resource.
In this way you can then retrieve the unique GUID of the entity and pass it to the Silverlight application and use it to retrieve the post code on the form to pass it as input parameter to the Bing Map.
The snippet below shows how to access the parameters you passed from the CRM form to your Silverlight app:


            #region get the data passed from the CRM form context
            var queryStrings = HtmlPage.Document.QueryString;
            string recordGuid = string.Empty;
            if (queryStrings.ContainsKey("id"))
            {
                // it is also possible to get the GUID of the curent record from the query string
                string  entityId = queryStrings["id"].ToString();
                //get the GUID of the current record
                recordGuid = App.Current.Host.InitParams["id"];
                //get the current user language code i.e.1033 for English
                string userLcid = App.Current.Host.InitParams["userlcid"];
            }
            else
            {
                recordGuid = "87C203HB-C07F-E011-B487-000C29FD9425";
            }
            #endregion


For  a list of parameters that you may access from a Silverlight application embedded in an entity form see this post.


Now that we have our WCF data service in place, we need to create a Silverlight Web resource; we are actually going to create two web resources: one is the Silverlight xap web resource and the other is the HTML page that was created from Visual Studio if we have chosen to host our Silverlight application within a web application. We need to create the HTML web resource because we need to have access to the CRM client global context from our code behind in order to be able to retrieve information about our organization like: the server URL and Xrm.Page object e.g.
Ok, to do this we need to change few things in our HTML page


  1.  Remove or comment out this line: <script type="text/javascript" src="Silverlight.js"></script>
  2.  and add this line instead <script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script> 

Now we can add our web resources. We need to pay a little attention at the way we create the web resources: I personally always follow these guidelines:

  1. HTML Web resource name = /MyApplicationName.html, this will result,after saving, in    customizationprefix_/MyApplicationName.html.
  2. HTML Web resource display name = MyApplicationName.html.
  3. XAP Web resource name = /ClientBin/MyApplicationName.xap, this will result,after saving, in                customizationprefix_/ClientBin/MyApplicationName.xap.
  4. XAP Web resource display name = MyApplicationName.xap.
Save and don't forget to publish the web resources.
All is left to do at this point is to create the code! :()

First of all let's create a helper class that will be used to retrieve our organization server URL:

public static class ServerUtility
    {
        /// <summary>
        /// Returns the ServerUrl from Microsoft Dynamics CRM
        /// </summary>
        /// <returns>String representing the ServerUrl or String.Empty if not found.</returns>
        public static String GetServerUrl()
        {
            String serverUrl = String.Empty;

            //Try to get the ServerUrl from the Xrm.Page object
            serverUrl = GetServerUrlFromContext();

            return serverUrl;
        }

        /// <summary>
        /// Attempts to retrieve the ServerUrl from the Xrm.Page object
        /// </summary>
        /// <returns></returns>
        private static String GetServerUrlFromContext()
        {
            try
            {
                // If the Silverlight is in a form, this will get the server url
                ScriptObject xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
                ScriptObject page = (ScriptObject)xrm.GetProperty("Page");
                ScriptObject pageContext = (ScriptObject)page.GetProperty("context");

                String serverUrl = (String)pageContext.Invoke("getServerUrl");

                return serverUrl;
            }
            catch
            {
                return String.Empty;
            }
        }
    }

In our MainPage.xaml.cs add the following code:

public partial class MainPage : UserControl
{
     private SynchronizationContext _syncContext;

     private YourOrganizationNameContext _context;
     private string _serverUrl;
     private string _entityId = string.Empty;


public MainPage()
        {
            InitializeComponent();


            #region get the data passed from the CRM form context
            var queryStrings = HtmlPage.Document.QueryString;
            string recordGuid = string.Empty;
            if (queryStrings.Count > 1)
            {
                // it is also possible to get the GUID of the curent record from the query string
                string  entityId = queryStrings["id"].ToString();
                //get the GUID of the current record
                recordGuid = App.Current.Host.InitParams["id"];
                //get the current user language code i.e.1033 for English
                string userLcid = App.Current.Host.InitParams["userlcid"];
            }
            else
            {
                recordGuid = "87C203HB-C07F-E011-B487-000C29FD9425";
            }
            #endregion
            #region REST ENDPOINT

            //Keeps a reference to the UI thread
            _syncContext = SynchronizationContext.Current;

            //Get the ServerUrl (ServerUrl is formatted differently OnPremise than OnLine)
            _serverUrl = ServerUtility.GetServerUrl();

            if (!String.IsNullOrEmpty(_serverUrl))
            {

                //Setup Context
                _context = new ImpartaDevContext(
                    new Uri(String.Format("{0}/xrmservices/2011/organizationdata.svc/",
                        _serverUrl), UriKind.Absolute));

                //This is important because if the entity has new attributes added the code will fail.
                _context.IgnoreMissingProperties = true;

                //Retrieve the contact with its Guid
                BeginRetrieveContact(new Guid(recordGuid));
            }
            else
            {
                //No ServerUrl was found. Display message.
                MessagePanel.Children.Add(new TextBlock()
                {
                    Text =
                        "Unable to access server url. Launch this Silverlight " +
                        "Web Resource from a CRM Form OR host it in a valid " +
                        "HTML Web Resource with a " +
                        "<script src='../ClientGlobalContext.js.aspx' " +
                        "type='text/javascript'></script>"
                });
            }

        }
        /// <summary>
        /// Creates a DataServiceQuery to retrieve the current Contact
        /// </summary>
        /// <param name="Id"></param>
        private void BeginRetrieveContact(Guid Id)
        {
            try
            {
                DataServiceQuery<Contact> query = (DataServiceQuery<Contact>)_context
                    .ContactSet.Where<Contact>(c => c.ContactId == Id);

                query.BeginExecute(OnRetrieveContactComplete, query);
            }
            catch (DataServiceQueryException dsqe)
            {
                _syncContext.Send(new SendOrPostCallback(showErrorDetails), dsqe);
            }
        }
        ///<summary>
        /// Extracts the retrieved Contact from the query result.
        /// </summary>
        /// <param name="result"></param>
        private void OnRetrieveContactComplete(IAsyncResult result)
        {
            try
            {
                DataServiceQuery<Contact> results = result.AsyncState as DataServiceQuery<Contact>;

                Contact retrievedContact = new DataServiceCollection<Contact>         (results.EndExecute(result)).First<Contact>();
                
                _postCode = retrievedContact.Address1_PostalCode;

                GeocodeInput(_postCode);

                MessagePanel.Children.Add(new TextBlock() { Text = 
                    String.Format("Retrieved the contact named \"{0}\".",
                        retrievedContact.Address1_PostalCode)
                });                
            }
            catch (SystemException se)
            {
                _syncContext.Send(new SendOrPostCallback(showErrorDetails), se);
            }
        }
        /// <summary>
        /// Will display exception details if an exception is caught.
        /// </summary>
        /// <param name="ex">An System.Exception object</param>
        private void showErrorDetails(object ex)
        {
            //Assure the control is visible
            MessagePanel.Visibility = System.Windows.Visibility.Visible;

            Exception exception = (Exception)ex;
            String type = exception.GetType().ToString();

            MessagePanel.Children.Add(new TextBlock()
            {
                Text =
                    String.Format("{0} Message: {1}", type, exception.Message)
            });

            MessagePanel.Children.Add(new TextBlock()
            {
                Text = String.Format("Stack: {0}", exception.StackTrace)
            });

            if (exception.InnerException != null)
            {
                String exceptType = exception.InnerException.GetType().ToString();
                MessagePanel.Children.Add(new TextBlock()
                {
                    Text =
                        String.Format("InnerException: {0} : {1}", exceptType,
                        exception.InnerException.Message)
                });
            }
        }




#endregion

        #region Bing Map
        private int geocodesInProgress;
        private PlatformServices.GeocodeServiceClient geocodeClient;
        private PlatformServices.GeocodeServiceClient GeocodeClient
        {
            get
            {
                if (null == geocodeClient)
                {
                    //Handle http/https; OutOfBrowser is currently supported on the MapControl only for http pages
                    bool httpsUriScheme = !Application.Current.IsRunningOutOfBrowser && HtmlPage.Document.DocumentUri.Scheme.Equals(Uri.UriSchemeHttps);
                    BasicHttpBinding binding = httpsUriScheme ? new BasicHttpBinding(BasicHttpSecurityMode.Transport) : new BasicHttpBinding(BasicHttpSecurityMode.None);
                    UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc");
                    if (httpsUriScheme)
                    {
                        //For https, change the UriSceheme to https and change it to use the default https port.
                        serviceUri.Scheme = Uri.UriSchemeHttps;
                        serviceUri.Port = -1;
                    }

                    //Create the Service Client
                    geocodeClient = new PlatformServices.GeocodeServiceClient(binding, new EndpointAddress(serviceUri.Uri));
                    geocodeClient.GeocodeCompleted += new EventHandler<PlatformServices.GeocodeCompletedEventArgs>(client_GeocodeCompleted);
                }
                return geocodeClient;
            }
        }

        private GeocodeLayer geocodeLayer;
        private GeocodeLayer GeocodeLayer
        {
            get
            {
                if (null == geocodeLayer)
                {
                    geocodeLayer = new GeocodeLayer(MyMap);
                }
                return geocodeLayer;
            }
        }

        private void GeocodeAddress(string address)
        {
            PlatformServices.GeocodeRequest request = new PlatformServices.GeocodeRequest();
            request.Culture = MyMap.Culture;
            request.Query = address;
            // Don't raise exceptions.
            request.ExecutionOptions = new PlatformServices.ExecutionOptions();
            request.ExecutionOptions.SuppressFaults = true;

            // Only accept results with high confidence.
            request.Options = new PlatformServices.GeocodeOptions();
            // Using ObservableCollection since this is the default for Silverlight proxy generation.
            request.Options.Filters = new ObservableCollection<PlatformServices.FilterBase>();
            PlatformServices.ConfidenceFilter filter = new PlatformServices.ConfidenceFilter();
            filter.MinimumConfidence = PlatformServices.Confidence.High;
            request.Options.Filters.Add(filter);

            Output.Text = "< geocoding " + address + " >";
            geocodesInProgress++;

            MyMap.CredentialsProvider.GetCredentials(
                (Credentials credentials) =>
                {
                    //Pass in credentials for web services call.
                    //Replace with your own Credentials.
                    request.Credentials = credentials;

                    // Make asynchronous call to fetch the data ... pass state object.
                    GeocodeClient.GeocodeAsync(request, address);
                });
        }

        private void client_GeocodeCompleted(object sender, PlatformServices.GeocodeCompletedEventArgs e)
        {
            // Callback when the geocode is finished.
            string outString;
            geocodesInProgress--;
            try
            {
                if (e.Result.ResponseSummary.StatusCode != PlatformServices.ResponseStatusCode.Success)
                {
                    outString = "error geocoding ... status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">";
                }
                else if (0 == e.Result.Results.Count)
                {
                    outString = "No results";
                }
                else
                {
                    // Only report on first result.
                    outString = e.Result.Results[0].DisplayName;
                    Location loc = GeocodeLayer.AddResult(e.Result.Results[0]);
                    // Zoom the map to the location of the item.
                    MyMap.SetView(loc, 18);
                }
            }
            catch
            {
                outString = "Exception raised";
            }

            Output.Text = outString;
        }


        private void GeocodeInput(string postCode)
        {
            // Geocode whatever is in the textbox.
            string address = postCode;
            if (address.Length > 0)
            {
                GeocodeAddress(address);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //GeocodeInput();
        }

        private void Input_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                //GeocodeInput();
            }
        }

        private void Input_GotFocus(object sender, RoutedEventArgs e)
        {
            Input.SelectAll();
        }
        #endregion


}

And this is the XAML:


<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Left" Width="600" Height="400">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
            <StackPanel Grid.ColumnSpan="2" Orientation="Horizontal" Grid.Column="0" Background="LightGray">
                <TextBox x:Name="Input" KeyDown="Input_KeyDown" GotFocus="Input_GotFocus" Height="25" Width="400" Margin="10,0,10,0" Text="type address or post code here"
                         FontSize="13" />
                <Button Content="Search" Height="25" Click="Button_Click"/>
                <TextBlock x:Name="Output" FontSize="10" Foreground="Red" Margin="14,0,0,0" VerticalAlignment="Center"/>
            </StackPanel>
     
        <m:Map CredentialsProvider="yourBingMapKey" x:Name="MyMap"
               Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Mode="Aerial" Center="51.216101,1.0"
               ZoomLevel="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
        <StackPanel x:Name="MessagePanel" Grid.Row="2" Grid.ColumnSpan="2" />
    </Grid>


The code shown in this example is taken from CRM SDK to retrieve a Contact and from the Bing maps interactive SDK.

Summary



  1. We have created a Silverlight application hosted in a web site
  2. Modified the html page that hosts the Silverlight object
  3. created two web resources, one for the xap and one for the html page
  4. Downloaded the CSDL file from CRM
  5. Created a WCF data service based on the CSDL file
  6. Passed parameters from CRM to Silverlight
  7. Used the Guid of the current Contact record to retrieve the postal code through the REST endpoint with a Data Service Query
  8. Passed the postal code as input to the Bing map to show the location on the map

This is the result:













Hope you found this article useful, and stay tuned for the next post. ;)

How to use the CSDL file generated by the REST endpoint with Silverlight.

In this walk through, I will walk you through the steps needed to download the CSDL file that the REST endpoint generates based on the entities available to your development organization. Later we will see how to create a WCF data service based on the CSDL file.


N.B. Only those entities that are present when you generate the classes will be available for the Silverlight application.
Steps:

  1. In CRM 2011 navigate to Settings. Choose Customizations and then Developer Resources.
  2. Under Service Endpoints, click the Download CSDL link and save the OrganizationData.csdl file to your computer.


Now that we have our file generated and downloaded to our computer, we can use it to create a WCF data service, so from Visual Studio:
  1. In your Visual Studio Silverlight application project, right-click References, and then click Add Service Reference.
  2. Type the path of the location where you saved the OrganizationData.csdl file, and then click Go.
  3. Enter an appropriate Namespace and then click OK.
The name of the System.Data.Services.Client context will be "YourOrganizationNameContext".

Well done! Now we have our Silverlight application all set up ready to access the entities in our CRM organization. In another post I will show how to use the REST endpoint with Silverlight.
Stay tuned!

26 May 2011

ALFAPEOPLE ITSM SUITE 2011

IT Infrastructure Library Service Management
The purpose of IT Service Management (ITSM) is to integrate IT strategy and the delivery of IT services with the goals of the business, with an emphasis on providing benefit to customers. The ITSM journey demands a shift in focus and culture, from managing IT within separate technology silos, to managing the end to end delivery of services using guidance from best practice frameworks such as the IT Infrastructure Library (ITIL)
ITIL (IT Infrastructure Library) provides the world’s most recognized best practices to manage IT services. AlfaPeople's solution is designed to help organizations optimize their existing IT service management practices. ITIL addresses methods for IT organizations to generate strategic business value and deliver quality IT services.

03 May 2011

Add Silverlight to an entity form Navigation Menu.

...another way to get advantage of the power of Silverlight within Crm 2011 is to use the whole entity form real estate. We can do this by adding a navigation item to an existing group. The navigation area is divided into five areas: Common, Sales, Service, Marketing and for entities that support them, Processes, We can either use the form editor using drag and drop to change their vertical position, add new items, move existing items to another area or use the "FormXml" element (I will talk about how to customise the FormXml element in another post) to achieve the same result.

N.B. "In Microsoft Dynamics CRM 2011 , the ability to add navigation items to the entity form has been moved from ISV.Config to the <FormXml> (FormXML) element for each entity form. During upgrade from Microsoft Dynamics CRM 4.0, any entity form navigation customizations in the ISV.Config are moved to the definition of the main form for that entity."

So, let's see how we can customise an entity navigation form; first of all we are going to add a new navigation item to an existing area (it is not possible to add a new area). Open an entity record select the Customize tab and click on "Form",







this will open the form editor, at this point click on "Navigation" select the area on the navigation menu that you want to customise,














select the "Insert" tab,









and click the Navigation Link button,
















Select a name, look up for an icon and the HTML web resource that hosts your Silverlight application that you have previously added.

And here is the final result.















Hope you enjoyed. :)

21 April 2011

How to display a Silverlight application in the home page grid.

Hi everyone, in this post I am going to show how to display a Silverlight application in the main page grid by customising the navigation pane site map.
First of all add the site map to an unmanaged solution and export the solution, open the customizations.xml file in Visual Studio, associate it with the customizationSolution.xsd located in the SDK\Schemas folder in the SDK download package. To associate the customizations.xml file with the xsd file: in Visual studio press F4, this will show the property pane, click on schemas then "...", click add..., navigate to the folder select the file and click OK.

In order to be able to show a Silverlight application in the main page grid we need to create a web resources for both the Silverlight xap application and the HTML web page that hosts the Silverlight app (chek this post on how to create a Silverlight web resource) then reference the web page from the site map with the "$webresource:" directive see example below.


These few lines of code add a new group within the Workplace area and a new sub area within the group.


And this is the result, hope this helps someone.
HAPPY EASTER!!!

14 April 2011

08 April 2011

How to create Silverlight Web Resources

In this article I will talk about how to create a Silverlight application and add it as a Web Resource in Microsoft Dynamics CRM 2011, and then embed it in an entity form.
First of all create a new Silverlight 4 project in Visual Studio:

















Creating a Silverlight web resource is nothing different from creating any other web resource in CRM 2011, it is just a matter of creating the application,as we did above, creating  new web resource as shown below, giving a name, a display name, type and uploading the file (xap).



















After having created the web resource we will need to add it to an entity form, in my case the entity is an opportunity. Open an opportunity select the customize tab and then click on Form, add a Tab by choosing the Insert tab as shown below, add a web resource and check the "Pass record object-type code and unique identifier as parameter"(see this post).







Save publish and reload the opportunity entity, you should be able to see your Silverlight applicaiton inside the newly created tab.
This is how my Silverlight application looks like inside a form entity.

Silverlight app outside CRM context

Recently I have been working on a project where I needed to create a Silverlight application that ran outside the CRM context and I needed to pass parameters to the applicaiton from CRM context.
In a previous post I have talked of how to pass parameter to a Silverlight web resource within CRM context.
So, likely in this project the Silverlight application was not totally disconnected from CRM(of course) but it was launched by clicking a custom ribbon button from CRM. This, in fact, made things a little bit easier.
When you customize the ribbon menu in CRM 2011 by adding a new button for example, you may want to pass input parameter to a Java Script function for instance, well this is possible by adding the appropriate tags to the customization.
Let's start from exporting the customization from our CRM organization, extract the archive, open the customization.xml file with Visual Studio or another XML editing application that supports XSD schema validation. "This will help avoid XML validation errors when you import the ribbon. Associate the customizations.xml file with the customizationsSolution.xsd file. You can find this schema in the SDK\Schemas\CustomizationsSolutions.xsd file in the SDK download package."(from the CRM 2011 SDK).
Alternatively you can export only the Ribbon by creating a new unmanaged solution for this purpose, this will reduce the amont of data that you export/import and time.
Once you have downloaded the solution open the customizations.xml with your favourite editor, locate the RibbonDiffXml node, it is here that you need to apply your customization, you can find more details on how to customize a Ribbon in the CRM 2011 SDK. However here is one of mine:


<RibbonDiffXml>
  <CustomActions>
    <CustomAction Id="AP.Pharmacy.Form.CustomAction" Location="Mscrm.Form.hd_pharmacy.MainTab.Actions.Controls._children" Sequence="21">
      <CommandUIDefinition>
        <Button Id="AP.Pharmacy.Form.Button.AddPrescription" Command="AP.Pharmacy.Form.CommandDefinition.AddPrescription" LabelText="Add Prescription" ToolTipTitle="Add Prescription" ToolTipDescription="Lookup for patient and add Prescription" TemplateAlias="o1" Image16by16="/_imgs/ribbon/AddExistingStandard_16.png" Image32by32="/_imgs/ribbon/AddExistingStandard_32.png" />
      </CommandUIDefinition>
    </CustomAction>
    <CustomAction Id="AP.Pharmacy.HomepageGrid.CustomAction" Location="Mscrm.HomepageGrid.hd_pharmacy.MainTab.Actions.Controls._children" Sequence="21">
      <CommandUIDefinition>
        <Button Id="AP.Pharmacy.HomepageGrid.Button.AddPrescription" Command="AP.Pharmacy.HomepageGrid.CommandDefinition.AddPrescription" LabelText="Add Prescription" ToolTipTitle="Add Prescription" ToolTipDescription="Lookup for patient and add Prescription" TemplateAlias="o1" Image16by16="/_imgs/ribbon/AddExistingStandard_16.png" Image32by32="/_imgs/ribbon/AddExistingStandard_32.png" />
      </CommandUIDefinition>
    </CustomAction>
  </CustomActions>
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions>
    <CommandDefinition Id="AP.Pharmacy.HomepageGrid.CommandDefinition.AddPrescription">
      <EnableRules>
        <EnableRule Id="AP.contact.WebClient.EnableRule" />
        <EnableRule Id="AP.contact.grid.OneSelected.EnableRule" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="AP.contact.WebClient.DisplayRule" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction Library="$webresource:AA_/AddNewSample.js" FunctionName="AddNewRecordFromHomepageGrid">
          <CrmParameter Value="SelectedControlSelectedItemIds" />
          <CrmParameter Value="UserLcid" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
    <CommandDefinition Id="AP.Pharmacy.Form.CommandDefinition.AddPrescription">
      <EnableRules>
        <EnableRule Id="AP.contact.WebClient.EnableRule" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="AP.contact.WebClient.DisplayRule" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction Library="$webresource:AA_/AddNewSample.js"   FunctionName="AddNewRecordFromForm">
          <CrmParameter Value="FirstPrimaryItemId" />
          <CrmParameter Value="UserLcid" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
  </CommandDefinitions>
  <RuleDefinitions>
    <TabDisplayRules />
    <DisplayRules>
      <DisplayRule Id="AP.contact.WebClient.DisplayRule">
        <CrmClientTypeRule Type="Web" />
      </DisplayRule>
      <DisplayRule Id="AP.contact.form.FormStateNotNew.DisplayRule">
        <FormStateRule State="Create" InvertResult="true" />
      </DisplayRule>
    </DisplayRules>
    <EnableRules>
      <EnableRule Id="AP.contact.WebClient.EnableRule">
        <CrmClientTypeRule Type="Web" />
      </EnableRule>
      <EnableRule Id="AP.contact.form.NotNew.EnableRule">
        <FormStateRule State="Create" InvertResult="true" />
      </EnableRule>
      <EnableRule Id="AP.contact.grid.OneSelected.EnableRule">
        <SelectionCountRule AppliesTo="SelectedEntity" Maximum="100" Minimum="1" />
      </EnableRule>
    </EnableRules>
  </RuleDefinitions>
  <LocLabels />
</RibbonDiffXml>

As you can see in the highlighted part you can call a javascript function that must be a web resource and pass parameter to this function, here there are only three parameter shown, you can find more on the SDK. So once you click the button this function gets called and then from there you can call the HTML page that hosts the Silverlight application and pass those parameters to Silverlight.
Hope this helps.