[[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; } }