Is there an easy way to dynamically discover all the XAMLs files within all the currently loaded modules (specifically of a Silverlight Prism application)? I am sure this is possible, but not sure where to start.
This has to occur on the Silverlight client: We could of course parse the projects on the dev machine, but that would reduce the flexibility and would include unused files in the search.
Basically we want to be able to parse all XAML files in a very large Prism project (independent of loading them) to identify all localisation strings. This will let us build up an initial localisation database that includes all our resource-binding strings and also create a lookup of which XAML files they occur in (to make editing easy for translators).
Why do this?: The worst thing for translators is to change a string in one context only to find it was used elsewhere with slightly different meaning. We are enabling in-context editing of translations from within the application itself.
Update (14 Sep):
The standard method for iterating assemblies is not available to Silverlight due to security restrictions. This means the only improvement to the solution below would be to cooperate with the Prism module management if possible. If anyone wants to provide a code solution for that last part of this problem there are points available to share with you!
Follow-up:
Iterating content of XAP files in a module-base project seems like a really handy thing to be able to do for various reasons, so putting up another 100 rep to get a real answer (preferably working example code). Cheers and good luck!
Partial solution below (working but not optimal):
Below is the code I have come up with, which is a paste together of techniques from this link on Embedded resources (as suggested by Otaku) and my own iterating of the Prism Module Catalogue.
Problem 1 - all the modules are already loaded so this is basically having to download them all a second time as I can't work out how to iterate all currently loaded Prism modules. If anyone wants to share the bounty on this one, you still can help make this a complete solution!
Problem 2 - There is apparently a bug in the ResourceManager that requires you to get the stream of a known resource before it will let you iterate all resource items (see note in the code below). This means I have to have a dummy resource file in every module. It would be nice to know why that initial GetStream call is required (or how to avoid it).
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)); } }