2013-01-31 22 views
5

C#.NET 4.0 WinForms如何使用MEF在插件DLL的元數據中包含圖像?

我開始從this tutorial實現MEF示例代碼,該代碼描述了爲元數據創建自定義ExportAttribute屬性。一切都在順利進行,直到我試圖在元數據中包含來自資源文件的圖像。目標是將每個插件DLL的標題,描述和圖標提取爲用於在主程序中構建插件菜單的元數據。

我現在得到的編譯錯誤:

"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"

所以現在我有一個問題,需要弄清楚之一:

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

在插件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; 
    } 
} 

最後,插件DLL中的程序類:

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

此行發生錯誤。

[PluginMetadata(ResourceFile.PlugInTitle, ResourceFile.PlugInDescription, ResourceFile.PlugInIcon24)] 

顯然是的resourcefile不考慮恆定的,但後來我如何可以使用圖像元數據,或者是不可能的?(注意,圖像設置爲「嵌入.resx」)

感謝您的任何幫助或建議!

回答

3

找到了解決辦法:

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文件夾)

Screenshot

的圖標從該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"; 
    } 
} 

解決方案應該是這樣的:

Solution


的解決方案單擊鼠標右鍵,並選擇「幻燈ct依賴......「。設置的依賴關係如下:

  • MainProgram(主程序) - 依賴 - ContractInterface
  • PlugInA - 依賴 - ContractInterface
  • PlugInB - 依賴 - ContractInterface
  • PlugInC - 依賴 - ContractInterface
  • ContractInterface - 取決於 - [nothing]

右鍵單擊Solution,然後選擇「Project Build O刻申...「。構建順序應該如下:

  1. ContractInterface
  2. PlugInA
  3. PlugInB
  4. PlugInC
  5. MainProgram(主程序)

生成並運行該程序。您應該將3個DLL文件複製到與解決方案文件(* .sln)相同的目錄級別的新「Plug-Ins」文件夾中。如果沒有,請根據上面插件代碼中的註釋檢查項目構建順序,依賴項以及您輸入構建後事件。如果這些文件存在,那麼ListView應該填充到具有Plug-In條目的表單中。雙擊每個ListView條目以啓動插件。

玩得開心,我希望這可以幫助別人....

+1

你偶然發現的限制是屬性問題,而不是MEF問題。他們所能掌握的東西相當有限。你會例如已經能夠存儲將字符串資源作爲字符串的路徑,然後擁有一個Image屬性,您不會設置它,然後使用該路徑來讀取嵌入的資源。 – flq

+0

我想通過元數據傳遞圖標,這樣我就不必爲了創建菜單而實例化插件程序集。上面的解決方案解決了這個問題,但我相信你是正確的 - 這是一個限制嵌入到屬性中的對象類型的問題。 – user1689175

+1

如果我在元數據中包含一個Icon類型,則所有編譯都運行到語句** container.ComposeParts(this); **,它將返回錯誤:_Property'PlugInA.PluginMetadataAttribute.PlugInIcon'類型爲'System.Drawing .Icon'這是一個無效的元數據類型。元數據只能包含具有可在編譯時嵌入到屬性中的類型的值。有關C#規範中哪些類型是有效的參考部分17.1.3的更多詳細信息._ – user1689175

相關問題