简体   繁体   English

如何在使用MEF的插件DLL的元数据中包含图像?

[英]How to include an image in metadata for a Plug-In DLL using MEF?

C# .NET 4.0 WinForms C#.NET 4.0 WinForms

I started implementing MEF example code from this tutorial which describes creating a custom ExportAttribute Attribute for metadata. 我开始实现本教程中的 MEF示例代码, 代码描述了为元数据创建自定义ExportAttribute属性。 Everything was going swimmingly until I tried to include an image from a Resource File in the metadata. 在我尝试在元数据中包含资源文件中的图像之前,一切都在顺利进行。 The goal being to extract the title, description, and icon for each plug-in DLL as metadata for constructing a menu of plug-ins in the main program. 目标是将每个插件DLL的标题,描述和图标提取为元数据,以便在主程序中构建插件菜单。

I now get the compile error: 我现在得到编译错误:

"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type" “属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式”

So now I have a problem, and need to figure out either: 所以现在我有一个问题,需要弄清楚:

1) How to include an image in a attribute ? 1)如何在属性中包含图像?

or 要么

2) How to include metadata in MEF without using attributes ? 2)如何在不使用属性的情况下在MEF中包含元数据?

Here is the code that I am using: 这是我正在使用的代码:

In the contract class: 在合同类中:

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

The custom attribute in the Plug-In DLL: 插件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;
    }
}

And finally, the Program class in the Plug-In DLL: 最后,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);
    }
}

This line is producing the error. 这条线产生错误。

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

Obviously the ResourceFile is not considered a constant, but then how can I use an image as metadata, or is that not possible ? 显然,ResourceFile不被视为常量,但是我如何将图像用作元数据,或者这是不可能的? ( Note that the image is set as "Embedded in .resx" ) (请注意,图像设置为“嵌入.resx”)

Thanks for any help or suggestions ! 感谢您的任何帮助或建议!

Found a solution: 找到了解决方案:

OK, as far as I can tell, you cannot use images or icons in MEF metadata. 好的,据我所知,你不能在MEF元数据中使用图像或图标。 You can however assign an icon to the DLL file, just as you can for EXE files. 但是,您可以为DLL文件指定一个图标,就像为EXE文件一样。 The icon can then be read using a static method built into the Icon type for .NET: 然后可以使用.NET类型中内置的静态方法读取图标:

Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath);

I am still not exactly sure why you can return strings from a Resource file as MEF metadata, but not embedded Icons or Images. 我仍然不确定为什么你可以将资源文件中的字符串作为MEF元数据返回,而不是嵌入的图标或图像。

Since I have been trying to piece together a functioning example program that provides a Plug-In menu with icons for a couple of days now, I figured that I would post the code in case it helps anyone else. 由于我一直试图拼凑一个功能性的示例程序,该程序现在提供带有图标的插件菜单几天,我想我会发布代码,以防它帮助其他任何人。


This is a fully functional example project with the following features: 这是一个功能齐全的示例项目,具有以下功能:

  • Intended to be a single Solution with 5 Projects ( MainProgram, ContractInterfaces, PlugInA, PlugInB, PlugInC ) 打算成为一个包含5个项目的单一解决方案(MainProgram,ContractInterfaces,PlugInA,PlugInB,PlugInC)

  • Post Build Events will automatically copy the DLLs from each project to a common "Plug-Ins" folder Post Build Events会自动将每个项目的DLL复制到一个通用的“Plug-Ins”文件夹中

  • The MainProgram (WinForm) project will build a catalog of the available DLL Plug-Ins and populate a ListView with the Icons and metadata Title of each Plug-In MainProgram(WinForm)项目将构建可用DLL插件的目录,并使用每个插件的图标和元数据标题填充ListView

  • Double-Clicking the ListView item will instantiate the Plug-In ( leveraging Lazy instantiation ), and start it. 双击ListView项将实例化插件(利用Lazy实例化),然后启动它。

  • Each Plug-In will receive a reference to the main Form at start up, create a new TextBox, and post it to the main Form to prove that it ran and can access the GUI. 每个插件将在启动时接收对主窗体的引用,创建一个新的TextBox,并将其发布到主窗体以证明它已运行并可以访问GUI。

  • The Title, Description, and Version metadata values of the selected Plug-In will print to the Console Window 所选插件的标题,描述和版本元数据值将打印到控制台窗口


I assigned a different Icon to each DLL ( from the old Visual Studio 6 Common Graphics Misc folder ) 我为每个DLL分配了一个不同的图标(来自旧的Visual Studio 6 Common Graphics Misc文件夹)

截图

The icons were extracted from the DLL Plug-Ins to create the ListView, and the TextBoxes were created and posted by the DLLs to the GUI once they started ( after double-clicking each Plug-In item in the ListView ). 从DLL插件中提取图标以创建ListView,并且一旦它们启动(在双击ListView中的每个插件项之后),DLL就会创建文档框并将其发布到GUI。


Add the following code to a fresh new C# WinForm project called "MainProgram" ( I used VS 2010 ): 将以下代码添加到名为“MainProgram”的全新C#WinForm项目中(我使用的是VS 2010):

For some reason the Code Sample parser is not liking the Using statements, so here they are as bullet points: 由于某种原因,Code Sample解析器不喜欢Using语句,因此它们作为要点:

  • using System; 使用系统;
  • using System.Collections.Generic; 使用System.Collections.Generic;
  • using System.ComponentModel.Composition; 使用System.ComponentModel.Composition;
  • using System.ComponentModel.Composition.Hosting; 使用System.ComponentModel.Composition.Hosting;
  • using System.Drawing; 使用System.Drawing;
  • using System.IO; 使用System.IO;
  • using System.Windows.Forms; 使用System.Windows.Forms;
  • using ContractInterfaces; 使用ContractInterfaces;
  • (namespace) MainProgram (命名空间)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; }
}

Now add a ListView Control called "listView1" to the main Form, and keep it to the Right side of the Form. 现在将一个名为“listView1”的ListView控件添加到主窗体,并将其保存到窗体的右侧。 The dynamically created TextBoxes from the Plug-Ins will show on the left. 插件中动态创建的TextBox将显示在左侧。


Next add a Class Project called "ContractInterfaces", then include this code: 接下来添加一个名为“ContractInterfaces”的类项目,然后包含以下代码:

  • using System.Windows.Forms; 使用System.Windows.Forms;
  • (namespace) ContractInterfaces (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; }
}

Next add a Class Project called "PlugInA", then include this code: 接下来添加一个名为“PlugInA”的类项目,然后包含以下代码:

  • using System; 使用系统;
  • using System.ComponentModel.Composition; 使用System.ComponentModel.Composition;
  • using System.Windows.Forms; 使用System.Windows.Forms;
  • using ContractInterfaces; 使用ContractInterfaces;
  • (namespace) PlugInA (名称空间)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";
    }
}

Next add a Class Project called "PlugInB", then include this code: 接下来添加一个名为“PlugInB”的类项目,然后包含以下代码:

  • using System; 使用系统;
  • using System.ComponentModel.Composition; 使用System.ComponentModel.Composition;
  • using System.Windows.Forms; 使用System.Windows.Forms;
  • using ContractInterfaces; 使用ContractInterfaces;
  • (namespace) PlugInB (命名空间)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";
    }
}

Next add a Class Project called "PlugInC", then include this code: 接下来添加一个名为“PlugInC”的类项目,然后包含以下代码:

  • using System; 使用系统;
  • using System.ComponentModel.Composition; 使用System.ComponentModel.Composition;
  • using System.Windows.Forms; 使用System.Windows.Forms;
  • using ContractInterfaces; 使用ContractInterfaces;
  • (namespace) PlugInC (命名空间)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";
    }
}

The Solution should look like this: 解决方案应如下所示:

解


Right-click on the Solution, and select "Project Dependencies...". 右键单击Solution,然后选择“Project Dependencies ...”。 Set the dependencies as follows: 设置依赖项如下:

  • MainProgram - depends on - ContractInterface MainProgram - 取决于 - ContractInterface
  • PlugInA - depends on - ContractInterface PlugInA - 取决于 - ContractInterface
  • PlugInB - depends on - ContractInterface PlugInB - 取决于 - ContractInterface
  • PlugInC - depends on - ContractInterface PlugInC - 取决于 - ContractInterface
  • ContractInterface - depends on - [nothing] ContractInterface - 取决于 - [没有]

Right-click on the Solution, and select "Project Build Order...". 右键单击解决方案,然后选择“项目构建顺序...”。 The build order should be as follows: 构建顺序应如下所示:

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

Build and run the program. 构建并运行该程序。 You should see the 3 DLL files copied to a new "Plug-Ins" folder at the same directory level as the Solution file (*.sln). 您应该看到3个DLL文件被复制到与解决方案文件(* .sln)相同的目录级别的新“Plug-Ins”文件夹中。 If not, check the Project Build Order, Dependencies, and that you entered the Post-Build Events according to the notes in the Plug-In code above. 如果没有,请检查项目构建顺序,依赖项,以及您是否根据上面插件代码中的注释输入了构建后事件。 If the files are there, then the ListView should be populated in the Form with the Plug-In entries. 如果文件在那里,则应在带有插件条目的表单中填充ListView。 Double-click each ListView entry to start the Plug-In. 双击每个ListView条目以启动插件。

Have fun, I hope this helps someone.... 玩得开心,我希望这有助于某人....

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 MEF +插件没有更新 - MEF + Plug-In not updating 如何使用jQuery Datable插件突出显示DataTable的搜索文本 - How to highlight search text of DataTable using jQuery Datable plug-in 如何在不将整个DLL复制到内存的情况下读取DLL插件中的MEF元数据? - How to read MEF metadata in a DLL plugin without copying the entire DLL into memory? 如何通过在MEF中使用元数据来创建合成作用域? - How can I create composition scoping by using metadata in MEF? 如何构建C#插件框架? - How to construct a C# plug-in framework? 如何将adblock插件添加到WebBrowser控件? - How to add an adblock plug-in to the WebBrowser control? 如何使用CRM Dynamics 2013/2015中的插件用相关实体更新CRM中的实体 - How to update an Entity in CRM with a related Entity using a plug-in in CRM Dynamics 2013/2015 如何使用C#为Windows Mobile Web浏览器(IE)创建插件? - How to create a plug-in for Windows Mobile web browser (IE) using C#? 如何使用C#插件将逗号分隔的多文本值添加到cm 2015中的选项集和查找字段 - How to add multitext values seperated by commas to optionset and lookup field in cm 2015 using c# plug-in 如何使用 MEF 继承的导出和元数据? - How to use MEF Inherited Export & MetaData?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM