Search code examples
wpfprism

Using Prism for building a Plugin (.dll)


I am developing a plugin to be used for a software I use. The software plugin is attached to the software by attaching the .dll produced from my code. Therefore, the software's documentation requires you to have a certain class (call it CPlugin) as the plugins entry point.

All the tutorials I am reading with Prism is where you initiate your project as a WPF Application. This way your project will have App.xaml and App.cs files where you start to implement your Prism framework. Compiling the code also (for a WPF application) will produce a .exe not a .dll.

The way I have my plugin setup is I start my project with a C# class. I would then create my CPlugin class and initiate all my variables and then display my MainView which creates my ViewModel and take it from there. There is no App.xaml or App.cs. I am not sure how to use Prism with the constraints I have.

This is the software I am developing the plugin for: https://www.csiamerica.com/products/etabs Upon installation in the install directory; the API helpfile can be found which explains how to develop or initiate a plugin. Here is a sample of the relevant information:

In your plugin, all functionality must be implemented in a class called cPlugin.

Class cPlugin must contain a subroutine cPlugin.Main that has two reference arguments of types ETABSv1.cSapModel and ETABSv1.cPluginCallback

Also

Adding a .NET Plugin The process for adding a .NET plugin is much simpler. In the External Plugin Data form, the user should simply browse for and select the plugin .NET DLL and click the "Add" button

Here is some sample code for a plugin that displays an empty window: Create a C# Class Library file (.NET Framework), reference the API as one of my references.

CPlugin.cs:

using Combonito.Views;          // Access Views (open window)

using CSiAPIv1;                  //to Access ETABS/SAP2000 API
using System;
using static Globals;

namespace Combonito
{
    // cPlugin has to be implemented in ETABS Plugins, it has to contain two functions Main() and Info()
    public class cPlugin
{

    private MainView _MyForm;

    //Entry point of plugin - has to exist with this exact signature
    // must call Finish() when plugin is closed!
    public void Main(ref cSapModel _SapModel, ref cPluginCallback _ISapPlugin)
    {
        ParentPluginObject = this;
        SapModel = _SapModel;
        ISapPlugin = _ISapPlugin;

        try
        {
            _MyForm = new MainView();   //Create MainView
            _MyForm.ShowDialog();       // Display window
        }
        catch (Exception ex)
        {
            try
            {
                ISapPlugin.Finish(1);
                Console.WriteLine(ex);
            }
            catch (Exception ex1)
            {
                Console.WriteLine(ex1);
                throw;
            }
        }
    }

    // return value should be 0 if successful
    public int Info(ref string txt)
    {
        try
        {
            txt = "Plugin is written by Moustafa El-sawy ([email protected])";
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw;
        }

        return 0;
    }

    //Deconstructor to clean up
    ~cPlugin()
    {
        Console.WriteLine(GC.GetGeneration(0));
    }
}

}

I then have an empty window MainView and A viewmodel MainWindowViewModel.

Edit: Here is the boiler initial to start any plugin similar to what I showed above but has more explanation https://kinson.io/post/etabs-plugin-quickstart/


Solution

  • Your starting point should be Bootstrapper

    First you need to install:

    • Prism.Unity
    • Prism.Wpf

    Need to create your bootstrapper based on https://github.com/PrismLibrary/Prism/blob/master/src/Wpf/Prism.Wpf/PrismBootstrapperBase.cs

    Override the virtual method to create Shell (which is your main view that contains regions

    Override the virtual method to configure your container. Finally register your views and viewmodels

    P.S.: Consider using an interface for each registered type e.g. IShellViewModel

    using Prism;
    using Prism.Ioc;
    using Prism.Unity;
    using System.Windows;
    
    namespace PrismTest
    {
        public class Bootstrapper : PrismBootstrapperBase
        {
            protected override IContainerExtension CreateContainerExtension()
            {
                return new UnityContainerExtension();
            }
    
            protected override DependencyObject CreateShell()
            {
                return Container.Resolve<Shell>();
            }
    
            protected override void RegisterTypes(IContainerRegistry containerRegistry)
            {
                 containerRegistry.Register<IShellViewModel, ShellViewModel>();
            }
        }
    }
    

    Call your Bootstrapper from your plugin :

    Bootstrapper bootstrapper = new Bootstrapper();
    bootstrapper.Run();
    

    Your View (Shell)

    <Window
        x:Class="PrismTest.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:PrismTest"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Shell"
        Width="800"
        Height="450"
        mc:Ignorable="d">
        <Grid>
            <TextBox Text="{Binding MyText}" />
        </Grid>
    </Window>
    

    In Shell.xaml.cs: Use your view model. This can be also automatically injected

    public partial class Shell : Window
    {
        private readonly IUnityContainer _container;
        public Shell(IUnityContainer container)
        {
            InitializeComponent();
    
            _container = container;
    
            this.DataContext = _container.Resolve<IShellViewModel>();
    
        }
    }
    

    Your ViewModel

    public class ShellViewModel : BindableBase, IShellViewModel
    {
        private string m_MyText = "Shell ViewModel Text";
    
        public string MyText
        {
            get { return m_MyText; }
            set { SetProperty(ref m_MyText, value); }
        }
    }
    

    Your interface:

    internal interface IShellViewModel
    {
       string MyText { get; set; }
    }
    

    Resulting view;

    enter image description here