在 Microsoft Dynamics CRM 2011 的解决方案里允许二次开发人员开发silverlight 应用部署到解决方案里,这给我们能实现自定义页面的功能,并且能很好的和2011的解决方案集成在一起,而且开发语言也是C#,对开发人员也更加友好。
在这里我主要记录下silverlight如果使用Developer Toolkit for Microsoft Dynamics CRM 2011 开发调试部署,以方便我自己以后查询。
我的开发环境 windows sever 2008 r2,Sqlserver 2008r2,Microsoft Dynamics CRM 2011已更新到update6,Developer Toolkit for Microsoft Dynamics CRM 2011 Release Version 1.0 ,Silverlight 5 在这里不再对Developer Toolkit for Microsoft Dynamics CRM 2011 Release Version 1.0 过多介绍,如果需要了解可以查看SDK。
为了快速开发silverlight应用我用到了 SilverCrmSoap Library,可以去http://silvercrmsoap.codeplex.com/这里下载。
这个类库主要包含三个部分,XrmHelper, FormHelper, 和SoapHelper.
1.XrmHelper 主要是功能帮助我们取得2011里的Xrm,Page,data,entity,attributes等内置js对象,这样方便我们嵌入silverlight应用和2011系统页面通信,
2.FormHelper 主要功能是帮我们取得系统页面的字段权限和给字段取值赋值,
3.SoapHelper 主要功能是帮助silverlight和2011的WCF通信,通过SOAP协议,也可以通过FetchXML不过他在SilverCrmSoap.FetchXml下。
安装好Developer Toolkit for Microsoft Dynamics CRM 2011(vs2010必须安装sp1),vs2011 会多出一些关于2011的项目模板如图:
选择第一个项目模板它会让你配置要连接的组织和解决方案如图:
当然你配置好后也可以修改配置信息
建立好后的解决方案目录结构
CrmPackage 是解决方案的配置信息,2011插件,工作流和silverlight的配置信息就在它里面所以不要误删,解决方案建立好后默认的会加入一些项目,
一个插件项目,一个工作流项目,一个silverlight项目和其承载的WEB项目。
我的silverlight项目的功能很简单就是让微软bing的silverlight Map在CRM里的account窗体上显示,当然我也用上silverlight的开发模式MVVM,但是事件处理上有瑕疵没有用绑定,没有找到完美的解决方案。
为了使用bingMap可能你的去微软申请一个key,是免费的,网上有很多教程。还要去下载bing Map的silverlight控件,
我的代码结构如下:
Account.cs 为封装客户的实体类,
BaseViewModel.cs为MapViewMode.cs的父类,BaseViewModel.cs类作用是让MapViewMode.cs里的公开属性变化后能让silverlight得到通知,
MapViewMode.cs 里存放程序简单的业务逻辑,
BingMap.xaml里存放着bing Map控件和数据绑定,BingMap.cs里在BingMap.xaml界面上的控件事件触发后调用MapViewMode.cs逻辑代码。
VisibilityConverter.cs是一个类型转换器,我自己测试用的(也就是让XAML认识你自定义的类型)。
其实里面的代码都可以从网上查资料就能得到,我下面只介绍重点代码和配置:
2011项目建立后会把默认的silverlight程序集加入到CrmPackage 的References里,可是我需要对其属性重新调整
当我点击silverlight的dll时属性窗口会先显示其默认属性,这时我们要注意到Unique Name 这个属性
第一个反斜杠前面一定要放你解决方案的前缀,后面就可以照silverlight承载的HTML配置,我的HTML承载页面如下:
<form id="form1" runat="server" style="height: 100%"> <div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ClientBin/lamex.crm.silvermap.xap" /> <param name="onError" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="5.0.61118.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.61118.0" style="text-decoration: none"> <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="获取 Microsoft Silverlight" style="border-style: none" /> </a> </object> <iframe id="_sl_historyFrame" style="visibility: hidden; height: 0px; 0px; border: 0px"></iframe> </div> </form>
注意 param 标签 name 为 source的 value,就复制它。此处一定不能错,否则silverlight不能正常显示。
这时我需要在CrmPackage 里加入要承载silverlight的html页面,页面原有的silverlight.js需要替换成如下:
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
如果不替换的话你就能不能取到2011的内置对象,这时我们就可以在CrmPackage 点击鼠标右键再出现的菜单上选择部署,部署完成后在客户试图上建立一个iframe,然后配置我刚部署的html页面,不要应用XAP。
如果有只定义参数需要传入silverlight可以在iframe配置如图
我的自定义参数写的是我的申请bing map的key,你也可以将“将记录对象类型和唯一代码标识服作为参数传递。”勾选
在代码里可以这样取得,我是在App.xml里的Application_Startup里去取然后存入App.Current.Host.InitParams里
private void Application_Startup(object sender, StartupEventArgs e)
{
IDictionary<string, string> idic = HtmlPage.Document.QueryString;
foreach (string item in idic.Keys)
{
e.InitParams.Add(item, idic[item]);
}
this.RootVisual = new MainPage();
}
然后可以定义个方法去取App.Current.Host.InitParams的值
private string GetCrmFormParameter(string parameterName)
{
if (App.Current.Host.InitParams.ContainsKey(parameterName))
{
return App.Current.Host.InitParams[parameterName];
}
else
{
return string.Empty;
}
}
取得具体方法
CredentialKey = new ApplicationIdCredentialsProvider(GetCrmFormParameter("data")); //自定义参数
crmId = GetCrmFormParameter("id");//记录guid
crmType = GetCrmFormParameter("type");//记录typecode
crmTypeName = GetCrmFormParameter("typename");//记录实体名
但是有时候我们需要和iframe的父窗体通信怎么如下面代码
ScriptObject objparent = (ScriptObject)HtmlPage.Window.GetProperty("parent");
ScriptObject xrm = (ScriptObject)objparent.GetProperty("Xrm");
ScriptObject Page = xrm.GetProperty("Page") as ScriptObject;
ScriptObject data = Page.GetProperty("data") as ScriptObject;
ScriptObject xrmPageDataEntity = data.GetProperty("entity") as ScriptObject;
ScriptObject XrmPageDataEntityAttributes = xrmPageDataEntity.GetProperty("attributes") as ScriptObject;
string latitude = FormHelper.GetEntityValue<string>(XrmPageDataEntityAttributes, "new_latitude", string.Empty);
因为我需要取到iframe的父窗体里的对象,所以先得取得js里的parent对象,然后我对FormHelper里的方法进行拉修改如下:
public static T GetEntityValue<T>(string fieldname, T defaultValue)
{
return GetEntityValue<T>(null, fieldname, defaultValue);
}
public static T GetEntityValue<T>(ScriptObject XrmPageDataEntityAttributes, string fieldname, T defaultValue)
{
if (XrmPageDataEntityAttributes != null)
{
if (XrmPageDataEntityAttributes == null) return defaultValue;
var attribute = (ScriptObject)XrmPageDataEntityAttributes.Invoke("get", fieldname);
return (T)attribute.Invoke("getValue");
}
else
{
if (XrmHelper.XrmPageDataEntityAttributes == null) return defaultValue;
var attribute = (ScriptObject)XrmHelper.XrmPageDataEntityAttributes.Invoke("get", fieldname);
return (T)attribute.Invoke("getValue");
}
}
写程序时我们可能需要多次部署,在部署之前请先删除我先前部署的XAP,然后开始新一次的部署。调试的时候我只要在系统进程中找到如下图类似的进程就可以开始调试:
程序最后的效果: