A mellékelt példa három projektet tartalmaz. A BMClient projekt tartalmazza a Windows-os alkalmazás kódját, mely futási időben felhasználja a Modules mappában megtalálható (oda fordított) BMBuildinLibrary dinamikus könyvtárat. A BMLibrary projekt tartalmazza azokat a segéd-osztályokat és -metódusokat, melyek lehetővé teszik, hogy az új csomópont megjelenjen.
Az alkalmazás Form-ja két szegmensre bomlik. A bal oldali szegmens egy TreeView kontrol, melyben a csomópont megjelenik. A jobb oldali szegmens egy Panel kontrol, melynek helyére a csomóponthoz tartozó felület jelenik meg abban az esetben, ha a betöltődött csomópont két alpontja közül valamelyikre kattintunk. Amennyiben a Modules mappa üres, akkor a fastruktúra nem tartalmaz elemeket.
Vizsgáljuk meg az egyes projekteket.
BMLibrary projekt
A projektben létrehozunk két interfészt, melyekben deklaráljuk a beépülő modulban definiálandó metódusokat és eseményeket. Az első interfész deklarációja a következő:
public interface IModule
{
IModuleData[] GetData();
BMControl GetForm(IModuleData d);
}
A másik interfészben létrehozzuk az eseményt.
public interface IModuleData
{
event EventHandler DataChanged;
}
Megvalósítunk egy saját attribútumot is, hogy a beépülő csomópont osztályát minősíthessük. Ez az attribútum határozza meg, hogy mi lesz a csomópont felirata.
[AttributeUsage(AttributeTargets.Class)]
public class ModuleNameAttribute : System.Attribute
{
private string displayName;
public ModuleNameAttribute(string name) : base()
{
displayName = name;
}
Az attribútumban felül kell írnunk az ősosztály ToString metódusát, hogy a minősítéskor ne az attribútum osztályának neve, hanem az általunk megadott név (jelen esetben „Csomópont” karakterlánc) jelenjen meg.
public override string ToString()
{
return displayName;
}
}
Végül létrehozunk egy absztrakt kontrol-osztályt, mely majd a csomópontra történő kattintáskor betöltődő szerkesztőablak őse lesz.
public abstract class BMControl : UserControl
{
protected IModuleData data;
public BMControl(IModuleData d)
{
data = d;
}
}
BMBuildinLibrary projekt
A BMBuildinClasses.cs állományban létrehozzuk a fent említett interfészek metódusait és eseményét implementáló osztályokat. A BMBuildinData osztály az IModuleData interfész DataChanged eseményét implementálja. Erre akkor lesz szükség, amikor az adott csomópontra kattintva betöltődik a Form, és annak szövegmezőjében megváltoztatjuk a csomópont feliratát. Az osztály NodeName property-je határozza meg, hogy mi lesz a csomópont felirata induláskor.
A BMBuildinModule osztály az IModule interfészt implementálja, vagyis a két metódusának definícióit tartalmazza. A GetData metódusban létrehozzuk az új csomópontokat, és egy tömbben adjuk vissza ezek objektumait.
public IModuleData[] GetData()
{
IModuleData[] data = new BMBuildinData[]{new BMBuildinData("Első csomópont"),new BMBuildinData("Második csomópont")};
return data;
}
A GetForm metódusban létrehozzuk a modul szerkesztőablakát reprezentáló kontrolt, mely a BMLibrary absztrakt osztályából származik.
public BMControl GetForm(IModuleData d)
{
return new BMBuildinControl((BMBuildinData)d);
}
A beépülő modul kontroljában egy Label és egy TextBox kontrolt találunk. A szövegmező kontrol tartalmazza a csomópont nevét akkor, amikor a csomópontra kattintva megjelenik a kontrol felülete a Windows-os alkalmazás jobboldali szegmensében. Ha a szövegmezőben változik a szöveg, akkor az eredmény rögtön mutatkozik a csomópont feliratában.
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (sender == textBox1)
{
((BMBuildinData)data).NodeName = textBox1.Text;
}
}
BMClient projekt
A kliensalkalmazásban létrehozunk két, a TreeNode osztályból származó osztályt, hogy megteremtsük az infrastruktúrát a leendő csomópontok számára. Ezek neve DataNode és ModuleNode.
A program indulásakor hívjuk meg a LoadModule metódust, melyben megvizsgáljuk, hogy a Modules mappában található-e beépíthető DLL állomány. Amennyiben igen, akkor mindegyikből generálunk egy Assembly osztályt, majd lekérdezzük a típusokat.
if (Directory.Exists("Modules"))
{
string[] files = Directory.GetFiles("Modules", "*.dll");
foreach(string f in files)
{
Assembly a = Assembly.LoadFrom(f);
System.Type[] types = a.GetTypes();
Amennyiben a típusok között megtalálható az IModule interfész, vagyis a modul implementálja az interfész metódusait, akkor az adott típust a megfelelő csomópont-típusra konvertáljuk, és a csomópontot a fára fűzzük.
if(type.GetInterface("IModule")!=null)
{
treeView1.Nodes.Add(new ModuleNode(type));
}
Amikor a Csomópont feliratú elem megjelenik, akkor a két alpontja közül bármelyikre kattinthatunk, betöltődik a BMBuildinControl kontrol, és a szövegmezőben az aktuális felirat jelenik meg. Ez szerkeszthető is. Ekkor úgy járunk el, hogy a létrehozott kontrol-példányt a Panel kontrol gyermekkontroljai közé felvesszük, és a kontrol GetForm metódusával lekérdezett dialógusablakot megjelenítjük.
panel1.Controls.Add(((ModuleNode)node.Parent).Instance.GetForm(((DataNode)node).Data));
Tehetünk egy próbát is a dinamikus beépítés továbbfokozására. Másoljunk egy BMBuildinLibrary.dll állományt a Modules mappába úgy, hogy a nevét megváltoztatjuk. Ekkor már kettő darab DLL áll rendelkezésre a betöltődéshez. A program indulása után két csomópont jelenik meg a fastruktúrában.