[[http://www.drdobbs.com/cpp/implementing-a-plug-in-architecture-in-c/184403942|Implementing a Plug-In Architecture in C#]] {{ :helloworld:design_pattern:microkernel:csharp:implementing_a_plug-in_architecture_in_c_dr_dobb_s_2020-04-27_9_34_09_am_.html |Archive du 01/01/2003 le 27/04/2020}}
[[http://www.drdobbs.com/cpp/implementing-a-plug-in-architecture-in-c/184403942?pgno=2|Implementing a Plug-In Architecture in C# 2]] {{ :helloworld:design_pattern:microkernel:csharp:implementing_a_plug-in_architecture_in_c_dr_dobb_s_2020-04-27_9_34_18_am_.html |Archive du 01/01/2003 le 27/04/2020}}
[[http://www.drdobbs.com/cpp/implementing-a-plug-in-architecture-in-c/184403942?pgno=3|Implementing a Plug-In Architecture in C# 3]] {{ :helloworld:design_pattern:microkernel:csharp:implementing_a_plug-in_architecture_in_c_dr_dobb_s_2020-04-27_9_34_27_am_.html |Archive du 01/01/2003 le 27/04/2020}}
{{ :helloworld:design_pattern:microkernel:csharp:source_microkernel.zip |Code source}}
=====Base commune=====
En cas d'exception :
using System;
namespace HostCommon
{
public class PlugNotValidException : System.Exception
{
public PlugNotValidException(System.Type type, string Message) : base("The plug-in " + type.Name + " is not valid\n" + Message)
{
return;
}
}
}
Pour avoir le nom du plugin
using System;
namespace HostCommon
{
[AttributeUsage(AttributeTargets.Class)]
public class PlugDisplayNameAttribute : System.Attribute
{
private string _displayName;
public PlugDisplayNameAttribute(string DisplayName) : base()
{
_displayName=DisplayName;
return;
}
public override string ToString()
{
return _displayName;
}
}
}
Pour avoir la description du plugin
using System;
namespace HostCommon
{
[AttributeUsage(AttributeTargets.Class)]
public class PlugDescriptionAttribute : System.Attribute
{
private string _description;
public PlugDescriptionAttribute(string Description) : base()
{
_description=Description;
return;
}
public override string ToString()
{
return _description;
}
}
}
L'interface qui contiendra les données de chaque plugin
using System;
namespace HostCommon
{
public interface IPlugData
{
event EventHandler DataChanged;
}
}
Affichage graphique
using System;
using System.Windows.Forms;
namespace HostCommon
{
public abstract class PlugDataEditControl : System.Windows.Forms.UserControl
{
protected IPlugData _data;
public PlugDataEditControl(IPlugData Data)
{
_data=Data;
}
}
}
L'interface de chaque plugin
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
namespace HostCommon
{
public interface IPlug
{
// Les données de chaque plugin
IPlugData[] GetData();
// Localisation du rendu graphique
PlugDataEditControl GetEditControl(IPlugData Data);
// Fonction Enregistrer du plugin
bool Save(string Path);
// Fonction Imprimer du plugin
bool Print(PrintDocument Document);
}
}
=====IHM=====
L'interface graphique est un TreeView.
Chaque plugin est affiché depuis un TreeNode racine (PlugTreeNode) et un TreeNode pour les différentes données contenues dans chaque plugin (DataTreeNode).
using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using System.Xml;
using HostCommon;
namespace Host
{
class DataTreeNode : System.Windows.Forms.TreeNode
{
private IPlugData _data;
public IPlugData Data{get{return _data;}}
public DataTreeNode(IPlugData Data) : base()
{
_data=Data;
this.Text = _data.ToString();
_data.DataChanged += new EventHandler(this._data_DataChanged);
return;
}
private void _data_DataChanged(object sender, EventArgs e)
{
this.Text = _data.ToString();
return;
}
}
}
using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using System.Xml;
using HostCommon;
namespace Host
{
class PlugTreeNode : System.Windows.Forms.TreeNode
{
private System.Type _type;
private IPlug _instance;
public PlugTreeNode(System.Type type) : base()
{
_type=type;
// On crée le plugin
_instance = (IPlug)Activator.CreateInstance(_type);
// On crée un Node pour chaque donnée
IPlugData[] data = _instance.GetData();
foreach(IPlugData d in data)
{
this.Nodes.Add(new DataTreeNode(d));
}
this.Text = this.DisplayName;
return;
}
public string DisplayName
{
get{return _type.GetCustomAttributes(typeof(PlugDisplayNameAttribute),false)[0].ToString();}
}
public string Description
{
get{return _type.GetCustomAttributes(typeof(PlugDescriptionAttribute),false)[0].ToString();}
}
public IPlug Instance
{
get
{
if (_instance==null)
_instance = (IPlug)Activator.CreateInstance(_type);
return _instance;
}
}
}
}
IHM
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using HostCommon;
namespace Host
{
class HostForm : System.Windows.Forms.Form
{
private MenuItem _file;
private MenuItem _save;
private MenuItem _print;
private MenuItem _sep;
private MenuItem _exit;
private TreeView _tree;
private Splitter _split;
private Panel _panel;
private StatusBar _status;
public HostForm () : base ()
{
this.Menu = new MainMenu();
_file = new MenuItem("File", new EventHandler(this._menuItem_Clicked));
_print = new MenuItem("Print", new EventHandler(this._menuItem_Clicked));
_save = new MenuItem("Save", new EventHandler(this._menuItem_Clicked));
_sep = new MenuItem("-");
_exit = new MenuItem("Exit",new EventHandler(this._menuItem_Clicked));
_file.MenuItems.AddRange(new MenuItem[]{_print, _save,_sep,_exit});
this.Menu.MenuItems.Add(_file);
_tree = new TreeView();
_tree.Dock = DockStyle.Left;
_tree.AfterSelect += new TreeViewEventHandler(this._tree_AfterSelect);
_split = new Splitter();
_split.Dock = DockStyle.Left;
_panel = new Panel();
_panel.Dock = DockStyle.Fill;
_panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
_status = new StatusBar();
_status.ShowPanels=true;
_status.Panels.Add("Description");
this.Controls.Add(_panel);
this.Controls.Add(_split);
this.Controls.Add(_tree);
this.Controls.Add(_status);
this.LoadPlugs();
this.Size = new Size(400,350);
this.Text = "Host Application";
_status.Panels[0].Width=_status.Width;
this.Show();
return;
}
private PlugTreeNode GetSelectedPlug()
{
TreeNode node = _tree.SelectedNode as PlugTreeNode;
if(node!=null)
return (PlugTreeNode)node;
else
{
node = _tree.SelectedNode.Parent as PlugTreeNode;
if(node!=null)
return (PlugTreeNode)node;
}
return null;
}
private void _menuItem_Clicked(object sender, EventArgs e)
{
if(sender==_print)
{
PrintDocument doc = new PrintDocument();
PrintDialog pd = new PrintDialog();
pd.Document = doc;
if (pd.ShowDialog()==DialogResult.OK)
{
PlugTreeNode node = GetSelectedPlug();
node.Instance.Print(doc);
}
}
else if(sender==_save)
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog()==DialogResult.OK)
{
PlugTreeNode node = GetSelectedPlug();
node.Instance.Save(sfd.FileName);
}
}
else if(sender==_exit)
this.Close();
}
private void _tree_AfterSelect(object sender, TreeViewEventArgs e)
{
foreach(Control c in _panel.Controls)
c.Dispose();
_panel.Controls.Clear();
TreeNode node=null;
node = e.Node as PlugTreeNode;
if(node!=null)
_status.Panels[0].Text = ((PlugTreeNode)node).Description;
else
{
node = e.Node as DataTreeNode;
if(node!=null)
{
_status.Panels[0].Text = ((PlugTreeNode)node.Parent).Description;
_panel.Controls.Add( ((PlugTreeNode)node.Parent).Instance.GetEditControl(((DataTreeNode)node).Data));
}
}
return;
}
private void LoadPlugs()
{
string[] files = Directory.GetFiles("Plugs", "*.plug");
foreach(string f in files)
{
try
{
Assembly a = Assembly.LoadFrom(f);
System.Type[] types = a.GetTypes();
foreach(System.Type type in types)
{
if(type.GetInterface("IPlug")!=null)
{
if(type.GetCustomAttributes(typeof(PlugDisplayNameAttribute),false).Length!=1)
throw new PlugNotValidException(type, "PlugDisplayNameAttribute is not supported");
if(type.GetCustomAttributes(typeof(PlugDescriptionAttribute),false).Length!=1)
throw new PlugNotValidException(type, "PlugDescriptionAttribute is not supported");
_tree.Nodes.Add(new PlugTreeNode(type));
}
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
return;
}
protected override void Dispose(bool disposing)
{
GC.SuppressFinalize(this);
base.Dispose(disposing);
return;
}
[STAThread]
public static void Main(string[] args)
{
Application.Run(new HostForm());
return;
}
}
}
=====Un plugin=====
Les données clients avec le pattern observé.
using System;
using HostCommon;
internal class CustomerData : System.Object, IPlugData
{
public event EventHandler DataChanged;
internal CustomerData(string CompanyName)
{
_companyName = CompanyName;
return;
}
public string _companyName;
public string CompanyName
{
get
{
return _companyName;
}
set
{
_companyName=value;
if(DataChanged!=null)
DataChanged(this, new EventArgs());
return;
}
}
public override string ToString()
{
return _companyName;
}
}
Mise à jour de l'IHM.
using System;
using System.Drawing;
using System.Windows.Forms;
using HostCommon;
public class CustomerDataControl : PlugDataEditControl
{
private Label _lblCompanyName;
private TextBox _txtCompanyName;
internal CustomerDataControl(CustomerData Data) : base(Data)
{
_lblCompanyName = new Label();
_lblCompanyName.Text = "Company Name";
_lblCompanyName.Size = new Size(100,15);
_lblCompanyName.Location = new Point(10,20);
_txtCompanyName = new TextBox();
_txtCompanyName.Size = new Size(150,25);
_txtCompanyName.Location = new Point(10,40);
_txtCompanyName.Text = ((CustomerData)_data).CompanyName;
_txtCompanyName.TextChanged += new EventHandler(this._text_TextChanged);
this.Controls.Add(_lblCompanyName);
this.Controls.Add(_txtCompanyName);
return;
}
private void _text_TextChanged(object sender, EventArgs e)
{
if(sender==_txtCompanyName)
((CustomerData)_data).CompanyName=_txtCompanyName.Text;
}
}
Le plugin.
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using HostCommon;
[PlugDisplayName("Customers")]
[PlugDescription("This plug is for managing customer relationships")]
public class CustomerPlug : System.Object, IPlug
{
public CustomerPlug() : base()
{
return;
}
public IPlugData[] GetData()
{
IPlugData[] data = new CustomerData[]{
new CustomerData("Laugh Factory")
,new CustomerData("Improv")
};
return data;
}
public PlugDataEditControl GetEditControl(IPlugData Data)
{
return new CustomerDataControl((CustomerData)Data);
}
public bool Save(string Path)
{
MessageBox.Show("todo: add SAVE implementation for CustomerPlug here");
return true;
}
public bool Print(PrintDocument Document)
{
MessageBox.Show("todo: add PRINT implementation for CustomerPlug here");
return true;
}
}