1

I have searched for an answer for this question all day without coming up with any solutions directly applicable to my case, or anything that works (in the one case I found that was applicable).

I have a Caliburn.Micro framework set up to use MEF, and I load my modularized elements just fine. The one thing missing is getting WPF to recognize the resources I use in one of my modules.

How modules are loaded in my app bootstrapper

[ImportMany]
private IEnumerable<IMyModule> _myModules;

protected override void Configure()
{
    // Because Configure() is also called from SelectAssemblies(), we cannot instantiate MEF again because it will create conflicts.
    if (_configured)
    {
        return;
    }

    AggregateCatalog aggregateCatalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
    aggregateCatalog.Catalogs.Add(new DirectoryCatalog(ConfigurationManager.AppSettings["MyModuleFolderLocation"]));
    aggregateCatalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));

    _container = new CompositionContainer(aggregateCatalog);

    CompositionBatch batch = new CompositionBatch();
    batch.AddExportedValue<IWindowManager>(new WindowManager());
    batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    batch.AddExportedValue(_container);

    _container.Compose(batch);
    _container.SatisfyImportsOnce(this);

    _configured = true;
}

protected override IEnumerable<Assembly> SelectAssemblies()
{
    // SelectAssemblies() is called before Configure(), so manually force Configure() to run first so that MEF is instantiated properly
    Configure();

    if (!_configured)
    {
        throw new Exception("Unable to configure assemblies");
    }

    List<Assembly> assemblies = new List<Assembly>();

    assemblies.Add(Assembly.GetExecutingAssembly());

    // Need to add all module assemblies so that Caliburn will be able to find the View for a ViewModel
    foreach(IMyModule myModule in _myModules)
    {
        Assembly assembly = myModule.GetType().Assembly;
        assemblies.Add(assembly);
    }

    return assemblies.Distinct();
}

This works just fine to get a module to be displayed properly.

But when a module has used an image, this image is never displayed, because this kind of loading apparently doesn't take resources into account. I create a Resources.resx file in the module project and add an image to that. The image file that is presented in Visual Studio then has a Build Action that says "Resource" and "Do not copy (to output directory)". This should mean that the image is embedded in the resulting DLL file.

The image is placed in a folder called "Resources" in the module project, and the XAML use it like this:

<Image Source="/Resources/myImage.png" />

The image is displayed in the preview in Visual Studio, but is not displayed when the application runs.

What I have tried that didn't work

  • Referencing the image in another way: <Image Source="pack://application:,,,/Resources/myImage.png" />
  • Getting the resources in BAML form and reinserting them into the executing assembly, like in this question: Instantiate ResourceDictionary xaml from other Assembly (which causes an OutOfMemoryException on this line var reader = new Baml2006Reader(stream);)
  • A lot of other answers that reference ResourceDictionary, but I have a Resource.resx file (which only generates an internal class that is not a ResourceDictionary)

The question remains

How can I get WPF/Caliburn.Micro to recognize resources from a DLL loaded by MEF?

Community
  • 1
  • 1
TheHvidsten
  • 4,028
  • 3
  • 29
  • 62
  • That question talks about ResourceDictionaries embedded in the XAML file. As I specified I do not have ResourceDictionaries, but a RESX file. – TheHvidsten Apr 18 '16 at 13:15
  • http://stackoverflow.com/questions/36639688/how-to-get-a-list-of-xaml-resources-defined-in-an-assembly/36639829#36639829 ? – StepUp Apr 18 '16 at 13:17
  • Where do I insert the `DictionaryEntry` into the main application? – TheHvidsten Apr 18 '16 at 13:25
  • ooops, what do you mean the `DictionaryEntry`? – StepUp Apr 18 '16 at 13:54
  • The question you linked to has the following code: `foreach (DictionaryEntry entry in reader)` which is fine because I think it contains stuff from my RESX file. But what do I do with the `DictionaryEntry` once I've got it? I believe I should put it somewhere (like `Application.Current.Resources`) but I can't find anything that accepts a `DictionaryEntry` as input. – TheHvidsten Apr 18 '16 at 14:18
  • you should put your `Resources` into `ResourceDictionary` and then it is possible to read all your resources. – StepUp Apr 18 '16 at 14:42
  • Even if my code did use ResourceDictionary for its images and texts, the code in the question you linked to still only returns DictionaryEntry. DictionaryEntry != ResourceDictionary. – TheHvidsten Apr 18 '16 at 14:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109451/discussion-between-stepup-and-gthvidsten). – StepUp Apr 18 '16 at 14:49

2 Answers2

2

Answer

Use this syntax for the Source property for images with Build Action: Resource

<Image Source="/AssemblyName;component/Resources/MyImage.png" />

Where AssemblyName is the name of the assembly (as defined in the project properties), and /Resource/MyImage.png is the path to the image (as defined in the project). component must always be present.

Side note

After a lot of help from @StepUp I initially decided to ask a new question using what was learned from this question and rephrasing everything to be more specific to my problem.

When writing this new question I ended up googling for phrases and commands that might help with the rephrasing, and I stumbled upon this page: http://www.geekchamp.com/tips/wp7-working-with-images-content-vs-resource-build-action

Apparently, the WPF Image control has a ton of ways to define the Source property. I had already tried quite a lot of various Source inputs and thought I had tried them all, but the page linked to above proved me wrong.

As far as I have been able to test, the syntax described above seems to work for images marked with Build Action: Resource. Therefore I no longer need to have a RESX file for the images, and I do not need any special handling when bootstrapping MEF.

TheHvidsten
  • 4,028
  • 3
  • 29
  • 62
0

At first, you should read the assembly with Style. Then, it is neccessary to read BAML files from external library using Baml2006Reader. Let me show an example:

private GetResourceDictionary()
{
    string address = @"WpfCustomControlLibrary1.dll";
    Assembly skinAssembly = Assembly.LoadFrom(address);
    string[] resourceDictionaries = skinAssembly.GetManifestResourceNames();
    Stream bamlStream = null;            
    string name = "themes/AllStylesDictionary.baml";//themes/AllStylesDictionary.baml
    foreach (string resourceName in resourceDictionaries)
    {
       ManifestResourceInfo info = skinAssembly.GetManifestResourceInfo(resourceName);
       if (info.ResourceLocation != ResourceLocation.ContainedInAnotherAssembly)
       {
          Stream resourceStream = skinAssembly.GetManifestResourceStream(resourceName);
          using (ResourceReader reader = new ResourceReader(resourceStream))
          {
              foreach (DictionaryEntry entry in reader)
              {
                 if (entry.Key.ToString().Equals(name.ToLower()))
                 {
                     bamlStream = entry.Value as Stream;
                 }
              }
          }
        }
    }   
    ResourceDictionary rd = LoadBaml<ResourceDictionary>(bamlStream);
    Application.Current.Resources.MergedDictionaries.Add(rd);
    Style style = Application.Current.Resources.MergedDictionaries[0]["myStyle"] as Style;
    button.Style = style;
}

and:

public static T LoadBaml<T>(Stream stream)
{
   var reader = new Baml2006Reader(stream);
   var writer = new XamlObjectWriter(reader.SchemaContext);
   while (reader.Read())
      writer.WriteNode(reader);
   return (T)writer.Result;
}

Update:

If you want to load an image from another libary, you should use the following code:

yourImage.Source = new Bitmap(System.Reflection.Assembly.GetEntryAssembly().
    GetManifestResourceStream("MyProject.Resources.myimage.png"));

Update1:

To load an image from the external dll.

foreach (DictionaryEntry entry in reader)
{
   if (entry.Key.ToString().Equals(name.ToLower()))
   {
        bamlStream = entry.Value as Stream;
        BitmapImage bmp = LoadImage(bamlStream);
        img.Source = bmp;
   }
}

public static BitmapImage LoadImage(Stream stream) 
{ 
   BitmapImage bmi; 
   using (MemoryStream ms1 = new MemoryStream()) 
   { 
      stream.CopyTo(ms1); 
      bmi = new BitmapImage(); 
      bmi.BeginInit(); 
      bmi.StreamSource = new MemoryStream(ms1.ToArray()); 
      bmi.EndInit(); 

   } 
   return bmi; 
}
StepUp
  • 36,391
  • 15
  • 88
  • 148
  • @GTHvidsten soffyr for the late answer. I've been very busy. Feel free to ask any question. If you feel that my reply helps to you, then you can mark my reply as an answer to simplify future search of other people. Please, read this http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work – StepUp Apr 21 '16 at 07:42
  • I get the following exception: `XamlObjectWriterException: Could not register named object. Cannot register duplicate name 'SomeName' in this scope.` where `SomeName` is the name attribute of a MediaElement in an XAML file. The name `SomeName` is only used in that one XAML file, so there must be some sort of internal conflict in `LoadBaml()` – TheHvidsten Apr 21 '16 at 07:49
  • If I add code to skip the entries starting with "views/", I get to the next entry called "resources/myImage.png" which is the one I'm interested in (at this point). Running that through `LoadBaml()` throws the `OutOfMemoryException` described in the original question. – TheHvidsten Apr 21 '16 at 07:53
  • This isn't about loading an image from another library. Because this is a modularized application, the other library (module) tries to use one of the images in it's own resources (RESX file). This library is then loaded through MEF somewhere else. This MEF loading causes the library to not find the images in its own resource file because it has not been registered with the main application (which it can't because it is modularized and the modules shouldn't know about the main application). So, main application needs to load the resources from all libraries loaded by MEF into its own resources. – TheHvidsten Apr 21 '16 at 08:43