模块化WPF中的资源(使用Caliburn.Micro和MEF)

时间:2016-04-18 12:31:57

标签: c# wpf xaml mef caliburn.micro

我已经整天搜索了这个问题的答案,但没有提出任何直接适用于我的案例的解决方案,或任何有效的方法(在我认为适用的一个案例中)。

我有一个Caliburn.Micro框架设置为使用MEF,我加载我的模块化元素就好了。缺少的一件事是让WPF识别我在其中一个模块中使用的资源。

如何在我的app引导程序中加载模块

[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();
}

这样可以正常显示模块。

但是当模块使用了图像时,该图像永远不会显示,因为这种加载显然不会考虑资源。 我在模块项目中创建一个Resources.resx文件,并为其添加一个图像。然后,Visual Studio中显示的图像文件具有“动作”,其中显示“资源”和“不复制(到输出目录)”。这应该意味着图像嵌入在生成的DLL文件中。

图像放在模块项目中名为“Resources”的文件夹中,XAML使用它如下:

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

图像显示在Visual Studio的预览中,但在应用程序运行时不会显示。

我尝试过的功能不起作用

  • 以其他方式引用图片:<Image Source="pack://application:,,,/Resources/myImage.png" />
  • 获取BAML表单中的资源并将其重新插入正在执行的程序集中,如同以下问题:Instantiate ResourceDictionary xaml from other Assembly(导致此行{0}上的OutOfMemoryException)
  • 很多其他引用ResourceDictionary的答案,但我有一个Resource.resx文件(只生成一个不是ResourceDictionary的内部类)

问题仍然存在

如何让WPF / Caliburn.Micro识别MEF加载的DLL中的资源?

2 个答案:

答案 0 :(得分:2)

答案

对于Source

的图片,Build Action: Resource属性使用此语法
<Image Source="/AssemblyName;component/Resources/MyImage.png" />

其中AssemblyName是程序集的名称(在项目属性中定义),/Resource/MyImage.png是图像的路径(在项目中定义)。 component必须始终存在。

旁注

经过@StepUp的大量帮助,我最初决定使用从这个问题中学到的东西来提出一个新问题,并将所有内容改为更具体地解决我的问题。

在撰写这个新问题时,我最终搜索了可能有助于重新措辞的短语和命令,我偶然发现了这个页面:http://www.geekchamp.com/tips/wp7-working-with-images-content-vs-resource-build-action

显然,WPF Image控件有很多方法可以定义Source属性。我已经尝试了很多不同的Source输入,并且认为我已经尝试了所有这些,但是上面链接的页面证明我错了。

就我能够测试而言,上述语法似乎适用于标有Build Action: Resource的图像。因此,我不再需要为图像提供RESX文件,并且在引导MEF时我不需要任何特殊处理。

答案 1 :(得分:0)

首先,您应该使用Style阅读程序集。然后,有必要使用Baml2006Reader从外部库中读取BAML文件。让我举个例子:

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;
}

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;
}

<强>更新

如果要从其他库中加载图像,则应使用以下代码:

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

<强> UPDATE1:

从外部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; 
}