Per mostrare come MEF può essere di aiuto nel creare un'applicazione con archituttura così detta a "plug-in" ho pensato di realizzare un'applicazione Windows Forms in cui sono definite delle regioni in grado di contenere componenti grafici che rispondono a deterniate caratteristiche.
In sintesi la finestra è stata suddivisa in regioni (aree) dove a run-time verranno inseriti i componenti grafici, per creare le regioni ho utilizzato dei TableLayoutPanel oppotunemente suddivisi per righe e colonne secondo le esigenze della mia applicazione dove ogni elemento può contenere in modulo grafico:
nell'immagine soprastante sono evidenziare con un colore grigio scuro le varie regioni definite.
Quindi è necessario creare un controllo base da cui deriveranno tutti i componenti grafici inseribili nell'applicazione:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Composition;
namespace MefForWindowsForms
{
public partial class PanelBase : UserControl
{
public PanelBase()
{
InitializeComponent();
}
}
}
Ho creato un'interfaccia per esporre i metadati relativi ad ogni componente grafico:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MefForWindowsForms
{
public interface IPanelBaseData
{
String RegionName { get; }
}
}
Ogni pannello inseribile nelle regioni definite nell'applicazione sarà qualcosa di questo tipo:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Composition;
namespace MefForWindowsForms
{
[Export(typeof(PanelBase))]
[ExportMetadata("RegionName", "panel1")]
public partial class Panel1 : PanelBase
{
public Panel1()
{
InitializeComponent();
}
}
}
La classe è stata decorata con l'attributo "Export", attraverso il quale si specifica che un tipo, una proprietà, un campo o un metodo fornisce una determinata esportazione.
Quindi mediante l'attributo ExportMetadata è possibile specificare i metadati per un tipo, una proprietà, un campo, o un metodo contrassegnato con ExportAttribute.
In questo caso con ExportMetadata indichiamo il nome della regione che dovrà contenere in componente grafico.
Ciascun pannello importabile dall'applicazione deve essere decorato mediante gli attributi indicati sopra, in più per evidenziare e differenziare ogni singolo pannello ho impostato un colore di backgroud differente per ciascuno di essi.
A questo punto ho inserito qualche riga di codice per gestire la lista dei pannelli generata ed inserirli nella posizione opportuna;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MefForWindowsForms
{
public partial class MefForm : Form
{
private CompositionContainer _container;
#pragma warning disable 0649
[ImportMany]
private IEnumerable<Lazy<PanelBase, IPanelBaseData>> m_panels;
#pragma warning restore 0649
private Dictionary<String, System.Windows.Forms.Panel> m_regions;
public MefForm()
{
InitializeComponent();
m_regions = new Dictionary<String, Panel>();
m_regions[region1.Name] = region1;
m_regions[region2.Name] = region2;
m_regions[region3.Name] = region3;
m_regions[region4.Name] = region4;
m_regions[region5.Name] = region5;
m_regions[region6.Name] = region6;
m_regions[region7.Name] = region7;
//An aggregate catalog that combines multiple catalogs
var catalog = new AggregateCatalog();
//Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
//Create the CompositionContainer with the parts in the catalog
_container = new CompositionContainer(catalog);
//Fill the imports of this object
this._container.ComposeParts(this);
foreach (Lazy<PanelBase, IPanelBaseData> i in m_panels)
{
MoveToRegion(i.Value, i.Metadata.RegionName);
}
}
private void MoveToRegion(PanelBase panel, String regionName)
{
if (m_regions.ContainsKey(regionName))
{
m_regions[regionName].Controls.Add(panel);
panel.Dock = DockStyle.Fill;
}
}
}
}
Il risultato è qualcosa del tipo:
Scopo di questo semplice esempio è evidenziare come MEF può aiutarci nel costruire applicazioni modulari e facilmente estendibili, i concetti applicati per importare e posizionare i componenti grafici dell'applicazione possono essere utilizzati per inserire oggetti di vario tipo come comandi, menu, operazioni di navigazione, servizi, ecc..
MefForWindowsForms.zip