Introduction

This article will demonstrate how to create a basic plug-in application for WinForms using the PluginFramework. This is not intended to be a perfect solution, but it is a mighty good start in that direction, if I must say so myself! It should help you get started on the right track. I have been meaning to write this for quite some time, but never had the time; in fact, I still don't have much time, so you'll have to forgive me if the article is somewhat lacking in elaboration!

Interface

First of all, we need to define a common interface for loading plug-ins. All interfaces are defined in the PluginFramework.Interfaces project. This way, both the host and the plug-ins can reference a separate project (we wouldn't want the interfaces in the host application, as this would mean each plug-in built would need to reference the plug-in host!).

IPlugin

About as simple as it gets... this is the interface that will be used to make sure all the code can play nice.

Group: This allows you to group related plug-ins together (think of a MenuStrip and menu items).

SubGroup: Not too hard to figure out what this is, is it?

Configuration: This allows you to pass configuration details to and from your plug-in. For example, the host could supply the configuration on plug-in load, and when disposing the project, the latest configuration can be passed back to the host for saving to disk for later use.

Icon: URI to an icon file, which can be used on a TreeView or MenuStrip control, for example.

IUserControlPlugin

Attributes

This will be used when trying to load assembly files. Marking the assembly with the correct attributes means you are sure to not try to load a rogue DLL. It also helps you locate the control to load from the assembly.

Yes, it needs some work. If I had the time, I'd clean it up, but this is intended to give you a basic working framework for building a plug-in app. You are welcome to customize as you see fit. In any case, it works well enough.

Helper Controls

Just to be really helpful, here are some controls that will auto-load an IPlugin:

PluginMenuStrip

Simply add one of these to your form, call the AddPlugin method from your code-behind... and voila; you now have a new menu item that once clicked will activate your plug-in!

PluginTreeView

Pretty much the same code as with the PluginMenuStrip. This control will load a plug-in via the AddPlugin method. However, as there is no standard way to show a UserControl, you will have to write that code yourself from the plug-in host (your app). You can get the currently selected IPlugin from the current TreeNode's Tag property.

And there you have it! Yes, I may get less points for not writing more words, but you can't complain that it isn't an easy-to-use framework! Enjoy! And if you have any improvements, I would be more than happy to hear them.

History

v1.1 - 2010 09 14

Due to various requests, I have updated the code with a newer version that addresses some bugs; yes, those icons are fixed (I never did quite get why something so off scope of the article was so important to some, but hey...). There was also an issue when loading the host the first time around; it would sometimes crash when closing. This is now resolved.

As an added bonus, there is now an ISettingsPlugin interface so that the user can change settings as well as a SettingsForm that will load the settings plugins for you (You could always make your plugin tabbed,but I think that just gets messy). To create a Settings plugin, do the same as with a regular plugin;

Create your user control

Implement ISettingsPlugin

Add Content Attribute to Assembly using the SettingsContentAttribute instead of the MainContentAttribute

Away you go

When you're done. Test it with the Host.exe by clicking on the Plugin Settings menu item.

Comments and Discussions

Dude, I have no issue with your vote of 3 and indeed I appreciate constructive criticism. The issue was with regards to your comment about the icons. If I ever decide to write an article on icon opacity, I'll let you know...

No hard feelings, yeah? My comment was in jest. We all need a chuckle sometimes...

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

Fair enough, but I did say the code comments were an extra, in addition to having supplied further details. So, now the question is, are you asking me to do anything further or should I just sit tight and wait for you guys?

So, now the question is, are you asking me to do anything further or should I just sit tight and wait for you guys?

Personally, I think you've done fine. I appreciate the effort you took to make this contribution, and I like the elegant simplicity of it. Yes, this isn't original, but if you look at Microsoft's Application Block to do this, it's huge and ugly. If you look at Spring.NET or some dependency injection framework to something like this, it's also overkill. So it's nice to see a something done minimally, that doesn't force me to swallow a bunch of other stuff that I don't need. And your code examples are fine, a few minutes invested actually looking at what each method does makes it pretty obvious how everything works.

I've got two technical comments: Not every dll in the plugin directory is necessarily going to be a plugin, it might be a dependent dll that the plugin requires, so it would probably be helpful to load the assembly and scan it for classes that implement IPlugin, but in a manner in which you can unload the assembly. There's a "load for reflection only" call, but it's a bear to work with.

Secondly, it would by useful to have a method that the main app can call when the main form is closing, to inform the plugin to release any unmanaged resources. Basically, a "plugin dispose" method.

It's hard to justify spending hours on an article when one is a husband, a father and working 44 hours per week..., the CodeProject has helped me a lot over the years, so I wanted to give something back; even if it is not elegantly written (the article, that is). I trust the readers are more than capable of tweaking it. Regarding the disposable plugins, I would suggest simply adding a void Dispose(); to the interface... no need to even implement it, because it will already be taken care of (UserControl / Form Dispose methods).