head.WriteLine()

Montag, Januar 21, 2008

Grafische Verwaltung von Add-Ins

Wie ich hier bereits beschrieben habe, bietet System.AddIn keine Unterstützung für Windows Forms-basierte Add-Ins. Gleiches gilt auch für die grafische Verwaltung in einer Anwendung. Einen Add-In-Manager-Dialog, wie man ihn aus Visual Studio oder Office kennt, ist zurzeit nicht verfügbar.

Daher habe ich einen generischen Dialog entwickelt, der die Aktivierung und Verwaltung von Add-Ins zur Laufzeit stark vereinfacht. Zusätzlich stelle ich zwei Dialoge bereit, in denen Aktivierungsoptionen eingestellt werden können, bzw. die Informationen über geladene Add-Ins bieten. Diese sind jedoch in erster Linie für den Entwickler und weniger für den Anwender gedacht, doch dazu später mehr.

Da ja bekanntlich ein Bild mehr als tausend Worte sagt, hier die Bestandteile in grafischer Form:

AddInManager

1)  AddInManagerForm beinhaltet den eigentlichen Verwaltungsdialog. Für die Darstellung der Add-In-Liste greift er auf das AddInManagerControl zurück. In ihr werden die verfügbaren Add-Ins gelistet.

2)  AddInManagerControl ist ein User Control für die Anzeige der verfügbaren Add-Ins, bzw. zu dessen Aktivierung.

3)  AddInActivationOptionsForm bietet Steuerungsoptionen zur Add-In-Aktivierung. Hier kann beispielsweise eingestellt werden, ob das Add-In in einer AppDomain oder einem separaten Prozess gehostet werden soll.

4)  AddInDetailsForm stellt Informationen über bereits geladene Add-Ins zu Verfügung. Er stellt beispielsweise Informationen über die zugehörige Add-In-Assembly bereit.

Alle Komponenten sind in der WindowsFormsAddInProxy.dll vereint, die ich bereits hier und hier beschrieben habe.

Verwendung

Durch die Verwendung des Add-In-Manager-Dialogs reduziert sich der Code in der Anwendung deutlich, da dieser nicht nur eine grafische Oberfläche bereitstellt, sondern ebenfalls die entsprechende Aktivierung und Deaktivierung übernimmt. Damit dies funktioniert, müssen jedoch einige Voraussetzungen erfüllt sein.

Zunächst müssen zwei Listen geführt werden, die alle verfügbaren, bzw. geladenen Add-Ins enthalten. Diese können beispielsweise als statische Member in der Program-Klasse hinterlegt werden.

static class Program
{
    public static Collection<AddInToken> AvailableAddIns;
    public static AddInDictionary LoadedAddIns;
    public static string AddInPath;
   
    [STAThread]
    static void Main()
    {
        LoadedAddIns = new AddInDictionary();
        AddInPath = Environment.CurrentDirectory;
       
        // Vorhandene Add-Ins ermitteln
        AvailableAddIns =
           AddInStore.FindAddIns(typeof(AddInHostView),
           Program.AddInPath);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }
}

Beim Programmstart werden zunächst die verfügbaren Add-Ins in der Main()-Methode ermittelt. Für die Liste der geladenen Add-Ins kommt eine Instanz der AddInDictionary-Klasse zum Einsatz. Diese leitet von Dictionary<AddInToken, IAddInManager> ab und  enthält für jedes geladene Add-In ein Token und die entsprechende Instanz. IAddInManger ist ein Marker-Interface, das keinerlei Logik enthält und ausschließlich für den generischen Umgang mit den entsprechenden Add-In-View-Instanzen verwendet wird. Dies ist nötig, da AddInManagerForm keinerlei Informationen über die konkreten View-Klassen hat. Diese müssen nun lediglich von IAddInManger ableiten und können daraufhin generisch verwaltet werden. Im Beispielprojekt sieht die wie folgt aus:

public abstract class AddInHostView : IAddInManager
{
    public abstract WindowProxyBase GetSurface();
}

Nach dieser Vorarbeit kann der Dialog aufgerufen werden:

AddInManagerForm frm = new AddInManagerForm();
frm.AddInPath = Program.AddInPath;
frm.AvailableAddIns = Program.AvailableAddIns;
frm.LoadedAddIns = Program.LoadedAddIns;
if (frm.ShowDialog(this) == DialogResult.OK)
{
    // ...
}

AddInMangerForm muss nun der Pfad des Add-In-Verzeichnisses, sowie die Liste der verfügbaren und geladenen Add-Ins übergeben werden, alles Weitere übernimmt der Dialog.

AddInManagerForm

Aktiviert nun der Benutzer ein Add-In, wird dieses automatisch geladen und ist im Anschluss über Program.LoadedAddIns verfügbar. In der Beispielanwendung werden die geladenen Add-Ins in eine Liste aufgenommen, über die sie gebunden werden können.

...
if (frm.ShowDialog(this) == DialogResult.OK)
{
    // Liste der geladenen Add-Ins füllen
    this.addInsListView.Items.Clear();
    foreach (AddInToken token in Program.LoadedAddIns.Keys)
    {
        ListViewItem item =
           this.addInsListView.Items.Add(token.Name);
        item.Tag = token;
    }
}

AddInList

Zum Binden der Oberfläche wird zunächst die entsprechende View-Instanz über Program.LoadedAddIns ermittelt und auf dieser die GetSurface()-Methode aufgerufen.

private void bindAddInButton_Click(object sender, EventArgs e)
{
    AddInToken token =
       this.addInsListView.SelectedItems[0].Tag as AddInToken;
    AddInHostView instance =
       Program.LoadedAddIns[token] as AddInHostView;
    if (instance != null)
    {
        // Oberfläche des selektierten Add-Ins binden
        WindowProxyBase window = instance.GetSurface();
        if (window != null)
        {
            this.windowsProxyPanel1.SetWindow(window);
            this.windowsProxyPanel1.Select();
        }
    }
}

Das Resultat sehen Sie hier:

AddInBinding 

Zusätzliche Optionen

Wie oben bereits erwähnt, kann die Aktivierung eines Add-Ins durch einen Optionsdialog unterstützt werden. Hier können Hosting-Modell (AppDomain oder Prozess) und der Sicherheitskontext eingestellt werden.

ActivationOptionsForm

Der Dialog ist jedoch in erster Linie für den Entwickler und Anwendungsbetreuer gedacht und sollte dem Endbenutzer nicht zu Verfügung stehen. Daher kann die Anzeige über die ShowActivationOptions-Eigenschaft der AddInManagerForm-Klasse gesteuert werden.

Wurde die Option deaktiviert, so können Sie über die Eigenschaften AddInActivationType und AddInSecurityLevel der Aktivierungstyp, bzw. der Sicherheitskontext festgelegt werden.

Die Sourcen, inkl. Beispielprojekt finden Sie hier. Für Feedback bin ich wie immer dankbar.

1 Comments:

Kommentar veröffentlichen

<< Home