2013-01-31 47 views
5

C# .NET 4.0 WinFormsLàm thế nào để bao gồm một hình ảnh trong siêu dữ liệu cho một DLL Plug-in bằng cách sử dụng MEF?

Tôi bắt đầu triển khai mã ví dụ MEF từ this tutorial mô tả việc tạo Thuộc tính xuất khẩu tùy chỉnh cho siêu dữ liệu. Tất cả mọi thứ đã đi bơi cho đến khi tôi cố gắng bao gồm một hình ảnh từ một tập tin tài nguyên trong siêu dữ liệu. Mục đích là trích xuất tiêu đề, mô tả và biểu tượng cho từng plugin DLL làm siêu dữ liệu để xây dựng trình đơn các trình cắm thêm trong chương trình chính.

bây giờ tôi nhận được lỗi biên dịch:

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

Vì vậy, bây giờ tôi có một vấn đề, và cần phải tìm ra một trong hai:

1) Làm thế nào để bao gồm một hình ảnh trong một thuộc tính?

hoặc

2) Làm thế nào để bao gồm siêu dữ liệu trong MEF mà không sử dụng các thuộc tính?

Đây là mã mà tôi đang sử dụng:

Trong lớp hợp đồng:

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

Thuộc tính tùy chỉnh trong Plug-In 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; 
    } 
} 

Và cuối cùng, lớp Chương trình trong Trình cắm thêm 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); 
    } 
} 

Dòng này đang tạo ra lỗi.

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

Rõ ràng là ResourceFile không được xem là một hằng số, nhưng sau đó làm thế nào tôi có thể sử dụng một hình ảnh như siêu dữ liệu, hoặc là không thể? (Lưu ý rằng hình ảnh được đặt là "Được nhúng trong .resx")

Cảm ơn bạn đã được trợ giúp hoặc gợi ý!

Trả lời

3

Tìm thấy một giải pháp:

OK, như xa như tôi có thể nói, bạn có thể không sử dụng hình ảnh hoặc biểu tượng trong siêu dữ liệu MEF. Tuy nhiên, bạn có thể gán một biểu tượng cho tệp DLL, giống như bạn có thể cho các tệp EXE. Các biểu tượng sau đó có thể được đọc bằng cách sử dụng phương pháp tĩnh được xây dựng vào loại Biểu tượng cho NET:

Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath); 

Tôi vẫn không chắc chắn chính xác lý do tại sao bạn có thể trở lại chuỗi từ một tập tin tài nguyên như siêu dữ liệu MEF, nhưng không được nhúng Icons hoặc Hình ảnh.

Vì tôi đã cố gắng ghép nối với nhau một chương trình ví dụ hoạt động cung cấp trình đơn Trình cắm có biểu tượng trong một vài ngày, tôi đã đoán rằng tôi sẽ đăng mã trong trường hợp nó giúp bất kỳ ai khác.


Đây là một ví dụ dự án đầy đủ chức năng với các tính năng sau:

  • Mục đích để có một giải pháp duy nhất với 5 dự án (MainProgram, ContractInterfaces, PlugInA, PlugInB, PlugInC)

  • Sự kiện xây dựng bài đăng sẽ tự động sao chép các tệp DLL từ mỗi dự án vào một thư mục "Plugin" phổ biến

  • Ma dự án inProgram (WinForm) sẽ xây dựng một danh mục các Plugin DLL có sẵn và điền vào một ListView với các Biểu tượng và siêu dữ liệu Tiêu đề của mỗi Trình cắm thêm

  • Nhấp đúp vào mục ListView sẽ khởi tạo Trình cắm (tận dụng Lazy instantiation), và bắt đầu nó.

  • Mỗi trình cắm sẽ nhận được tham chiếu đến Biểu mẫu chính khi khởi động, tạo một TextBox mới và đăng lên Biểu mẫu chính để chứng minh rằng nó chạy và có thể truy cập GUI.

  • Các Tiêu đề, mô tả, và phiên bản giá trị siêu dữ liệu của chọn Plug-In sẽ in ra cửa sổ Console


tôi gán một biểu tượng khác nhau cho mỗi DLL (từ Visual Studio cũ 6 Common Graphics misc thư mục)

Screenshot

các biểu tượng được trích xuất từ ​​các DLL plug-Ins để tạo ra các ListView, và thứ Các TextBox được tạo ra và được đăng bởi các DLL vào GUI khi chúng bắt đầu (sau khi kích đúp vào mỗi mục Plug-In trong ListView).


Thêm mã sau đây để một mới C# WinForm dự án mới mang tên "MainProgram" (tôi sử dụng VS 2010):

Đối với một số lý do các mẫu phân tích cú pháp Mã không thích những điều khoản Sử dụng, vì vậy ở đây họ là điểm bullet:

  • using System;
  • bằng System.Collections.Generic;
  • bằng System.ComponentModel.Composition;
  • bằng System.ComponentModel.Composition.Hosting;
  • using System.Drawing;
  • bằng System.IO;
  • bằng System.Windows.Các hình thức;
  • sử dụng 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; } 
} 

Bây giờ thêm một điều khiển ListView được gọi là "listView1" vào Form chính, và giữ cho nó sang phía bên phải của Mẫu. Các TextBox được tạo động từ Plug-Ins sẽ hiển thị bên trái.


Tiếp theo, thêm một dự án lớp gọi là "ContractInterfaces", sau đó bao gồm mã này:

  • sử dụng System.Windows.Forms;
  • (namespace) 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; } 
} 

Tiếp theo, thêm một dự án lớp gọi là "PlugInA", sau đó bao gồm mã này:

  • using System;
  • bằng System.ComponentModel.Composition;
  • bằng System.Windows.Forms;
  • sử dụng ContractInterfaces;
  • (namespace) 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"; 
    } 
} 

Tiếp theo, thêm một dự án lớp gọi là "PlugInB", sau đó bao gồm mã này:

  • using System;
  • bằng System.ComponentModel.Composition;
  • bằng System.Windows.Forms;
  • sử dụng ContractInterfaces;
  • (namespace) 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"; 
    } 
} 

Tiếp theo, thêm một dự án lớp gọi là "PlugInC", sau đó bao gồm mã này:

  • using System;
  • bằng System.ComponentModel.Composition;
  • bằng System.Windows.Các hình thức;
  • sử dụng 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"; 
    } 
} 

Giải pháp nên xem xét như thế này:

Solution


Nhấp chuột phải vào các giải pháp, và chọn "Proje ct Phụ thuộc ... ". Đặt phụ thuộc như sau:

  • MainProgram - phụ thuộc vào - ContractInterface
  • PlugInA - phụ thuộc vào - ContractInterface
  • PlugInB - phụ thuộc vào - ContractInterface
  • PlugInC - phụ thuộc vào - ContractInterface
  • ContractInterface - phụ thuộc vào - [không có gì]

Nhấp chuột phải vào Giải pháp và chọn "Project Build O rder ... ". Việc xây dựng trật tự nên thực hiện như sau:

  1. ContractInterface
  2. PlugInA
  3. PlugInB
  4. PlugInC
  5. MainProgram

xây dựng và chạy chương trình. Bạn sẽ thấy 3 tệp DLL được sao chép vào một thư mục "Plug-Ins" mới ở cùng cấp thư mục với tệp Solution (* .sln). Nếu không, hãy kiểm tra Thứ tự xây dựng dự án, Phụ thuộc và bạn đã nhập Sự kiện sau khi xây dựng theo ghi chú trong mã Trình cắm ở trên. Nếu các tệp ở đó, thì ListView sẽ được điền trong Biểu mẫu có các mục trình cắm thêm. Bấm đúp vào mỗi mục nhập ListView để khởi động Trình cắm.

Hãy vui vẻ, tôi hy vọng điều này sẽ giúp ai đó ...

+1

Giới hạn bạn gặp phải là vấn đề thuộc tính chứ không phải của MEF. Họ khá hạn chế trong những gì họ có thể nắm giữ. Bạn sẽ ví dụ: đã có thể lưu trữ đường dẫn đến tài nguyên thre dưới dạng chuỗi và sau đó có thuộc tính Hình ảnh mà bạn không đặt nhưng sau đó sử dụng đường dẫn để đọc tài nguyên được nhúng. – flq

+0

Tôi muốn truyền biểu tượng qua Siêu dữ liệu để tôi không phải khởi tạo Hội đồng trình cắm thêm chỉ để tạo một menu. Giải pháp ở trên hoạt động xung quanh vấn đề đó, nhưng tôi tin rằng bạn là chính xác - đó là một vấn đề với những hạn chế đối với các loại đối tượng có thể được nhúng trong các thuộc tính. – user1689175

+1

Nếu tôi bao gồm loại Biểu tượng trong siêu dữ liệu, mọi thứ biên dịch sẽ chạy đến câu lệnh ** container.ComposeParts (this); **, trả về lỗi: _Property 'PlugInA.PluginMetadataAttribute.PlugInIcon' có loại 'System.Drawing .Icon 'là loại siêu dữ liệu không hợp lệ. Siêu dữ liệu chỉ có thể chứa các giá trị có loại có sẵn để được nhúng vào lúc biên dịch thành các thuộc tính. Để biết thêm chi tiết về những loại nào là tham chiếu hợp lệ trong phần 17.1.3 trong đặc tả C#._ – user1689175