В этой статье описан пример, как создать приложение с поддержкой плагин-архитектуры.
Итак, создает новый проект (Приложение Windows Forms), назовем его «PluginDemo«. После создания проекта переименовываем форму в «FrmMain«, бросаем на форму «ListView«, обзываем его «lvPluginsList«, устанавливаем свойство «View» в «Details«, добавляем 3 колонки (столбца): «Имя плагина», «Описание», «Версия». Думаю, уже догадались, что в этом «ListView» будут выводиться подключенные плагины.
Теперь приступим к написаю интерфейса для будущих плагинов (должны же мы знать, как с ними «общаться»). В обозревателе решений клацаем правой кнопкой мыши по решению «PluginDemo«, выбираем «Добавить» => «Создать проект«.
В списке выбираем «Библиотека классов«, указываем имя — «PlugIn«. Переименовываем «Class1.cs» в «Interfaces.cs» . Открываем этот файл, убираем пустой класс, который создался автоматически и добавляем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public interface IPlugin { string PluginName { get; } // имя плагина string DisplayPluginName { get; } // имя плагина, которое отображается string PluginDescription { get; } // описание плагина string Author { get; } // имя автора int Version { get; } // версия IPluginHost Host { get; set; } // ссылка на главную форму void Show(); // отображает форму } public interface IPluginHost { bool Register( IPlugin plug ); } |
У всех плагинов должен быть реализован интерфейс, который мы создали выше.
При загрузке плагинов и добавлении их в список мы будет получать значения следующих полей плагина: «DisplayPluginName«, «PluginDescription» и «Version«.
Когда пользователь будет выбирать в списке плагинов нужный ему плагин, то программа будет вызывать метод «Show()» выбранного плагина, в нем плагин может показать свою форму и т.п.
Кстати, не забудьте добавить наследование интерфейса «IPluginHost» главной форме:
1 |
public partial class FrmMain : Form, IPluginHost |
Также нужно реализовать метод «Register( IPlugin plug )«:
1 2 3 4 |
public bool Register( IPlugin plug ) { return true; } |
Приступим к написанию плагинов. Снова добавьте новый проект («Библиотека классов«), назовите ее «Plugin1«. После создания проекта нажмите правой клавишей на «References«, выберите «Добавить ссылку…«, в открывшемся окне справа выберите «Решение» => «Проекты«, потом поставьте галочку напротив «PlugIn«.
Добавили мы ссылку на нашу библиотеку, где хранится интерфейс для наших плагинов. Нужно этот интерфейс реализовать в первом плагине (и остальных, естественно 🙂 ), но немного позже. Сейчас нужно добавить форму первому плагину. Снова, в обозревателе решений, нажимаем правой клавишей на «Plugin1«, выбираем «Добавить» => «Форма Windows«, называем ее «FrmP1Main«. Бросаем на форму «TextBox«, обзываем его «tbInfo«, свойство «Multiline» переключаем в «True«.
Далее открываем код нашей формы, добавляем:
1 |
using PlugIn; |
Нашему классу «frmP1Main» добавляем поле «plug» типа «IPlugin«:
1 |
IPlugin _plug; |
Конструктору нашей формы добавляем параметр «IPlugin plug» и в теле конструктора присваиваем параметр полю «_plug», т.е. выйдет такое:
1 2 3 4 5 |
public FrmP1Main( IPlugin plug ) { InitializeComponent(); this._plug = plug; } |
Теперь добавляем обработчик события «Load» нашей форме (нажимаем на форму, с окне «Свойства» нажимаем на значок молнии, два раза клацаем по пункту «Load«; либо двойной щелчок на форме):
1 2 3 4 5 6 7 8 |
private void FrmP1Main_Load( object sender, EventArgs e ) { this.tbInfo.AppendText( this._plug.Author + "\r\n" ); this.tbInfo.AppendText( this._plug.DisplayPluginName + "\r\n" ); this.tbInfo.AppendText( this._plug.PluginDescription + "\r\n" ); this.tbInfo.AppendText( this._plug.PluginName + "\r\n" ); this.tbInfo.AppendText( this._plug.Version + "\r\n" ); } |
Думаю, все понятно: во время загрузки формы в «tbInfo» будет добавлена информация о плагине.
Настало время реализовать интерфейс в плагине. Переименовываем файл первого плагина «Class1.cs» в «PlugIn«. Удаляем пустой класс и добавляем следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public class PlugIn : IPlugin { private string _PluginName = "Plugin1"; private string _DisplayPluginName = "Первый плагин"; private string _PluginDescription = "Описание первого плагина"; private string _Author = "Dev"; private int _Version = 100; IPluginHost _Host; public void Show() { frmP1Main frm = new frmP1Main( this ); frm.ShowDialog(); } public IPluginHost Host { get { return _Host; } set { _Host = value; _Host.Register( this ); } } public string PluginName { get { return _PluginName; } } public string DisplayPluginName { get { return _DisplayPluginName; } } public string PluginDescription { get { return _PluginDescription; } } public string Author { get { return _Author; } } public int Version { get { return _Version; } } } |
В коде выше реализованы методы и поля интерфейса «IPlugin«, также добавлены свои поля.
Надеюсь, еще не забыли о том, что когда пользователь будет выбирать нужный ему плагин в главном приложении, то наше приложение будет вызывать метод «Show()«, в котором создается экземпляр формы плагина и показывается пользователю.
Второй плагин попробуйте реализовать сами (в конце статьи есть исходник :)).
Возвратимся к главному приложению (PluginDemo). Плагины уже есть, а вот их загрузки нет 🙂 Сейчас исправим. Добавьте главному приложению ссылку на «PlugIn» (выше есть пример; также подключите с помощью using), далее добавьте поле «_plugins«:
1 |
List<IPlugin> _plugins; |
Создадим метод «LoadPlugins( string path )«, в качестве параметра будет передаваться путь к папке, где находятся наши плагины (у нас они будут находиться в папке с программой):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
private void LoadPlugins( string path ) { string[] pluginFiles = Directory.GetFiles( path, "*.dll" ); this._plugins = new List<IPlugin>(); foreach ( string pluginPath in pluginFiles ) { Type objType = null; try { // пытаемся загрузить библиотеку Assembly assembly = Assembly.LoadFrom( pluginPath ); if ( assembly != null ) { objType = assembly.GetType( Path.GetFileNameWithoutExtension( pluginPath ) + ".PlugIn" ); } } catch { continue; } try { if ( objType != null ) { this._plugins.Add( (IPlugin)Activator.CreateInstance( objType ) ); this._plugins[ this._plugins.Count - 1 ].Host = this; } } catch { continue; } } } |
Собственно, ищем все .dll в папке с программой (проверяя на наличие нашего «PlugIn«) и добавляем в список.
Теперь добавим метод, который будет добавлять подключенные плагины в «ListView«:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private void AddPluginsItems() { this.lvPlugins.Items.Clear(); foreach ( IPlugin plugin in this._plugins ) { if ( plugin == null ) { continue; } this.lvPlugins.Items.Add( plugin.DisplayPluginName ); this.lvPlugins.Items[ this.lvPlugins.Items.Count - 1 ].SubItems.Add( plugin.Version.ToString() ); this.lvPlugins.Items[ this.lvPlugins.Items.Count - 1 ].SubItems.Add( plugin.Author ); } } |
Пробегаемся по списку плагинов и добавляем в «lvPlugins» имя, версию и автора плагинов.
Добавляем методы «LoadPlugins()» и «AddPluginsItems()» в обработчик события «Load» формы:
1 2 3 4 5 |
private void FrmMain_Load( object sender, EventArgs e ) { this.LoadPlugins( Application.StartupPath ); this.AddPluginsItems(); } |
Добавляем еще один обработчик события «DoubleClick» для «lvPlugins«:
1 2 3 4 5 6 7 8 |
private void lvPlugins_DoubleClick( object sender, EventArgs e ) { if ( this.lvPlugins.SelectedItems.Count > 0 ) { int selectedIndex = this.lvPlugins.SelectedItems[ 0 ].Index; this._plugins[ selectedIndex ].Show(); } } |
Здесь мы проверяем: есть ли выделенные элементы, если есть — получаем индекс первого выделенного и вызываем метод «Show()» выбранного плагина.
В общем, всё 🙂 Должно получится что-то типа такого:
Отличная статья. Огромное спасибо автору…. Везде искал эта самая понятная…
… какой крд
plugins[ plugins.Count ].Host = (IPluginHost)this;
Тут ошибка типа «out of range». Необходимо учитывать что массивы считаются от нуля.
Ага, и не только массивы 🙂
П.С. В исходниках на гитхабе норм, а здесь почему так — хз 😀
Подскажите плагины должны лежать только в корне программы?
Все зделал запустил работает, если плагины лежат в корне, если переношу в папку Корень\\Плагины\\ то они не отстраиваются.
Рекомендую попытаться разобраться в данном коде хотя бы чуточку, а не просто копировать/вставлять 😉
Я таки немного смотрел код, переписал под себя.
private void Update_version_Load(object sender, EventArgs e)
{
String adress_plugin = Application.StartupPath ;
//String adress_plugin = Application.StartupPath + «\\Main_plugins\\»;
loadPlugins(adress_plugin);
addPluginsItems();
}
В таком варианте работает если я пишу String adress_plugin = Application.StartupPath + «\\Main_plugins\\»;
То на куске кода
try
{
Assembly ass = null;
ass = Assembly.Load( args );
var s = ass.FullName;
if ( ass != null )
{
ObjType = ass.GetType( args + «.PlugIn» );
}
}
catch
{
return;
} Все время срабатывает исключение
Нужно юзать LoadFrom тогда, вот ей нужно передавать полный путь к либе. Позже внесу правки в пример.
ass = Assembly.LoadFrom(pluginFiles[i].ToString()); решил этот вопрос
string конвертить в string — это пять.
На одном компьютере работает все четко, а на другом компьютере плагины не отображаются в listview. В чем может быть проблема?
Хз. На втором компьютере эти плагины точно есть?
точно
Печалька однако
Приветствую, всё хорошо, спасибо за пример, но возможно подскажете как эти плагины запускать именно в главной форме FrmMain??? например в панель как то передавать сонтролы с формы!
Ссылки на плагины хранятся в
this._plugins
главной формы. В чем проблема передать их «куда нужно» и производить с ними необходимые действия?