5

C# .NET 4.0 WinForms

メタデータ用のカスタム ExportAttribute 属性の作成について説明するこのチュートリアルのMEF サンプル コードの実装を開始しました。リソース ファイルの画像をメタデータに含めようとするまでは、すべて順調に進んでいました。目標は、メイン プログラムでプラグインのメニューを構築するためのメタデータとして、各プラグイン 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();
}

プラグイン 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 の 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);
    }
}

この行はエラーを生成しています。

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

明らかに ResourceFile は定数とは見なされませんが、画像をメタデータとして使用するにはどうすればよいですか、それとも不可能ですか? (画像は「Embedded in .resx」として設定されていることに注意してください)

助けや提案をありがとう!

4

1 に答える 1

3

解決策を見つけました:

OK、私が知る限り、MEF メタデータで画像やアイコンを使用することはできません。ただし、EXE ファイルの場合と同様に、DLL ファイルにアイコンを割り当てることができます。アイコンは、.NET の Icon タイプに組み込まれた静的メソッドを使用して読み取ることができます。

Icon MyDLLIcon = Icon.ExtractAssociatedIcon(DLLFilePath);

リソース ファイルから文字列を MEF メタデータとして返せるのに、埋め込まれたアイコンや画像を返せない理由はまだよくわかりません。

ここ数日、プラグイン メニューにアイコンを提供する機能するサンプル プログラムをまとめようとしてきたので、誰かの役に立てばと思い、コードを投稿することにしました。


これは、次の機能を備えた完全に機能するサンプル プロジェクトです。

  • 5 つのプロジェクト ( MainProgram、ContractInterfaces、PlugInA、PlugInB、PlugInC ) を持つ単一のソリューションを意図しています。

  • ビルド後のイベントは、DLL を各プロジェクトから共通の「プラグイン」フォルダーに自動的にコピーします。

  • MainProgram (WinForm) プロジェクトは、利用可能な DLL プラグインのカタログを構築し、ListView に各プラグインのアイコンとメタデータ タイトルを設定します。

  • ListView アイテムをダブルクリックすると、プラグインがインスタンス化され (Lazy インスタンス化を利用)、開始されます。

  • 各プラグインは、起動時にメイン フォームへの参照を受け取り、新しい TextBox を作成し、それをメイン フォームにポストして、プラグインが実行され、GUI にアクセスできることを証明します。

  • 選択したプラグインのタイトル、説明、およびバージョンのメタデータ値がコンソール ウィンドウに出力されます。


各DLLに異なるアイコンを割り当てました(古いVisual Studio 6 Common Graphics Miscフォルダーから)

スクリーンショット

ListView を作成するために DLL プラグインからアイコンが抽出され、TextBox が作成され、DLL が起動すると ( ListView の各プラグイン項目をダブルクリックした後)、DLL によって GUI にポストされました。


次のコードを「MainProgram」と呼ばれる新しい C# WinForm プロジェクトに追加します (私は VS 2010 を使用しました)。

何らかの理由でコード サンプル パーサーは Using ステートメントを好まないため、箇条書きとして以下に示します。

  • システムを使用する;
  • System.Collections.Generic の使用;
  • System.ComponentModel.Composition を使用します。
  • System.ComponentModel.Composition.Hosting の使用;
  • System.Drawing を使用します。
  • System.IO の使用;
  • System.Windows.Forms を使用します。
  • ContractInterfaces を使用します。
  • (名前空間) 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」というクラス プロジェクトを追加し、次のコードを含めます。

  • システムを使用する;
  • System.ComponentModel.Composition を使用します。
  • 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」というクラス プロジェクトを追加し、次のコードを含めます。

  • システムを使用する;
  • System.ComponentModel.Composition を使用します。
  • 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」というクラス プロジェクトを追加し、次のコードを含めます。

  • システムを使用する;
  • System.ComponentModel.Composition を使用します。
  • System.Windows.Forms を使用します。
  • ContractInterfaces を使用します。
  • (名前空間) 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";
    }
}

ソリューションは次のようになります。

解決


ソリューションを右クリックし、[プロジェクトの依存関係...] を選択します。依存関係を次のように設定します。

  • MainProgram - 依存 - ContractInterface
  • PlugInA - 依存 - ContractInterface
  • PlugInB - 依存 - ContractInterface
  • PlugInC - 依存 - ContractInterface
  • ContractInterface - 依存 - [なし]

ソリューションを右クリックし、[Project Build Order...] を選択します。ビルド順序は次のとおりです。

  1. 契約インターフェース
  2. プラグインA
  3. プラグインB
  4. プラグイン
  5. メインプログラム

プログラムをビルドして実行します。3 つの DLL ファイルが、ソリューション ファイル (*.sln) と同じディレクトリ レベルにある新しい "Plug-Ins" フォルダーにコピーされます。そうでない場合は、プロジェクトのビルド順序、依存関係を確認し、上記のプラグイン コードのメモに従ってビルド後のイベントを入力したことを確認します。ファイルが存在する場合は、ListView にプラグイン エントリをフォームに入力する必要があります。各 ListView エントリをダブルクリックして、プラグインを開始します。

楽しんでください、これが誰かに役立つことを願っています....

于 2013-02-01T23:51:19.477 に答える