找到了解決辦法:
OK,據我所知,你不能在MEF元數據使用圖像或圖標。但是,您可以爲DLL文件分配一個圖標,就像EXE文件一樣。該圖標然後可以使用內置的圖標類型爲.NET靜態方法閱讀:
Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath);
我仍然不知道是什麼原因,你可以從一個資源文件作爲MEF元數據中返回的字符串,但不嵌入圖標或圖片。
由於我一直試圖拼湊一個功能強大的示例程序,該程序提供了插圖菜單和圖標幾天,所以我想我會發布代碼以防其他人幫助。
這是具有以下功能的全功能示例項目:
意在與5個項目的單一解決方案(MainProgram(主程序),ContractInterfaces,PlugInA,PlugInB,PlugInC)
Post Build Events會自動將每個項目的DLL複製到一個通用的「Plug-Ins」文件夾中
The Ma inProgram(WinForm)項目將構建可用DLL插件的目錄並使用每個插件的圖標和元數據標題填充ListView
雙擊ListView項目將實例化插件(利用懶惰實例化),並啓動它。
每個插件將在啓動時收到對主窗體的引用,創建一個新的文本框,並將其發佈到主窗體以證明它運行並可以訪問GUI。
標題,所選擇的插件的描述,和版本的元數據值將打印到控制檯窗口
我分配一個不同的圖標,以每個DLL(從舊的Visual Studio 6通用圖形Misc文件夾)
的圖標從該DLL插件解壓縮以創建的ListView,和第e文本框是在DLL啓動後(在雙擊ListView中的每個插件項後)由DLL創建併發布到GUI的。
下面的代碼添加到所謂的「MainProgram(主程序)」一個全新的C#WinForm的項目(我用VS 2010):
出於某種原因,代碼示例解析器不順心的使用說明,所以在這裏,他們都作爲要點:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.Composition;
- using System.ComponentModel.Composition.Hosting;
- using System.Drawing;
- using System.IO;
- using System.Windows。形式;
- 使用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」 一類項目,則包括此代碼:
- 使用System.Windows.Forms的;
- (命名空間)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; }
}
接着添加稱爲 「PlugInA」 一類項目,則包括此代碼:
- 使用系統;
- using System.ComponentModel.Composition;
- using System.Windows.Forms;
- 使用ContractInterfaces;
- (命名空間)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";
}
}
接着添加稱爲 「PlugInB」 一類項目,則包括此代碼:
- 使用系統;
- using System.ComponentModel.Composition;
- using System.Windows.Forms;
- 使用ContractInterfaces;
- (命名空間)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";
}
}
接着添加稱爲 「PlugInC」 一類項目,則包括此代碼:
- 使用系統;
- using System.ComponentModel.Composition;
- using System.Windows。形式;
- 使用ContractInterfaces;
- 空間(namespace)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";
}
}
解決方案應該是這樣的:
的解決方案單擊鼠標右鍵,並選擇「幻燈ct依賴......「。設置的依賴關係如下:
- MainProgram(主程序) - 依賴 - ContractInterface
- PlugInA - 依賴 - ContractInterface
- PlugInB - 依賴 - ContractInterface
- PlugInC - 依賴 - ContractInterface
- ContractInterface - 取決於 - [nothing]
右鍵單擊Solution,然後選擇「Project Build O刻申...「。構建順序應該如下:
- ContractInterface
- PlugInA
- PlugInB
- PlugInC
- MainProgram(主程序)
生成並運行該程序。您應該將3個DLL文件複製到與解決方案文件(* .sln)相同的目錄級別的新「Plug-Ins」文件夾中。如果沒有,請根據上面插件代碼中的註釋檢查項目構建順序,依賴項以及您輸入構建後事件。如果這些文件存在,那麼ListView應該填充到具有Plug-In條目的表單中。雙擊每個ListView條目以啓動插件。
玩得開心,我希望這可以幫助別人....
你偶然發現的限制是屬性問題,而不是MEF問題。他們所能掌握的東西相當有限。你會例如已經能夠存儲將字符串資源作爲字符串的路徑,然後擁有一個Image屬性,您不會設置它,然後使用該路徑來讀取嵌入的資源。 – flq
我想通過元數據傳遞圖標,這樣我就不必爲了創建菜單而實例化插件程序集。上面的解決方案解決了這個問題,但我相信你是正確的 - 這是一個限制嵌入到屬性中的對象類型的問題。 – user1689175
如果我在元數據中包含一個Icon類型,則所有編譯都運行到語句** container.ComposeParts(this); **,它將返回錯誤:_Property'PlugInA.PluginMetadataAttribute.PlugInIcon'類型爲'System.Drawing .Icon'這是一個無效的元數據類型。元數據只能包含具有可在編譯時嵌入到屬性中的類型的值。有關C#規範中哪些類型是有效的參考部分17.1.3的更多詳細信息._ – user1689175