繁体   English   中英

如何动态发现Silverlight prism应用程序中所有模块中的所有XAML文件

[英]How to dynamically discover all XAML files in all modules in a Silverlight prism app

有没有一种简单的方法可以动态发现所有当前加载的模块(特别是Silverlight Prism应用程序)中的所有XAML文件? 我确信这是可能的,但不知道从哪里开始。

这必须在Silverlight客户端上发生:我们当然可以在开发机器上解析项目,但这会降低灵活性并在搜索中包含未使用的文件。

基本上我们希望能够解析一个非常大的Prism项目中的所有XAML文件(独立于加载它们)以识别所有本地化字符串。 这将让我们构建一个初始本地化数据库,其中包括我们所有的资源绑定字符串,还可以创建查找它们所在的XAML文件(以便为翻译人员编辑)。

为什么会这样?: 翻译者最糟糕的事情就是在一个上下文中更改一个字符串,发现它在其他地方使用的含义略有不同。 我们正在从应用程序本身启用翻译的上下文编辑。

更新(9月14日):

由于安全限制,Silverlight无法使用标准迭代程序集的方法。 这意味着以下解决方案的唯一改进是尽可能与Prism模块管理合作。 如果有人想为此问题的最后一部分提供代码解决方案,可以与您分享点数!

跟进:

在基于模块的项目中迭代XAP文件的内容似乎是一个非常方便的事情,因为各种原因,所以能够做另外100个代表来获得真正的答案(最好是工作示例代码)。 干杯,祝你好运!

下面的部分解决方案(工作但不是最佳):

下面是我提出的代码,它是嵌入式资源上这个链接的技术(由Otaku建议)和我自己的Prism模块目录的迭代。

  • 问题1 - 所有模块都已加载,因此基本上必须再次下载它们,因为我无法解决如何迭代所有当前加载的Prism模块。 如果有人想在这个上分享赏金,你仍然可以帮助这个成为一个完整的解决方案!

  • 问题2 - ResourceManager中显然存在一个错误,它要求您获取已知资源的流,然后才能迭代所有资源项(请参阅下面的代码中的注释)。 这意味着我必须在每个模块中都有一个虚拟资源文件。 很高兴知道为什么需要初始的GetStream调用(或者如何避免它)。

     private void ParseAllXamlInAllModules() { IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>(); foreach (var module in mm.Modules) { string xap = module.Ref; WebClient wc = new WebClient(); wc.OpenReadCompleted += (s, args) => { if (args.Error == null) { var resourceInfo = new StreamResourceInfo(args.Result, null); var file = new Uri("AppManifest.xaml", UriKind.Relative); var stream = System.Windows.Application.GetResourceStream(resourceInfo, file); XmlReader reader = XmlReader.Create(stream.Stream); var parts = new AssemblyPartCollection(); if (reader.Read()) { reader.ReadStartElement(); if (reader.ReadToNextSibling("Deployment.Parts")) { while (reader.ReadToFollowing("AssemblyPart")) { parts.Add(new AssemblyPart() { Source = reader.GetAttribute("Source") }); } } } foreach (var part in parts) { var info = new StreamResourceInfo(args.Result, null); Assembly assy = part.Load(System.Windows.Application.GetResourceStream(info, new Uri(part.Source, UriKind.Relative)).Stream); // Get embedded resource names string[] resources = assy.GetManifestResourceNames(); foreach (var resource in resources) { if (!resource.Contains("DummyResource.xaml")) { // to get the actual values - create the table var table = new Dictionary<string, Stream>(); // All resources have “.resources” in the name – so remove it var rm = new ResourceManager(resource.Replace(".resources", String.Empty), assy); // Seems like some issue here, but without getting any real stream next statement doesn't work.... var dummy = rm.GetStream("DummyResource.xaml"); var rs = rm.GetResourceSet(Thread.CurrentThread.CurrentUICulture, false, true); IDictionaryEnumerator enumerator = rs.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Key.ToString().EndsWith(".xaml")) { table.Add(enumerator.Key.ToString(), enumerator.Value as Stream); } } foreach (var xaml in table) { TextReader xamlreader = new StreamReader(xaml.Value); string content = xamlreader.ReadToEnd(); { // This is where I do the actual work on the XAML content } } } } } } }; // Do the actual read to trigger the above callback code wc.OpenReadAsync(new Uri(xap, UriKind.RelativeOrAbsolute)); } } 

使用GetManifestResourceNames反射并从那里解析只获得以.xaml结尾的那些。 以下是使用GetManifestResourceNames的示例: 枚举嵌入式资源 虽然示例显示了如何使用单独的.xap执行此操作,但您可以使用加载的.xap执行此操作。

我见过人们抱怨棱镜有一些相当严重的错误

解决你的问题:

问题1 :我不熟悉Prism,但从面向对象的角度来看,Module Manager类应该跟踪模块是否已加载,如果尚未加载,则允许您使用List<Module>上的map函数递归加载其他模块List<Module>或Prism用于抽象地表示组件的任何类型。 简而言之,让您的Module Manager实现一个隐藏状态,表示加载的模块列表。 然后,您的Map函数应该将已加载的模块列表作为种子值,并返回尚未加载的模块列表。 然后,您可以内部化公共LoadAllModules方法的逻辑,或允许某人迭代公共List<UnloadedModule> where UnloadedModule : Module并让他们选择要加载的内容。 由于在通过多个线程访问模块管理器时出现并发问题,我不建议同时公开这两种方法。

问题2 :需要初始GetStream调用,因为ResourceManager懒惰地评估资源。 直觉上,我猜测的原因是附属程序集可以包含多个特定于语言环境的模块,如果所有这些模块一次性加载到内存中,它可能会耗尽堆,而这些都是非托管资源。 您可以使用RedGate的.NET Reflector查看代码以确定详细信息。 可能有比GetStream更便宜的方法。 您也可以通过加载每个Silverlight程序集中的资源来触发它来加载程序集。 尝试使用ResourceManager.GetObject(“TOOLBAR_​​ICON”)或者可能是ResourceManager.GetStream(“TOOLBAR_​​ICON”) - 请注意,我没有尝试过这个并且正在键入此建议,因为我即将离开这一天。 我的理由是它一直比SomeDummy.Xaml方法更快,我相信TOOLBAR_​​ICON是硬连线的,是每个组件中的第0个资源。 因此,它将在Stream中尽早阅读。 Faaaaaast。 因此,不仅要避免在我建议的项目的每个程序集中都需要SomeDummy.Xaml; 我也建议进行微观优化。

如果这些技巧奏效,您应该能够显着提高性能。


其他想法:

我想你可以进一步清理你的代码。

IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>();
    foreach (var module in mm.Modules)
    {

可以重构以删除对UnityContainer的引用。 另外,IModuleCatalog将通过我在回答问题1时提到的List<Module>的包装器进行实例化。换句话说,IModuleCatalog将是所有已加载模块的动态视图。 我假设还有更多的性能可以从这个设计中拉出来,但至少你不再依赖于Unity。 这将有助于您以后更好地重构代码,以获得更多性能提升。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM