找到了解决方案:
好的,据我所知,您无法在MEF元数据中使用图像或图标。 但是,您可以为DLL文件分配一个图标,就像可以为EXE文件一样。 然后可以使用内置于.NET的Icon类型的静态方法读取图标:
Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath)
我仍然不确定为什么可以从资源文件中作为MEF元数据返回字符串,但不能返回嵌入的图标或图像。
由于我一直在努力组合一个具有插件菜单和图标的功能示例程序已经几天了,所以我想发布代码以帮助其他人。
这是一个具有以下功能的完全功能示例项目:
旨在成为一个包含5个项目的单一解决方案(MainProgram、ContractInterfaces、PlugInA、PlugInB、PlugInC)
后置生成事件将自动将每个项目的DLL复制到公共的“插件”文件夹中
MainProgram(WinForm)项目将构建可用的DLL插件目录,并使用每个插件的图标和元数据标题填充ListView
双击ListView项将实例化插件(利用延迟实例化),并启动它。
每个插件将在启动时接收对主窗体的引用,创建一个新的TextBox,并将其发布到主窗体以证明它已运行并且可以访问GUI。
所选插件的标题、描述和版本元数据值将打印到控制台窗口
我为每个DLL分配了不同的图标(来自旧的Visual Studio 6 Common Graphics Misc文件夹)
![Screenshot](https://istack.dev59.com/M9v0G.webp)
这些图标是从DLL插件中提取出来创建ListView的,TextBox是由DLL在启动后创建并发布到GUI的(在双击ListView中的每个插件项之后)。
将以下代码添加到名为“MainProgram”的全新C# WinForm项目中(我使用的是VS 2010):
由于某种原因,代码示例解析器似乎不喜欢Using语句,因此这里将它们作为项目列出:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.Composition;
- using System.ComponentModel.Composition.Hosting;
- using System.Drawing;
- using System.IO;
- using System.Windows.Forms;
- using ContractInterfaces;
- (namespace) MainProgram
public partial class Form1 : Form
{
// Prerequisites to run:
// 1) Project, Add Reference, Projects, ContractInterface
// 2) Project, Add Reference, .NET, System.ComponentModel.Composition
[ImportMany(typeof(IPlugIn))]
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> LoadedPlugIns;
List<PlugInInfo> AvailablePlugIns = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Get a list of the available Plug-Ins
AvailablePlugIns = GetPlugInList();
// Prepare an ImageList to hold the DLL icons
ImageList ImgList = new ImageList();
ImgList.ColorDepth = ColorDepth.Depth32Bit;
ImgList.ImageSize = new Size(32, 32);
// Populate ImageList with Plug-In Icons
foreach (var item in AvailablePlugIns)
{
ImgList.Images.Add(item.PlugInIcon.ToBitmap());
}
// Assign the ImageList to the ListView
listView1.LargeImageList = ImgList;
int imageIndex = 0;
// Create the ListView items
foreach (var item in AvailablePlugIns)
{
listView1.Items.Add(item.PlugInTitle, imageIndex);
imageIndex++;
}
listView1.MouseDoubleClick += new MouseEventHandler(listView1_MouseDoubleClick);
}
void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
// Get the Plug-In index number
int plugInNum = listView1.SelectedItems[0].Index;
PlugInInfo selectedPlugIn = AvailablePlugIns[plugInNum];
// Call the StartPlugIn method in the selected Plug-In.
// Lazy Instantiation will fully load the Assembly here
selectedPlugIn.PlugIn.StartPlugIn(this);
Console.WriteLine("Plug-In Title: {0}", selectedPlugIn.PlugInTitle);
Console.WriteLine("Plug-In Description: {0}", selectedPlugIn.PlugInDescription);
Console.WriteLine("Plug-In Version: {0}", selectedPlugIn.PlugInVersion);
Console.WriteLine();
}
private List<PlugInInfo> GetPlugInList()
{
// Create a List to hold the info for each plug-in
List<PlugInInfo> plugInList = new List<PlugInInfo>();
// Set Plug-In folder path to same directory level as Solution
string plugInFolderPath = System.IO.Path.Combine(Application.StartupPath, @"..\..\..\Plug-Ins");
// Test if the Plug-In folder exists
if (!Directory.Exists(plugInFolderPath))
{
// Plug-In Folder is missing, so try to create it
try
{ Directory.CreateDirectory(plugInFolderPath); }
catch
{ MessageBox.Show("Failed to create Plug-In folder", "Folder Creation Error:", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); }
}
try
{
// Create a catalog of plug-ins
var catalog = new DirectoryCatalog(plugInFolderPath, "*.dll");
AggregateCatalog plugInCatalog = new AggregateCatalog();
plugInCatalog.Catalogs.Add(catalog);
CompositionContainer container = new CompositionContainer(plugInCatalog);
// This line will fetch the metadata from each plug-in and populate LoadedPlugIns
container.ComposeParts(this);
// Save each Plug-Ins metadata
foreach (var plugin in LoadedPlugIns)
{
PlugInInfo info = new PlugInInfo();
info.PlugInTitle = plugin.Metadata.PlugInTitle;
info.PlugInDescription = plugin.Metadata.PlugInDescription;
info.PlugInVersion = plugin.Metadata.PlugInVersion;
info.PlugIn = plugin.Value;
plugInList.Add(info);
}
int index = 0;
// Extract icons from each Plug-In DLL and store in Plug-In list
foreach (var filePath in catalog.LoadedFiles)
{
plugInList[index].PlugInIcon = Icon.ExtractAssociatedIcon(filePath);
index++;
}
}
catch (FileNotFoundException fex)
{
Console.WriteLine("File not found exception : " + fex.Message);
}
catch (CompositionException cex)
{
Console.WriteLine("Composition exception : " + cex.Message);
}
catch (DirectoryNotFoundException dex)
{
Console.WriteLine("Directory not found exception : " + dex.Message);
}
return plugInList;
}
}
public class PlugInInfo
{
public string PlugInTitle { get; set; }
public string PlugInDescription { get; set; }
public string PlugInVersion { get; set; }
public Icon PlugInIcon { get; set; }
public IPlugIn PlugIn { get; set; }
}
现在在主窗体中添加一个名为"listView1"的ListView控件,并将其保留在窗体的右侧。插件动态创建的文本框将显示在左侧。
接下来添加一个名为"ContractInterfaces"的类项目,然后包含以下代码:
- using System.Windows.Forms;
- (命名空间) ContractInterfaces
public interface IPlugIn
{
void StartPlugIn(Form mainForm);
}
public interface IPlugInMetadata
{
string PlugInTitle { get; }
string PlugInDescription { get; }
string PlugInVersion { get; }
}
接下来添加一个名为“PlugInA”的类项目,然后包含以下代码:
- using System;(使用系统)
- using System.ComponentModel.Composition;(使用组件)
- using System.Windows.Forms;(使用Windows窗体)
- using ContractInterfaces;(使用合同接口)
- (namespace) PlugInA(命名空间为PlugInA)
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
public string PlugInDescription
public object PlugInVersion
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
}
接下来添加一个名为 "PlugInB" 的类项目,然后包含以下代码:
- using System; (使用系统库)
- using System.ComponentModel.Composition;(使用组件组合库)
- using System.Windows.Forms;(使用窗体库)
- using ContractInterfaces;(使用契约接口)
- (namespace) PlugInB(命名空间为PlugInB)
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
public string PlugInDescription
public object PlugInVersion
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
}
接下来添加一个名为“PlugInC”的类项目,然后包含以下代码:
- 使用System;
- 使用System.ComponentModel.Composition;
- 使用System.Windows.Forms;
- 使用ContractInterfaces;
- (命名空间) PlugInC
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
public string PlugInDescription
public object PlugInVersion
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
}
解决方案应该长这样:
![Solution](https://istack.dev59.com/5geY7.webp)
右键单击解决方案,然后选择“项目依赖项...”。设置如下依赖项:
- MainProgram - 依赖于 - ContractInterface
- PlugInA - 依赖于 - ContractInterface
- PlugInB - 依赖于 - ContractInterface
- PlugInC - 依赖于 - ContractInterface
- ContractInterface - 依赖于 - [无]
右键单击解决方案,然后选择“项目构建顺序...”。构建顺序如下:
1. ContractInterface
2. PlugInA
3. PlugInB
4. PlugInC
5. MainProgram
构建并运行程序。你应该可以在与解决方案文件(*.sln)在同一目录级别的新“Plug-Ins”文件夹中看到三个DLL文件被复制。如果没有,请检查项目构建顺序、依赖项以及是否按照插件代码中的注释输入了Post-Build事件。如果文件存在,则窗体中的ListView应该被插件条目填充。双击每个ListView条目以启动插件。
玩得开心,希望这对某个人有所帮助....