use*_*175 5 c# plugins mef metadata image
C#.NET 4.0 WinForms
我开始实现本教程中的 MEF示例代码,该代码描述了为元数据创建自定义ExportAttribute属性.在我尝试在元数据中包含资源文件中的图像之前,一切都在顺利进行.目标是将每个插件DLL的标题,描述和图标提取为元数据,以便在主程序中构建插件菜单.
我现在得到编译错误:
"属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式"
所以现在我有一个问题,需要弄清楚:
1)如何在属性中包含图像?
要么
2)如何在不使用属性的情况下在MEF中包含元数据?
这是我正在使用的代码:
在合同类中:
// Metadata contract interface
public interface IPlugInMetadata
{
string PlugInTitle { get; }
string PlugInDescription { get; }
Image PlugInIcon { get; }
}
// Plug-In contract interface
public interface IPlugIn
{
void StartPlugIn(object systemObject);
void StopPlugin();
}
Run Code Online (Sandbox Code Playgroud)
插件DLL中的自定义属性:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
{
public string title { get; set; }
public string description { get; set; }
public Image icon { get; set; }
public PluginMetadataAttribute(string plugInTitle, string plugInDescription, Image plugInIcon)
: base(typeof(IPlugInMetadata))
{
title = plugInTitle;
description = plugInDescription;
icon = plugInIcon;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,Plug-In DLL中的Program类:
[Export(typeof(IPlugIn))]
[PluginMetadata(ResourceFile.PlugInTitle, ResourceFile.PlugInDescription, ResourceFile.PlugInIcon24)]
public class Program : IPlugIn
{
public void StartPlugIn(object systemObject)
{
Console.WriteLine("Start Plug-In: " + ResourceFile.PlugInTitle);
}
public void StopPlugin()
{
Console.WriteLine("Stop Plug-In: " + ResourceFile.PlugInTitle);
}
}
Run Code Online (Sandbox Code Playgroud)
这条线产生错误.
[PluginMetadata(ResourceFile.PlugInTitle, ResourceFile.PlugInDescription, ResourceFile.PlugInIcon24)]
Run Code Online (Sandbox Code Playgroud)
显然,ResourceFile不被视为常量,但是我如何将图像用作元数据,或者这是不可能的? (请注意,图像设置为"嵌入.resx")
感谢您的任何帮助或建议!
找到了解决办法:
好的,据我所知,您不能在 MEF 元数据中使用图像或图标。不过,您可以为 DLL 文件指定一个图标,就像为 EXE 文件指定图标一样。然后可以使用内置于 .NET Icon 类型中的静态方法读取图标:
Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath);
Run Code Online (Sandbox Code Playgroud)
我仍然不太确定为什么可以从资源文件中返回字符串作为 MEF 元数据,但不能返回嵌入的图标或图像。
由于几天来我一直在尝试拼凑一个功能示例程序,该程序提供带有图标的插件菜单,因此我想我会发布代码,以防它对其他人有帮助。
这是一个功能齐全的示例项目,具有以下功能:
旨在成为具有 5 个项目的单一解决方案(MainProgram、ContractInterfaces、PlugInA、PlugInB、PlugInC)
构建后事件会自动将每个项目中的 DLL 复制到公共“Plug-Ins”文件夹中
MainProgram (WinForm) 项目将构建可用 DLL 插件的目录,并使用每个插件的图标和元数据标题填充 ListView
双击 ListView 项目将实例化插件(利用延迟实例化)并启动它。
每个插件将在启动时收到对主窗体的引用,创建一个新的文本框,并将其发布到主窗体以证明它已运行并且可以访问 GUI。
所选插件的标题、描述和版本元数据值将打印到控制台窗口
我为每个 DLL 分配了不同的图标(来自旧的 Visual Studio 6 Common Graphics Misc 文件夹)

从 DLL 插件中提取图标以创建 ListView,并且在启动后由 DLL 创建文本框并将其发布到 GUI(双击 ListView 中的每个插件项后)。
将以下代码添加到名为“MainProgram”的全新 C# WinForm 项目中(我使用的是 VS 2010):
由于某种原因,代码示例解析器不喜欢使用语句,因此这里将它们作为要点:
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; }
}
Run Code Online (Sandbox Code Playgroud)
现在将一个名为“listView1”的 ListView 控件添加到主窗体中,并将其保留在窗体的右侧。从插件动态创建的文本框将显示在左侧。
接下来添加一个名为“ContractInterfaces”的类项目,然后包含以下代码:
// Prerequisites to run:
// 1) Project, Add Reference, .NET, "System.Windows.Forms"
public interface IPlugIn
{
void StartPlugIn(Form mainForm);
}
public interface IPlugInMetadata
{
string PlugInTitle { get; }
string PlugInDescription { get; }
string PlugInVersion { get; }
}
Run Code Online (Sandbox Code Playgroud)
接下来添加一个名为“PlugInA”的类项目,然后包含以下代码:
// Prerequisites to run:
// 1) Project, Add Reference, Projects, "ContractInterface"
// 2) Project, Add Reference, .NET, "System.Windows.Forms"
// 3) Project, Add Reference, .NET, "System.ComponentModel.Composition"
// 4) Project, Properties, Build Events, Post-Build event command line:
// xcopy "$(ProjectDir)$(OutDir)$(TargetFileName)" "$(SolutionDir)Plug-Ins\" /Y
// 5) Project, Properties, Build Events, Run the post-build event:, Always
// 6) Project, Properties, Application, Icon and manifest, [Select an icon]
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
{
private Form MainForm;
public void StartPlugIn(Form mainForm)
{
MainForm = mainForm;
// Place a TextBox on the Main Form
TextBox textBox = new TextBox();
textBox.Text = "PlugInA";
MainForm.Controls.Add(textBox);
textBox.Width = 65;
textBox.Height = 20;
textBox.Top = 0;
textBox.Left = 0;
}
}
// Create a custom strong-typed Metadata Attribute for MEF
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
{
public string PlugInTitle { get; set; }
public string PlugInDescription { get; set; }
public object PlugInVersion { get; set; }
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
{
PlugInTitle = "Plug-In A";
PlugInDescription = "This is Plug-In A";
PlugInVersion = "1.0.0.0";
}
}
Run Code Online (Sandbox Code Playgroud)
接下来添加一个名为“PlugInB”的类项目,然后包含以下代码:
// Prerequisites to run:
// 1) Project, Add Reference, Projects, "ContractInterface"
// 2) Project, Add Reference, .NET, "System.Windows.Forms"
// 3) Project, Add Reference, .NET, "System.ComponentModel.Composition"
// 4) Project, Properties, Build Events, Post-Build event command line:
// xcopy "$(ProjectDir)$(OutDir)$(TargetFileName)" "$(SolutionDir)Plug-Ins\" /Y
// 5) Project, Properties, Build Events, Run the post-build event:, Always
// 6) Project, Properties, Application, Icon and manifest, [Select an icon]
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
{
private Form MainForm;
public void StartPlugIn(Form mainForm)
{
MainForm = mainForm;
// Place a TextBox on the Main Form
TextBox textBox = new TextBox();
textBox.Text = "PlugInB";
MainForm.Controls.Add(textBox);
textBox.Width = 65;
textBox.Height = 20;
textBox.Top = 30;
textBox.Left = 0;
}
}
// Create a custom strong-typed Metadata Attribute for MEF
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
{
public string PlugInTitle { get; set; }
public string PlugInDescription { get; set; }
public object PlugInVersion { get; set; }
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
{
PlugInTitle = "Plug-In B";
PlugInDescription = "This is Plug-In B";
PlugInVersion = "1.0.0.1";
}
}
Run Code Online (Sandbox Code Playgroud)
接下来添加一个名为“PlugInC”的类项目,然后包含以下代码:
// Prerequisites to run:
// 1) Project, Add Reference, Projects, "ContractInterface"
// 2) Project, Add Reference, .NET, "System.Windows.Forms"
// 3) Project, Add Reference, .NET, "System.ComponentModel.Composition"
// 4) Project, Properties, Build Events, Post-Build event command line:
// xcopy "$(ProjectDir)$(OutDir)$(TargetFileName)" "$(SolutionDir)Plug-Ins\" /Y
// 5) Project, Properties, Build Events, Run the post-build event:, Always
// 6) Project, Properties, Application, Icon and manifest, [Select an icon]
[Export(typeof(IPlugIn))]
[PluginMetadata]
public class Program : IPlugIn
{
private Form MainForm;
public void StartPlugIn(Form mainForm)
{
MainForm = mainForm;
// Place a TextBox on the Main Form
TextBox textBox = new TextBox();
textBox.Text = "PlugInC";
MainForm.Controls.Add(textBox);
textBox.Width = 65;
textBox.Height = 20;
textBox.Top = 60;
textBox.Left = 0;
}
}
// Create a custom strong-typed Metadata Attribute for MEF
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginMetadataAttribute : ExportAttribute
{
public string PlugInTitle { get; set; }
public string PlugInDescription { get; set; }
public object PlugInVersion { get; set; }
public PluginMetadataAttribute()
: base(typeof(IPlugInMetadata))
{
PlugInTitle = "Plug-In C";
PlugInDescription = "This is Plug-In C";
PlugInVersion = "1.0.0.2";
}
}
Run Code Online (Sandbox Code Playgroud)
解决方案应该如下所示:

右键单击解决方案,然后选择“项目依赖项...”。设置依赖关系如下:
右键单击解决方案,然后选择“项目构建顺序...”。构建顺序应如下所示:
构建并运行程序。您应该看到 3 个 DLL 文件被复制到与解决方案文件 (*.sln) 位于同一目录级别的新“Plug-Ins”文件夹中。如果没有,请检查项目构建顺序、依赖关系,以及您是否根据上面插件代码中的注释输入了构建后事件。如果文件存在,则列表视图中应使用插件条目填充表单中。双击每个 ListView 条目以启动插件。
玩得开心,我希望这对某人有帮助......