Пишем приложение, поддерживающее плагины

20 дек. 2009 г. | | |

Идея проста – есть интерфейс ( IPlugin ), о котором знает главная программа, и который должны реализовывавать все плагины. Вот, в принципе, и вся идея :)

Плагины наше приложение будет искать и автоматически подгружать в меню Plugins из папки plugins. Естественно, плагины должны быть реализованы ввиде dll-модуля. Если в папке будет dll, содержащая классы, не реализующие интерфейс IPlugin, то лоадер просто игнорирует их.

Реализовать данную затею предлагаю на C# в виде простенького Windows Forms Application.

Итак, создаем новый проект (Windows Forms Application), назовем его, к примеру, PluginTest. На главную форму добавим меню (menuStrip1) и панель (contentHolder), на которую будем подгружать наши формы.


В меню добавляем пункты, как на картинке. На событие Click пункта меню Exit пишем код

Application.Exit();

По клику на SampleForm в панель contentHolder приложение должно будет загрузить форму. Давайте создадим класс, который будет ответственный за это. Для этого добавим в проект класс ContentManager. Перед использование его нужно инициализировать контролом, на который нужно подгружать различные формы. Код:

class ContentManager
{
protected static Control _contentHolder;

public ContentManager()
{

}

public static void Inizialize(Control ctrl)
{
_contentHolder = ctrl;
}

public static void LoadContent(Control content)
{
_contentHolder.Controls.Clear();
content.Dock = DockStyle.Fill;
_contentHolder.Controls.Add(content);
}
}

Теперь добавим в проект UserControl с именем SampleForm. Какой функционал будет выполнять данный контрол - полет вашей фантазии, я ограничусь лишь кнопочкой "Hello, world!". После этого на событие Click пункта меню SampleForm напишем:

ContentManager.LoadContent(new SampleForm());


Сейчас приступим к реализации плагинной системы. Помните, мы говорили об интерфейсе? Так вот, добавьте в солюшн проект типа Class Library и назовите его IPlugin. Вот код самого интерфейса:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace IPlugins
{
public interface IPlugin
{
Control GetContent();
String GetName();
}
}

Таким образом, плагин для нашего приложения должен реализовать две функции - GetContent() - должна вернуть контол т.е. форму плагина, а GetName() - название плагина для красивого отображения в меню.
Добавим ссылку на проект IPlugin в проект PluginTest.
А теперь создадим парочку плагинов! Для этого в солюшн добавьте еще один проект типа Class Library и назовите его SamplePlugin. В References этого проекта тоже надо добавить ссылку на проект IPlugin. Теперь подключим в using IPlugin и напишем простенький плагин :)

public class Plugin1 : IPlugin
{
private String _pluginName = "Plugin1";

public Plugin1()
{
}

public String GetName()
{
return _pluginName;
}

public Control GetContent()
{
SamplePluginUI content = new SamplePluginUI();
return content;
}

}
}

Класс Plugin1 реализует интерфейс IPlugin. В методе GetContent() он создает UserControl SamplePluginUI и возвращает его получателю. Добавьте в проект SamplePlugin UserControl с название SamplePluginUI.
Теперь осталось чуть-чуть. Наш ContentManager пока умеет подгружать на форму только Control'ы. Давайте научим его еще добывать формы из плагинов. Для этого в класс ControlManager добавьте следующий метод:

public static void LoadContent(Type t)
{
IPlugin plugin = (IPlugin)Activator.CreateInstance(t);
LoadContent(plugin.GetContent());
}

Теперь осталось совсем чуть-чуть. Во-первых, нужно добавить метод в главную форму солюшена PluginTest, который будет искать плагины и подгружать их. Назовем его CheckForPlugins().

private void CheckForPlugins()
{
String PluginsDir = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "plugins");

if (!Directory.Exists(PluginsDir))
{
Directory.CreateDirectory(PluginsDir);
}

String[] PluginAssemblies = Directory.GetFiles(PluginsDir, "*.dll");

ToolStripMenuItem pluginsMenu = (ToolStripMenuItem) menuStrip1.Items["plugins"];

if (pluginsMenu.DropDown == null)
{
pluginsMenu.DropDown = new ToolStripDropDown();
}

foreach (String file in PluginAssemblies)
{
Assembly AddInAssembly = Assembly.LoadFrom(file);
foreach (Type t in AddInAssembly.GetExportedTypes())
{
if (t.IsClass && typeof(IPlugin).IsAssignableFrom(t))
{
AddPlugin(t, pluginsMenu);
}
}
}
}

private void AddPlugin(Type t, ToolStripMenuItem where)
{
IPlugin plugin = (IPlugin)Activator.CreateInstance(t);
string pluginName = plugin.GetName();
ToolStripMenuItem mi = new ToolStripMenuItem(pluginName);
mi.Click += new EventHandler(mi_Click);
mi.Tag = t;
where.DropDownItems.Add(mi);
}

void mi_Click(object sender, EventArgs e)
{
Type t = (Type)((ToolStripMenuItem)sender).Tag;
ContentManager.LoadContent(t);
}

Что тут происходит? В самом начале метода мы находим путь к папке plugins и проверяем, существует ли она. Потом выбираем из нее все файлы с расширением dll в массив PluginAssemblies. Далее, мы проверяем каждый элемент этого массива на то, является ли он классом и реализует ли интерфейс IPlugin. Если результат положительный, то добавляем его в меню Plugins методом AddPlugin().
Осталось только подправить конструктор формы Form1:

public Form1()
{
InitializeComponent();
ContentManager.Inizialize(this.contentHolder);
CheckForPlugins();
}

Вот и все, приложение, поддерживающее плагины, готово!
Исходный код можно скачать отсюда.

0 коммент.:

Отправить комментарий