|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionWould you like to be able to supply to the users of your applications an extensible framework that they can use to expand the functionality of your base application? We see this all the time – in Visual Studio and Office products they are called add-ins; in Eclipse they are called plug-ins; in other apps I've heard them called extensions or snap-ins or modules. Whatever they are called, they do one thing; they allow you application functionality to be expanded with functions you may never have dreamed of. In this article, I'm going to show you how to create a simple framework for extensibility that you can use to expand the functionality of your application. Using the codeIt's actually very simple to allow other developers to extend your application. All you have to do is provide a base class to extend from and then when loading the extensions, make sure that a class extends from that class. This class could be either an Interface or a Class. I chose to use an Interface, but if you have common code that you would like to be able to execute from any extension, you should use a Class. If you don't use an interface, you will need to provide another method of checking the type (see below in the Extensions collection class, searchDir Method). Below is the base class for the simple extension: using System.Drawing; using System; using System.Collections.Generic; using System.Text; namespace Extensibility { public interface IExtender { String Name { get; } String Description { get; } String MenuText { get; } String DLLPath { get; } Image Image { get; } String Provider { get; } Object Execute(); } } You can expand this with whatever properties and methods you want. I also like to type-safe my collections so I've provided an Extensions collection class. This also allows me a class to use for other things, like populating the extensions and the MenuItem if they are to accessed form a menu, and providing an event to notify the application when an extension has been clicked. Below is my Collection class: using System.Windows.Forms; using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Reflection; namespace Extensibility { public class Extensions : System.Collections.CollectionBase { public delegate void extensionClickedEventHandler(object sender, EventArgs e); public event extensionClickedEventHandler ExtensionClicked; public int add(IExtender extension) { return this.List.Add(extension); } public void remove(int index) { this.List.Remove(index); } public IExtender item(int index) { return (IExtender)this.List[index]; } public IExtender item(String name) { IExtender ret = null; foreach (IExtender ext in this.List) { if(ext.Name.Equals(name)){ ret = ext; break; } } return ret; } public int populate(String directory, bool searchSubs, ToolStripMenuItem menuItem) { int ret = this.populate(directory, searchSubs); if (ret > 0) { foreach (IExtender ext in this.List) { ToolStripMenuItemEx item = new ToolStripMenuItemEx(); item.Click += new EventHandler(item_Click); if (ext.MenuText != null) item.Text = ext.MenuText; if (ext.Image != null) item.Image = ext.Image; if (ext.Description != null) item.ToolTipText = ext.Description; item.Extension = ext; menuItem.DropDownItems.Add(item); } } return ret; } void item_Click(object sender, EventArgs e) { ExtensionClicked.Invoke(sender, e); } public int populate(String directory) { return this.populate(directory, false); } public int populate(String directory, bool searchSubs) { if(!Directory.Exists(directory)){ throw new IOException("Directory not found"); } rectoryInfo dir = new DirectoryInfo(directory); is.searchDir(dir, searchSubs); turn this.Count; pivate void searchDir(DirectoryInfo dir, bool searchSubs) { fileInfo[] files = dir.GetFiles("*.dll"); if (files.Length == 0) { throw new IOException("No dll files found in " + dir.FullName); } foreach (FileInfo f in files) { String fileName = f.FullName; Assembly assy = Assembly.LoadFile(fileName); Type[] types = assy.GetTypes(); foreach (Type t in types) { if (t.GetInterface("IExtender") != null) { IExtender ext = (IExtender)assy.CreateInstance(t.FullName); this.add(ext); } if (searchSubs) { foreach(DirectoryInfo subDir in dir.GetDirectories()){ this.searchDir(subDir, searchSubs); } } } } } Some notes about the load code. Reflection is used to load the extensions from all libraries in the specified directory. The code goes through all dll files located in the specified directory and queries all types to see if they implement the IExtender imterface. If it does, we load it into our collection. I've chosen to use the directory method (loading all extenions) over specifically identifying extensions because of ease of use. I could as easily provided registry entries or a file to specify which extensions to load or ignore. Back to business - optionally, when we call the populate method, we can supply a ToolStripMenuItem and it will also populate that menuitem with all the extensions it finds. In this regard, I've also provided an extended ToolStripMenuItem class (ToolStripMenuItemEx) that will hold the extension so I can pass back an extension to the application when it is clicked. The populate method will also create the internal event handler, which will raise the external event handler in your application and pass back an extender. Below see the ToolStripMenuItemEx class: using System; using System.Collections.Generic; using System.Text; namespace Extensibility { public class ToolStripMenuItemEx : System.Windows.Forms.ToolStripMenuItem { private IExtender _Extension = null; public IExtender Extension { get { return _Extension; } set { _Extension = value; } } } }
Extending application functionality with an extension
Putting it all together
Some development notes
|
|||||||||||||||||||||||||||||||||||||||||||||||||||