SilverLight系列——动态装载XAP包
其实关于动态装载XAP包,网上已有不少资料,且都附有代码,我当初也是参考网上找到的代码改的,基本原理就是通过WebClient请求XAP文件流,然后分析流,加载程序集,最后通过反射创建实例。细节方面,直接看代码就可以很清楚了:
/// <summary> /// XAP包加载器 /// </summary> public class XapLoader { /// <summary> /// XAP包路径 /// </summary> private string _xapUri; public XapLoader(string xapUri) { _xapUri = xapUri; } /// <summary> /// 加载元素完毕时触发此事件 /// </summary> public event EventHandler<XapLoadEventArgs> LoadCompleted; /// <summary> /// 从XAP包中加载指定的Xaml文件并实例化为FrameworkElement /// </summary> /// <param name="xamlFileName">Xaml文件路径</param> public void LoadXaml(string xamlFileName) { Uri uri = new Uri(_xapUri, UriKind.Relative); WebClient client = new WebClient(); client.OpenReadCompleted += (sender, e) => { StreamResourceInfo resource = Application.GetResourceStream( new StreamResourceInfo(e.Result, null), new Uri(xamlFileName, UriKind.Relative)); string resourceMainfest = new StreamReader(resource.Stream).ReadToEnd(); FrameworkElement element = null; string errorMessage = null; try { element = XamlReader.Load(resourceMainfest) as FrameworkElement; } catch (Exception ex) { errorMessage = ex.Message; } if (LoadCompleted != null) LoadCompleted(this, new XapLoadEventArgs(element, errorMessage)); }; client.OpenReadAsync(uri); } /// <summary> /// 从XAP包中的指定程序集中加载指定的类型 /// </summary> /// <param name="assmblyName">程序集名称(含后缀)</param> /// <param name="typeName">加载类型的完全限定名</param> public void LoadControl(string assmblyName, string typeName) { Uri uri = new Uri(_xapUri, UriKind.Relative); WebClient client = new WebClient(); client.OpenReadCompleted += (sender, e) => { if (e.Error != null) { if (LoadCompleted != null) LoadCompleted(this, new XapLoadEventArgs(null, e.Error.Message)); return; } StreamResourceInfo resource = Application.GetResourceStream( new StreamResourceInfo(e.Result, null), new Uri("AppManifest.xaml", UriKind.Relative)); string resourceMainfest = new StreamReader(resource.Stream).ReadToEnd(); var asmPartEls = XDocument.Parse(resourceMainfest).Root.Elements().Elements(); Assembly assembly = null; foreach (XElement asmPartEl in asmPartEls) { string source = asmPartEl.Attribute("Source").Value; if (string.Compare(source,assmblyName, StringComparison.InvariantCultureIgnoreCase)==0) { StreamResourceInfo streamInfo = Application.GetResourceStream( new StreamResourceInfo(e.Result, "application/binary"), new Uri(source, UriKind.Relative)); AssemblyPart assemblyPart = new AssemblyPart(); assembly = (new AssemblyPart()).Load(streamInfo.Stream); } } if (assembly == null) { if (LoadCompleted != null) LoadCompleted(this, new XapLoadEventArgs(null, "未能找到指定程序集!")); } else { FrameworkElement element = null; string errorMessage = null; try { Type type = assembly.GetType(typeName); element = Activator.CreateInstance(type) as FrameworkElement; } catch (Exception ex) { errorMessage = ex.Message; } if (LoadCompleted != null) LoadCompleted(this, new XapLoadEventArgs(element, errorMessage)); } }; client.OpenReadAsync(uri); } }
/// <summary> /// 加载XAP包事件参数 /// </summary> public class XapLoadEventArgs : EventArgs { private XapLoadEventArgs() { } public XapLoadEventArgs(FrameworkElement element, string error) { _element = element; _error = error; } private string _error; /// <summary> /// 获取加载时包含的错误信息 /// </summary> public string Error { get { return _error; } } private FrameworkElement _element; /// <summary> /// 获取加载的UI元素 /// </summary> public FrameworkElement Element { get { return _element; } } }
使用方法:
XapLoader loader = new XapLoader(xapfile); loader.LoadCompleted += new EventHandler<XapLoadEventArgs>(loader_LoadCompleted); loader.LoadControl(assembly, pageclass);
然后在loader_LoadCompleted里通过事件参数的Element属性获取加载的UI实例即可。
最后有很重要的一点要提醒:
比如有SL主程序APP,其中引用了程序集A.DLL,动态加载的XAP包中也包含了程序集A.DLL,但是版本与主程序中引用的不一致,那么
从XAP包中实例化的对象也将尝试使用主程序APP中所加载的A.DLL,而非XAP包中的A.DLL。所以,这种情况下如果提示“×××方法不存在”,就不要再纳闷了。