Dans le précédent billet, nous avons vu comment créer un module en Silverlight qui dispense un objet Control, ainsi qu'un WebService qui permet de donner une liste de tous les modules disponibles côté serveur. Nous allons voir aujourd'hui comment, à partir de la liste des modules récupérée depuis le WebService, nous allons pouvoir les télécharger au chargement de la page, puis incorporer les Control que ces derniers dispensent dans le Canvas principal.
J'ai écrit une classe Modman qui s'occupe de la première partie. Il appelle le WebService pour récupérer les URI relatives, télécharge la DLL grâce au Downloader puis créé une instance de la classe qui implémente l'interface IModule avec Activator.CreateInstance. Toutes les instances des modules sont alors stockées dans une liste accessible en lecture.
public class Modman
{
private List<IModule> _modules = new List<IModule>();
private ModmanWS.Modman _ws = new ModmanWS.Modman();
private List<ModuleAssembly> _modAssemblyList = new List<ModuleAssembly>();
public event EventHandler OnAllModulesLoaded;
public event EventHandler OnModuleDownloadFailed;
public event EventHandler<ModuleErrorEventArgs> OnModuleLoadError;
public List<IModule> ModulesInstances
{
get { return _modules; }
}
public void DownloadAndLoadAllModules()
{
string[][] modList = _ws.GetModulesList();
foreach (var mod in modList)
{
ModuleAssembly assembly = new ModuleAssembly(mod[0], mod[1], new Uri(mod[2], UriKind.Relative));
_modAssemblyList.Add(assembly);
Downloader loader = new Downloader();
loader.Completed += new EventHandler(onDownloadComplete);
loader.DownloadFailed += new ErrorEventHandler(onDownloadFailed);
loader.Open("GET", assembly.Url);
loader.Send();
}
}
private void onDownloadComplete(object sender, EventArgs e)
{
ModuleAssembly assemblyInfo = retrieveAssemblyDownloaded(sender as Downloader);
if (assemblyInfo != null)
{
loadModule(assemblyInfo);
_modAssemblyList.Remove(assemblyInfo);
if (_modAssemblyList.Count == 0 && this.OnAllModulesLoaded != null)
this.OnAllModulesLoaded(this, new EventArgs());
}
}
private void onDownloadFailed(object sender, EventArgs e)
{
if (this.OnModuleDownloadFailed != null)
this.OnModuleDownloadFailed(this, new EventArgs());
}
private ModuleAssembly retrieveAssemblyDownloaded(Downloader loader)
{
ModuleAssembly assemblyInfo = null;
foreach (ModuleAssembly assembly in _modAssemblyList)
{
if (assembly.Url == loader.Uri)
{
assemblyInfo = assembly;
break;
}
}
return assemblyInfo;
}
private void loadModule(ModuleAssembly assemblyInfo)
{
try
{
Assembly assembly = Assembly.Load(assemblyInfo.AssemblyName);
foreach (Type type in assembly.GetTypes())
{
if (type.GetInterface(typeof(IModule).FullName, false) != null)
{
IModule instance = (IModule)Activator.CreateInstance(type);
_modules.Add(instance);
}
}
}
catch (Exception ex)
{
if (OnModuleLoadError != null)
OnModuleLoadError(this, new ModuleErrorEventArgs(ex));
}
}
}
Notez que j'utilise un objet ModuleAssembly, qui ne me sert en fait qu'à stocker les informations d'un module que retourne le WebService.
On y est presque ! Dans le code de ma page principale Silverlight, je m'abonne aux évènements de Modman, pour pouvoir afficher l'état du chargement des modules dans mon Canvas, et lorsque tous les modules ont été chargés, je récupère la liste des instances de modules, et j'ajoute pour chacun d'entre eux le MainControl au Canvas.
public partial class Page : Canvas
{
public void Page_Loaded(object o, EventArgs e)
{
InitializeComponent();
Modman mm = new Modman();
mm.OnAllModulesLoaded += new EventHandler(ModMan_OnAllModulesLoaded);
mm.OnModuleDownloadFailed += new EventHandler(ModMan_OnModuleDownloadFailed);
mm.OnModuleLoadError += new EventHandler<ModuleErrorEventArgs>(ModMan_OnModuleLoadError);
mm.DownloadAndLoadAllModules();
}
void ModMan_OnModuleLoadError(object sender, ModuleErrorEventArgs e)
{
this.statusText.Text += e.Exception.Message;
}
void ModMan_OnModuleDownloadFailed(object sender, EventArgs e)
{
this.statusText.Text += "Cannot download module";
}
void ModMan_OnAllModulesLoaded(object sender, EventArgs e)
{
try
{
Modman moduleManager = sender as Modman;
foreach (IModule module in moduleManager.ModulesInstances)
{
Control ctrl = module.MainControl;
ctrl.SetValue<double>(Canvas.LeftProperty, 50);
ctrl.SetValue<double>(Canvas.TopProperty, 50);
ctrl.Visibility = Visibility.Visible;
this.Children.Add(ctrl);
}
}
catch (Exception ex)
{
this.statusText.Text += ex.Message;
}
}
}
Et voilà, nous avons une application Silverlight complètement modulaire ! Les modules sont libres de personnaliser le contrôle qu'ils veulent mettre à disposition, tandis que l'application centrale garde la main sur la disposition de ces derniers dans la Canvas principal. De plus, il suffit de modifier le WebService en filtrant la liste des modules qu'il retourne pour apporter une logique métier aux modules activés (selon l'utilisateur par exemple).
Adrien
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :