【问题标题】:Resources in modularized WPF (with Caliburn.Micro and MEF)模块化 WPF 中的资源(使用 Caliburn.Micro 和 MEF)
【发布时间】:2016-04-18 12:31:56
【问题描述】:

我整天都在寻找这个问题的答案,但没有提出任何直接适用于我的案例的解决方案,或者任何可行的解决方案(在我发现的一个案例中是适用的)。

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

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

[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 的预览中显示,但在应用程序运行时不显示。

我尝试过的方法不起作用

  • 以另一种方式引用图像:&lt;Image Source="pack://application:,,,/Resources/myImage.png" /&gt;
  • 以 BAML 形式获取资源并将它们重新插入到正在执行的程序集中,例如以下问题:Instantiate ResourceDictionary xaml from other Assembly(这会导致此行出现 OutOfMemoryException var reader = new Baml2006Reader(stream);
  • 很多其他答案都引用了 ResourceDictionary,但我有一个 Resource.resx 文件(它只生成一个不是 ResourceDictionary 的内部类)

问题依然存在

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

【问题讨论】:

  • 该问题涉及嵌入在 XAML 文件中的 ResourceDictionaries。正如我指定的,我没有 ResourceDictionaries,而是一个 RESX 文件。
  • 在哪里将DictionaryEntry 插入主应用程序?
  • 哎呀,DictionaryEntry 是什么意思?
  • 您链接到的问题具有以下代码:foreach (DictionaryEntry entry in reader) 这很好,因为我认为它包含我的 RESX 文件中的内容。但是,一旦我得到了DictionaryEntry,我该怎么办?我相信我应该把它放在某个地方(比如Application.Current.Resources),但我找不到任何接受DictionaryEntry 作为输入的东西。

标签: c# wpf xaml mef caliburn.micro


【解决方案1】:

回答

对带有Build Action: Resource 的图像的Source 属性使用此语法

<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 时不需要任何特殊处理。

【讨论】:

    【解决方案2】:

    首先,您应该阅读带有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"));
    

    更新1:

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

    【讨论】:

    • @GTHvidsten soffyr 为迟到的答案。我一直很忙。随时问任何问题。如果您觉得我的回复对您有帮助,那么您可以将我的回复标记为答案,以简化日后其他人的搜索。请阅读此meta.stackexchange.com/questions/5234/…
    • 我得到以下异常:XamlObjectWriterException: Could not register named object. Cannot register duplicate name 'SomeName' in this scope. 其中SomeName 是 XAML 文件中 MediaElement 的名称属性。 SomeName这个名字只用在那个XAML文件中,所以LoadBaml()里面肯定有某种内部冲突
    • 如果我添加代码以跳过以“views/”开头的条目,我会进入下一个名为“resources/myImage.png”的条目,这是我感兴趣的条目(此时)。通过LoadBaml() 运行它会抛出原始问题中描述的OutOfMemoryException
    • 这不是从另一个库加载图像。因为这是一个模块化应用程序,所以另一个库(模块)尝试使用它自己的资源(RESX 文件)中的一个图像。然后通过 MEF 在其他地方加载该库。此 MEF 加载导致库无法在其自己的资源文件中找到图像,因为它尚未向主应用程序注册(它不能注册,因为它是模块化的,并且模块不应该知道主应用程序)。因此,主应用程序需要将 MEF 加载的所有库中的资源加载到自己的资源中。
    猜你喜欢
    • 2012-11-26
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    相关资源
    最近更新 更多