C# Mef TabControl Plugin Problem

Xcantion

Newbie
Registriert
Feb. 2012
Beiträge
1
Hallo,
ich, als kleiner blutiger Programmier Anfänger, versuche seid geraumer Zeit ein kleines Programm [c#] auf Basis von einem mit MEF basierenden Plugin System zu schreiben.

Das eigentliche Plugin System wollte ich gerne in c# WPF realisieren. Ich habe bereits eine Grundstruktur eingebaut die auch funktioniert. Jedoch hänge ich jetzt an einem Problem wo ich trotz langem suchens nicht weiterkomme.

Ich beschreibe am besten erst einmal mein Vorhaben um es etwas zu konkretisieren und Missverständnisse vorzubeugen.

Also Mein Programm ist soll ein recht Simples Plugin System sein.
Es gibt das Main "Host" Project, 1-Project mit dem Interface/Metadata, 1-Project pro Plugin.

1Project
Host Project

Project
|-Interface
|-Metadata

Project
1.Plugin

Project
2.Plugin
.
.
.

Jedes Plugin soll in erster Linie in das Main Project eingebunden werden. (Gradlinig gesehen funktioniert das auch) Jedoch soll eine Unterscheidung der Plugins stattfinden da nicht unbedingt jedes Plugin im Main Fenster angezeigt werden soll, für diese Unterscheidung habe ich mir gedacht mache ich mir die Metadaten zu nutze. Sprich: Dem Plugin sollen Metdaten mitgegeben werden wie z.B. MyName, ProgrammTeil (Main, Optionen, Sonstiges), Kategorie usw.

Im Host teil sollen diese Plugins ausgelesen werden und zugeordnet werden.

Soweit meine erste Idee.

Nun zum eigentlichen Problem:
Plugins auslesen -> Funktioniert
Plugins -> Metdaten abfrage (in der reinen Natur funktioniert)
Plugin -> In Tabs anzeigen (in der reinen Natur funktioniert)

Also alle Plugins sollen ausgelesen werden (Funktioniert) von diesen Plugins sollen Metadaten abgefragt werden werden was an und für sich auch funktioniert. Das heißt es wird zum Bleistift abgefragt welche Sprache das Plugin hat (testhalber hat 1Plug German / 1Plug English). Diese Plugins sollen dann einen Tab aufbauen (Dieser Tab soll von den Plugins erstellt werden (also die TabPages). Jedoch habe ich das Problem das ich es nicht hinbekomme das passend zu kombinieren.

Entweder. Wird die Metadaten Abfrage zwar durchgeführt aber ignoriert so das dennoch beide Plugins in die tabs geladen wird.
Oder. Die Tabs werden aufgebaut jedoch ohne die Metadaten mitzuliefern so das ich nur die TabPage (Überschrift sehe) aber nicht meine Form. Sprich das Object wird nicht angesprochen.

Ausserdem habe ich ein Problem mit dem WPF was aber zusammenhängt mit der Bindung an dem TabPage. auf den Bildern die ich noch mitsende sollte mehr oder weniger herauszusehen sein was das Problem nun genau ist.

So für den Anfang gebe ich natürlich noch ein bissl Code mit.
Interface
Code:
using System.Windows.Controls;

namespace Contracts
{
    public interface IExtension
    {
        UserControl GetView();
    }
}

InterfaceMetadata - Beispiel: Anhand des Namens
Code:
using System;
using System.ComponentModel;
using System.ComponentModel.Composition;

namespace Contracts
{
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class)]
    public class ExtensionExportAttribute : ExportAttribute, IModuleMetadata
    {
        public string DisplayName { get; private set; }

        public ExtensionExportAttribute(Type contractType, string displayName)
            : base(contractType)
        {
            this.DisplayName = displayName;
        }
    }

    public interface IModuleMetadata
    {
        [DefaultValue("Extension")]
        string DisplayName { get; }
    }
}

CodeBehind eines Plugins:
Code:
using System.Windows.Controls;
using System.ComponentModel.Composition;
using Contracts;

namespace Extension1
{
    //[Export(typeof(IExtension))]
    [ExtensionExport(typeof(IExtension), "App2")]
    public class Extension1 : IExtension
    {
        private UserControl1 _view;

        public UserControl GetView()
        {
            if (_view == null)
                _view = new UserControl1();
            return _view;
        }



    }
}
Main der eigentliche Import
Code:
        [ImportMany(AllowRecomposition = true)]
        public IEnumerable<Lazy<IExtension, IModuleMetadata>> Extensions { get; set; }
Ich habe mal ein bisschen getestet mit den verschiedenen Katalog aufbauten... will an dieser Stell einfach mal nur 2 Beispiele nennen
1-Beispiel
Code:
            var Catalog = new AggregateCatalog(new ComposablePartCatalog[]
                {
                    new AssemblyCatalog(Assembly.GetExecutingAssembly()),
                    new DirectoryCatalog(".")
                });
            var _container = new CompositionContainer(Catalog);
            var batch = new CompositionBatch();
            batch.AddPart(this);
            _container.Compose(batch);
2-Beispiel
Code:
string strPath = System.Windows.Forms.Application.StartupPath;
using (var Catalog = new AggregateCatalog())
            {

                DirectoryCatalog directorywatcher = new DirectoryCatalog(strPath, ".");
                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);


            }
Jetzt ein Beispiel wie ich mir das entsprechende Item in zB. panel anzeigen lassen kann
foreach (var ext in this.Extensions)
{
if (ext.Metadata.DisplayName == "App1")
{
var ctl = ext.Value.GetView();
this.viewContainer.Children.Add(ctl);
}

Hier wird auch gleich eine Abfrage gemacht wie ich das entsprechende Item zuordnen kann mit zB if Abfrage.

Jetzt das ganze nocheinmal jedoch mit einem Databinding
Code:
foreach (var ext in this.Extensions)
            {
             if (ext.Metadata.DisplayName == "App1")
            {
             var ctl = ext.Value.GetView();
            DataContext = from p in Extensions
                        select new { Title = p.Metadata.DisplayName, Page = ctl };
}
Hier wird die Abfrage auch ausgeführt jedoch werden dennoch beide bzw. Alle Plugins eingeladen und im TabControl angezeigt (wenn auch fehlerhaft)

Im Anhang findet ihr zum einen 2 Bilder so wie ich mir das vorstelle mit dem TabAufbau (jedoch in einer WinForm)
Dazu habe ich eine Demo dazugepackt wo der Rohkunstrukt vorhanden ist eigentlichen Programm sprich. Metadaten Ex/Import. Abfrage der Metadaten und ausgabe via Button in einem Panel (container) und als WPF.

Bei fragen etc.. fragt einfach
 

Anhänge

  • ausgangsform.png
    ausgangsform.png
    9,9 KB · Aufrufe: 188
  • ausgangsform_activ.png
    ausgangsform_activ.png
    21,9 KB · Aufrufe: 176
  • MefSimpleDemo.rar
    MefSimpleDemo.rar
    108 KB · Aufrufe: 174
Hab mir deinen Code nicht ganz genau angeschaut, aber versuch mal bitte anstatt folgendem (ist eh falsch)

Code:
foreach (var ext in this.Extensions)
{
    if (ext.Metadata.DisplayName == "App1")
    {
        var ctl = ext.Value.GetView();
        DataContext = from p in Extensions
                                select new { Title = p.Metadata.DisplayName, Page = ctl };
    }
}

das hier zu benutzen:

Code:
DataContext = Extensions.Where( ext => ext.MetaData.DisplayName == "App1" ).Select(
                        ext => new
                         {
                             Title = ext.Metadata.DisplayName, 
                             Page = ext.Value.GetView()
                         } );

Ich sagte "ist eh falsch", weil du zwar mit dem if nach "App1" filterst, dann aber doch nen anonymen Typen baust, in dem wieder alle drin sind. Für mich ergibt das keinen Sinn, zumal du den View an alle Tabs heftest.

Btw. MEF für ein "simples" Plugin System ist wie mit Kanonen auf Spatzen, äh, Mücken zu schießen :)

Edit.
Noch etwas.
Code:
string strPath = System.Windows.Forms.Application.StartupPath;
Tu dir selbst den Gefallen und vermeide jegliche Referenz zu Windows.Forms, wenn du mit WPF arbeitest.

Obigen Pfad kriegst du entweder über Environment.Xyz oder Assembly.Location..

Edit2.
Kleinen Fehler im Query korriegiert ;)
 
Zuletzt bearbeitet:
Zurück
Oben