Here is the screenshot of final output.

Here is what PoC is doing
1. There is Silverlight application hosted in CRM on custom area-subarea. This in-turn needs two web resources
2. There is Custom button added in Ribbon
3. Third web resource is hosting JavaScript function
4. On click of Custom Button on CRM Ribbon function in JavaScript web resource is called which in-turn calls the method in C# code of Silverlight application. String input is passed to this method
5. C# method is converting the input string to upper case and returning it.
6. Finally alert is displayed with upper case string.
Here are the details to create the PoC
1. Created new solution in CRM
2. Created new Area and Sub-Area for this PoC by editing Site Map XML. Here is the snapshot of XML changes.
<AreaId="RibbonPoCArea"
Title="Custom Area For RibbonPoC"
ShowGroups="false"
Icon="/_imgs/ico_18_9600.png">
<GroupId="RibbonPoCGroup">
<SubAreaId="RibbonPoCSubArea"
Title="Custom Sub-Area For RibbonPoC"
Url="$webresource:new_/RibbonPocTestPage.html" />
</Group>
</Area>
3. Added custom button in Application Ribbon. Here is the updated XML for Ribbon
<RibbonDiffXml>
<CustomActions>
<CustomActionId="RibbonPoc.BasicHomeTab.Tools.CustomButton.CustomAction"
Location="Mscrm.BasicHomeTab.Tools.Controls._children"
Sequence="101">
<CommandUIDefinition>
<ButtonId="RibbonPoc.BasicHomeTab.Tools.CustomButton"
Command="RibbonPoc.BasicHomeTab.Tools.CustomButton.Command"
Sequence="101"
LabelText="$LocLabels:RibbonPoc.Form.CustomButton.LabelText"
ToolTipTitle="$LocLabels:RibbonPoc.Form.CustomButton.LabelText"
ToolTipDescription="$LocLabels:RibbonPoc.Form.CustomButton.ToolTip"
TemplateAlias="isv"Image16by16="/_imgs/ico_18_129.gif"
Image32by32="/_imgs/resourcecenter_24x24.gif" />
</CommandUIDefinition>
</CustomAction>
</CustomActions>
<Templates>
<RibbonTemplatesId="Mscrm.Templates"></RibbonTemplates>
</Templates>
<CommandDefinitions>
<CommandDefinitionId="RibbonPoc.BasicHomeTab.Tools.CustomButton.Command">
<EnableRules />
<DisplayRules />
<Actions>
<JavaScriptFunctionFunctionName="CallSilverlightMethod"
Library="$webresource:new_RibbonTestJavaScript" />
</Actions>
</CommandDefinition>
</CommandDefinitions>
<RuleDefinitions>
<TabDisplayRules />
<DisplayRules />
<EnableRules />
</RuleDefinitions>
<LocLabels>
<LocLabelId="RibbonPoc.Form.CustomButton.ToolTip">
<Titles>
<Titlelanguagecode="1033"description="Custom Button." />
</Titles>
</LocLabel>
<LocLabelId="RibbonPoc.Form.CustomButton.LabelText">
<Titles>
<Titlelanguagecode="1033"description="Custom Button" />
</Titles>
</LocLabel>
</LocLabels>
</RibbonDiffXml>
Here is important C# code. Note System.Windows.Browser.HtmlPage.RegisterScriptableObject("SilverlightCode", this); and [ScriptableMember]
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
System.Windows.Browser.HtmlPage.RegisterScriptableObject("SilverlightCode", this);
}
// After the Frame navigates, ensure the HyperlinkButton representing the current page is selected
private void ContentFrame_Navigated(object sender, NavigationEventArgs e)
{
foreach (UIElement child in LinksStackPanel.Children)
{
HyperlinkButton hb = child as HyperlinkButton;
if (hb != null && hb.NavigateUri != null)
{
if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
{
VisualStateManager.GoToState(hb, "ActiveLink", true);
}
else
{
VisualStateManager.GoToState(hb, "InactiveLink", true);
}
}
}
}
// If an error occurs during navigation, show an error window
private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
e.Handled = true;
ChildWindow errorWin = new ErrorWindow(e.Uri);
errorWin.Show();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(CustomMethod("Silverlight Button Clicked !"));
}
//This method will be called from JavaScript on click of Ribbon Button
//This method needs to be Public
[ScriptableMember]
public string CustomMethod(string message = "")
{
//MessageBox.Show(message, "Message", MessageBoxButton.OK);
return message.ToUpper();
}
}
Here is important HTML code. Note the <object id="SLFromJS"…
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object id="SLFromJS" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/RibbonPoC.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="4.0.50401.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
</form>
</body>
5. Hosted Silverlight Application in CRM. For this we need to create two web resources – one to host HTML and second for XAP.
6. Created one more web resource to host JavaScript function. Developer tools (F12) in IE 8 helped me to find exact location of my Silverlight Object (SLFromJS) in HTML DOM. Here is the JavaScript – Note window.frames['contentIFrame'].document.forms['form1'].SLFromJS;
function CallSilverlightMethod(sender) {
alert('Inside JS1!');
var slc = window.frames['contentIFrame'].document.forms['form1'].SLFromJS;
alert('Inside JS2!');
if (slc != null) {
alert('Inside if!');
alert(slc.Content.SilverlightCode.CustomMethod('Msg From JavaScript'));
alert('Going out of if!');
}
alert('Out of if!');
}
My CRM solution looks like following now
7. Done! Now test the work by opening link of HTML web resource.
Thanks to following blog posts which I referred.