Benutzer mit den meisten Antworten
MEF-Beispiel will nicht klappen

Frage
-
Hallo zusammen,
ich habe gerade versucht ein kleines Beispiel nachzuvollziehen und erste Gehversuche unter MEF zu machen. Es sollte dabei eine kleine Anwendung heraus kommen, die aus einem fest definierten "Plugin"-Ordner Plugins lädt und entsprechend in der Win-Forms GUI anzeigt. Die Basis für das Beispiel findet sich unter http://weblogs.asp.net/visualwebgui/archive/2010/03/24/creating-extendible-applications-with-mef.aspx
Ich wollte das ganze in 3 Teilprojekte wie folgt aufteilen:
- Projekt "GlobalInterfaces" für die gemeinsame Schnittstelle
- Projekt "MEF_Core" für die Kernanwendung, die die Plugins laden soll
- Projekt "MEF_Function" für das Plugin, das geladen werden soll
Die Interface-Datei sieht dabei wie folgt aus:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GlobalInterfaces { public interface IMEFApplication { void Init(); string MyName { get; } } }
Das Plugin und die Hauptanwendung importieren dieses Interface, in dem sie erst einmal das Teilprojekt "GlobalInterfaces" referenzieren und dann eben die Schnittstelle implementieren.
Das Plugin sieht dann wie folgt aus:
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; using GlobalInterfaces; namespace MEF_Example { //mark the object as a function and implement the interface as declared before. [Export(typeof(IMEFApplication))] public partial class MEFFunction : UserControl, IMEFApplication { public void Init() { InitializeComponent(); } public string MyName { get { return "Komponente 1"; } } private void cmdShowMessage_Click(object sender, EventArgs e) { MessageBox.Show("Simple Example Function!"); } private void MEFFunction_Load(object sender, EventArgs e) { } } }
Die Hauptanwendung sieht wie folgt aus:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Reflection; using System.IO; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using GlobalInterfaces; namespace MEF_Example { public partial class Form1 : Form { //this tag causes MEF to start searching for objects/functions tagged with export(typeof(IMEFApplication)) [ImportMany(typeof(IMEFApplication))] List<IMEFApplication> ImportedItems; public Form1() { InitializeComponent(); } private void Form_Load(object sender, EventArgs e) { //Load the lists with the importmany tags LoadMef(); //iterate through the list to get the functions and a a tab to the navigationtabs. foreach (IMEFApplication FunctionObject in ImportedItems) { IMEFApplication mAppExtension = (IMEFApplication)Activator.CreateInstance(FunctionObject.GetType()); mNavTab.Text = mAppExtension.MyName; //add the object as a reference to the tag.. Mind that no instantion is done. mNavTab.Tag = mAppExtension; navTabs.TabPages.Add(mNavTab); } //init the first tab navTabs_SelectedIndexChanged(navTabs, null); } //static public string AssemblyDirectory //{ // get // { // //where to look !! // string codeBase = Assembly.GetExecutingAssembly().CodeBase; // UriBuilder uri = new UriBuilder(codeBase); // //create a path withouth the file// extension in front // string path = Uri.UnescapeDataString(uri.Path); // return Path.GetDirectoryName(path); // } //} private void LoadMef() { //What directory to look for! String strPath = @"D:\Programmierung\C#\MEF_Example\MEF_Sample\MEFFunction\bin\Debug"; using (var Catalog = new AggregateCatalog()) { DirectoryCatalog directorywatcher = new DirectoryCatalog(strPath, "*.dll"); Catalog.Catalogs.Add(directorywatcher); CompositionBatch batch = new CompositionBatch(); batch.AddPart(this); CompositionContainer container = new CompositionContainer(Catalog); //get all the exports and load them into the appropriate list tagged with the importmany container.Compose(batch); } } private void navTabs_SelectedIndexChanged(object sender, EventArgs e) { //Clear the panel controls. splitContainer1.Panel2.Controls.Clear(); if (navTabs.SelectedTab == null) { //stop if we have no tabs!!! (no functions) return; } //get the functionobject from the tag as added by loading tabs function in frmMEFContainer_Load IMEFApplication mControl = (sender as TabControl).SelectedTab.Tag as IMEFApplication; //use the interface to get the name splitContainer1.Panel2.Text = mControl.MyName; //convert it to a control UserControl FunctionControl = mControl as UserControl; FunctionControl.Dock = DockStyle.Fill; //add to the panel splitContainer1.Panel2.Controls.Add(FunctionControl); //init the object to start the display mControl.Init(); } } }
Beim debuggen habe ich gesehen, dass unter "batch" PartsToAdd offentichlich auf 1 steht, also wurde das Plugin schon einmal identifiziert. Allerdings ist "ImportedItems" im weiteren Verlauf immer auf 0.
Hat jemand ne Ahnung, aus welchem Grund das Plugin dann doch nicht geladen wird?
Antworten
-
Hallo SP.,
OK, Du möchtest:
- "... erste Gehversuche unter MEF [zu] machen ..."
Dazu ist das Projekt nicht gut geeignet, da es auch noch zu Fremd-Software wie "VisualWebGui" benutzt, etc..
Dein Link zu dem Projekt verdeutlicht ja die Benutzung von ImportMany.
Dazu hatte ich ein kleines Projekt gemacht, was das einfacher verdeutlicht und sonst ziemlich ähnlich ist.
Eigentlicht war es als WinForm-Beispiel für den Webcast von Darius Parys gemacht.
[Windows Forms Beispielprojekt: MEF ImportMany/Import/Export]
http://dzaebel.net/Downloads/WinMefDemo.zip <--- DOWNLOAD
(analog dem Silverlight-MEF Webcast [Silverlight 4 (Teil 4 von 6) - Managed Extensibility Framework | MSDN Online]
Weitere Infos auch:
[C# Forenbeitrag]
http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/69f8cdce-b6af-40f8-b1b7-8f2c99160554
ciao Frank- Als Antwort markiert SPDeveloperXP Sonntag, 14. November 2010 17:42
-
Hallo SP.,
Du gehts hier einen empfehlenswerten Weg in vielerlei Belangen.
Du hast höchstwahrscheinlich nur vergessen, die DLL in das Debug-Verzeichnis zu spielen!
Ich habe Dir hier ein Beispielprojekt (für Dein Szenario) gemacht, was diese Kopie mal über eine Post-BuildAction ausführt:
[Download eines Windows Forms Beispielprojektes mit DirectoryCatalog]
http://Dzaebel.NET/Downloads/MEF_Example.zip [<--Download]
Ich denke, Du hast ansonsten alles richtig gesehen und gemacht.
ciao Frank- Als Antwort markiert SPDeveloperXP Sonntag, 14. November 2010 17:42
Alle Antworten
-
Hallo SP.,
OK, Du möchtest:
- "... erste Gehversuche unter MEF [zu] machen ..."
Dazu ist das Projekt nicht gut geeignet, da es auch noch zu Fremd-Software wie "VisualWebGui" benutzt, etc..
Dein Link zu dem Projekt verdeutlicht ja die Benutzung von ImportMany.
Dazu hatte ich ein kleines Projekt gemacht, was das einfacher verdeutlicht und sonst ziemlich ähnlich ist.
Eigentlicht war es als WinForm-Beispiel für den Webcast von Darius Parys gemacht.
[Windows Forms Beispielprojekt: MEF ImportMany/Import/Export]
http://dzaebel.net/Downloads/WinMefDemo.zip <--- DOWNLOAD
(analog dem Silverlight-MEF Webcast [Silverlight 4 (Teil 4 von 6) - Managed Extensibility Framework | MSDN Online]
Weitere Infos auch:
[C# Forenbeitrag]
http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/69f8cdce-b6af-40f8-b1b7-8f2c99160554
ciao Frank- Als Antwort markiert SPDeveloperXP Sonntag, 14. November 2010 17:42
-
Hallo Frank,
danke für dieses Beispiel!
Wo ich insgesamt hin will, sind flexible GUIs, also z.B., dass es in einem Extensions-Verzeichnis verschiedene Plugins gibt, die ihren Teil der GUI mitbringen. Dies sollten z.B. Icons/Buttons für eine Menüleiste oben sein, Einträge für ein Menü etc. Die Logik soll dann natürlich in den DLLs (Plugins) ausgeführt werden.
Wenn ich es richtig verstehe, müsste ich mir ausgehend von Deinem Beispiel dann über DirectoryCatalog alle DLLs aus einem Extension-Verzeichnis laden und eben auf das gemeinsame Interface abbilden:
Catalogs.Add(new AssemblyCatalog(typeof(PluginInterface).Assembly))
Das Interface würde ich dann dahingehend erweitern, dass z.B. auch eine "GetMenuItem()" bzw. eine "GetToolBarIcon()"-Methode bereit gestellt wird und diese dann den entsprechenden Teil der GUI liefern. Also das Plugin erzeugt sich intern eben seine GUI und stellt sie über geeignete Methoden über das Interface nach außen bereit.
Wäre das aus Deiner Sicht richtig so?
-
Hallo SP.,
Du gehts hier einen empfehlenswerten Weg in vielerlei Belangen.
Du hast höchstwahrscheinlich nur vergessen, die DLL in das Debug-Verzeichnis zu spielen!
Ich habe Dir hier ein Beispielprojekt (für Dein Szenario) gemacht, was diese Kopie mal über eine Post-BuildAction ausführt:
[Download eines Windows Forms Beispielprojektes mit DirectoryCatalog]
http://Dzaebel.NET/Downloads/MEF_Example.zip [<--Download]
Ich denke, Du hast ansonsten alles richtig gesehen und gemacht.
ciao Frank- Als Antwort markiert SPDeveloperXP Sonntag, 14. November 2010 17:42
-
Hallo Frank,
genau, daran muss es wohl gelegen haben. Mit Deiner Modifikation am Beispiel geht es wunderbar!
Eigentlich ist es egal, ob man Dein erstes Beispiel als Basis nimmt oder das modifizierte MEF_Example. Ich denke, dass beide als Basis gut geeignet sind und funktionieren wie gewünscht.
Von da aus kann man den Weg einschlagen, dass die Plugins nun selbst einen Teil der GUI "zurück liefern" und das Hauptprogramm die Methoden einfach nur abfragt, oder es werden vom Plugin lediglich simple Informationen, wie z.B. der Text als String, zurück geliefert das Hauptprogramm kümmert sich dann darum, dass eine TabPage, ein Menü-Eintrag oder was auch immer erzeugt wird.
Ich muss das weiter überlegen, aber aktuell gefällt es mir besser, wenn die Plugins selbst ihren Teil der GUI aufbauen und über Methoden zur Verfügung stellen und die Hauptanwendung hängt dann nur noch das ein, was da zurück kommt. Wie siehst Du das?
Spannend bleibt es für mich allemal, da ich zum ersten Mal auf diese Art und Weise eine GUI aufbaue ;-)
-
Hallo SP.,
ja perfekt.
Ich hatte jetzt das zweite Beispiel (Download) noch etwas modifiziert, und das Warning-Pragma angepasst.
> und die Hauptanwendung hängt dann nur noch das ein, was da zurück kommt. Wie siehst Du das?
genau so sollte es sein - und wird bspw. in PRISM Szenearien etc. so gemacht/empfohlen. WPF/Silverlight ist da ja eh vom Prinzip her etwas besser geeignet. Du benötigst dann noch globale Kommunikations Strategien zwischen den Komponenten. Aber das wirst Du alles finden. Schaue Dir auch die Grundlagen von MVVM an - als Empfehlung - auch wenns im WinForms Bereich kaum genutzt wird. Auf der PDC 2010 hat John Papa da gerade ein gutes "Kung Fu Silverlight: Top Tips and Architectural Patterns and Practices" - Video dazu vorgetragen - allerdings hier Silverlight, aber MVVM ist gut erklärt.> Spannend bleibt es für mich allemal, da ich zum ersten Mal auf diese
Art und Weise eine GUI aufbaue ;-)
später ist es für einen dann nur ganz natürlich es so zu machen.
Freut mich, schönes Restwochenende.
ciao Frank -
Hallo Frank,
vielen Dank! Also jetzt wo das Grundgerüst steht, lassen sich andere Dinge kinderleicht integrieren. Menü-Einträge, Menü-Icons als Embedded-Resource in der DLL, läuft alles wunderbar! Sieht so aus, als ob jetzt mehr und mehr auf diese Weise entsteht ;-)
Nochmals danke für die Unterstützung!