This project has moved. For the latest updates, please go here.

Using MEF with recursion and TreeView

Jan 15, 2015 at 5:49 PM
I have an application where I'd like to display a list of all plugins located in a specified directory (and sub-directories). This is my first time using MEF and so I'm having some difficulty understanding how the framework works and in what order things need to be done. I think I know what the problem is (more later), but I'm not sure and I'm uncertain in how to fix it.
    private CompositionContainer container;
    private AggregateCatalog catalog = new AggregateCatalog();
    [ImportMany(typeof(IPlugin), AllowRecomposition = true)]
    IEnumerable<Lazy<IPlugin, IPluginData>> plugins = null;
    private void LoadPlugins(string rootPath)
        /* release old contents if the refreshTree 
         * method is called
        if (null != plugins)
            TreeNode rootNode = new TreeNode(rootPath);
            rootNode.Name = rootPath;
            populateTreeView(rootNode.Nodes, rootNode, true);
            // add the root node to the TreeView
            container = new CompositionContainer(catalog);
        catch (CompositionException ex)
    private bool appendToCatalog(string path, bool create, out DirectoryCatalog dirCatalog)
        dirCatalog = null;
        // check that the plugin directory exists
        if (false == System.IO.Directory.Exists(path))
            // do we want to create a directory for it?
            if(true == create) 
            return false;
        // finally, append the plugins to the catalog
        dirCatalog = new DirectoryCatalog(path, Constants.Strings.PLUGIN_EXTENSION);
        return true;
    private void populateTreeView(TreeNodeCollection nodes, TreeNode currentNode, bool isRoot)
        DirectoryCatalog dirCatalog;
        if(false == appendToCatalog(currentNode.Name, isRoot, out dirCatalog))
            // get subfolder list
            string[] directories = Directory.GetDirectories(currentNode.Name);
            //add a new node for each subfolder
            foreach (string directory in directories)
                nodes.Add(directory, Path.GetFileName(directory));
            // Problem is probably here
            foreach (var plugin in plugins)
                nodes.Add(plugin.Metadata.Name, plugin.Metadata.Name);
            foreach(TreeNode node in nodes)
                populateTreeView(node.Nodes, node, false);
Now, here's what I believe the problem is. If you notice in the populateTreeView() function I have the following comment: // Problem is probably here. My assumption here is that the plugins have not yet been loaded and therefore the foreach loop is never executed. But at the same time, I'd like to only load valid plugins (i.e. No dll's from another unrelated project), so I'd rather not display a list of all dll's in the folder.
Apr 20, 2015 at 3:32 AM
Not sure if you figured it out for yourself, but I posted an idea on my blog which I'll share here. It's in VB though.
'Create our Catalog of different Catalogs
        Dim catalog = New AggregateCatalog()

        'Add in any plugins that are in our own assembly
        catalog.Catalogs.Add(New AssemblyCatalog(GetType(Main).Assembly))
        'Check to see if our directory for our Plugins exists, if so add it as well
        If Directory.Exists("addons") Then catalog.Catalogs.Add(New DirectoryCatalog("addons", "*.dll"))
        'If we have any saved Locations outside of the main base locations (I use this in my project)
        If My.Settings.AddonLocations.Count > 0 Then
            For Each addloc In My.Settings.AddonLocations
                If System.IO.Directory.Exists(addloc.ToString) Then
                    'Add those as well
                    catalog.Catalogs.Add(New DirectoryCatalog(addloc, "*.dll"))
                End If
        End If

        'Create the CompositionContainer with the parts in the catalog
        _container = New CompositionContainer(catalog)

        'Fill the imports of this object

           'If that was successful, then we know everything loaded, let's grab the filenames
            For Each cat In catalog.Catalogs
                'Check to make sure that the Catalog is a Directory Catalog (There are multiple TYPES)
                If TypeOf cat Is DirectoryCatalog Then
                    'Let's create a DirectoryCatalog and merge it with the contents of the current Catalog
                    Dim catdir As DirectoryCatalog = cat
                    'If the file loaded correctly then continue
                    If catdir.LoadedFiles.Count > 0 Then
                        'Grab each file individually
                        For Each lf In ids.LoadedFiles
                            'MessageBox the result for us to what you will with it though
                        'Loop Back Through Each DLL
                    End If
                Else 'Wasnot a Directory Catalog. The only other Catalog we coded for was an Assembly
                    Dim asm As AssemblyCatalog = i
                    'Again lets message out that location as well.
                End If
          'Loop back through again  
        Catch ref As ReflectionTypeLoadException
            For Each ex In ref.LoaderExceptions
        End Try
So basically, grab your AggregateCatalog that you created ("catalog" object in my code),
then do a For Each loop on the Catalogs subobject (catalog.Catalogs)
foreach ( void cat in catalog.Catalogs){

Then check what kind of Catalog you have, as they have different properties
if (cat is DirectoryCatalog) {
        DirectoryCatalog catdir = cat;
        if (catdir.LoadedFiles.Count > 0) {
            foreach (void filename in ids.LoadedFiles) {
                Interaction.MsgBox(filename.ToString);  //DirectoryCatalog . LoadedFiles ( index# )  will return the Full Path / Filename of the Loaded .dll from directories. 
    } else {
        AssemblyCatalog asm = cat;
        Interaction.MsgBox(asm.Assembly.Location); // AssemblyCatalog . Assembly . Location will produce a string for the full Path/Filename of the loaded Assembly
Hope that helps, sorry if the C# is wrong in any way, I am more of a programmer than C#.