Implementing a plug-in

LLBLGen Pro comes with a plug-in system, exposing the project to the plug-in. Plug-ins can be made easily by the developer. The plug-ins can be handy when you have to add a feature especially for your project, like renaming a lot of fields according to an xml file.

The LLBLGen Pro install comes with a set of plug-ins, which sourcecode is bundled with the sourcecode archive available under the downloads section of 'My account' on the website. You can use the sourcecode of these plug-ins as an example how to write a plug-in yourself.

This section gives some guidelines how to write your own plug-in. It's highly recommended you both check out the plug-ins sourcecode shipped with this SDK and the ApplicationCore reference manual also shipped with this SDK for details about the properties and methods of various classes.

Writing your own plug-in, a guide.

This guide steps through the steps to get you up and running with your first plug-in for the LLBLGen Pro designer. First, familiar yourself with the details about the plug-in system, which are described in step 0 below. Then go through the various steps to create your own plug-in. Be sure to think through what you want to do in a plug-in before starting.

Step 0: familiar yourself with the plug-in system details

All plug-ins have to be implemented using .NET and have to derive from a plug-in baseclass. This base class implements the foundation for a plug-in and describes the plug-in, allows the designer to call a plug-in etc. This base class also offers functionality to accept delegates to various methods in the LLBLGen Pro designer, and for example access to a progress window.

A plug-in is developed in a library assembly, which results in a .dll. Each plug-in dll has to be placed in the Plugins folder of the LLBLGen Pro installation before LLBLGen Pro is started. All dlls are loaded into the designer and used if one of the components in the dlls derives from PluginBase which is the common base class for plug-ins. If a plug-in crashes during startup, it is mentioned and the plug-in is ignored. The user has to restart llblgen pro if a new version of a plug-in has to be loaded. PluginBase is located in the Extensibility namespace in ApplicationCore.

A plug-in can also create a dockable window. In the case you want this, which is for example done in the Project Inspector plug-in, shipped with LLBLGen Pro, it's best to make the plug-in a DirectRun plugin, which means that it won't cause a pop-up before it's run, but it's run immediately.

When a plug-in isn't a DirectRun plug-in, it will get a bag with elements which it should use as the elements to work on. It also receives the whole project object to work on if required. The bag is a hashtable with as key the type of element in the value and as value an arraylist with objects.

It furthermore gets the application settings and user preferences as input. A hashtable with delegates is provided for the plug-in to work with the progress window created for the plug-in. The base class PluginBase is implemented in ApplicationCore, as well as the describe info object, so you'll have to reference the ApplicationCore assembly in your plug-in project.

The base class requires the plug-in to override a couple of virtual methods. One of them is the Describe method which fills a PluginDescription object. This object is used by the plug-in execution system to handle the plug-in inside the designer. It furthermore should override a method which produces a control which can be used for settings for the plug-in.

This method is called GetConfigurationControl. The control produced by this method is hosted in the dialog which is popped up by LLBLGen Pro when the user decides to run a plug-in. This control has to implement the interface IPluginConfigurationControl which exposes 2 events: DataIsValid and DataIsInvalid, for the run button enable/disable handling.

If DataIsValid is never fired, the plug-in will never run because the user won't be able to click the run button, as it will stay disabled. The third important method to override is Execute which actually runs the plug-in. The base class has empty implementations of these methods so not overriding them will simply produce a plug-in which doesn't do anything.

Plugins are ran from the project explorer: by right-clicking on a root node (e.g. Entities) or a single object node (like an entity), the user can select to run a plug-in and select one from the cascading menu. Plug-ins can only be started when clicking Project elements, not by clicking fields or other tiny project elements.

When the plug-in's Execute method is called, a ProgressViewer instance is shown to show the progress of the plug-in. The plug-in can control that pogress viewer by calling base class methods, like ProgressTaskStart, ProgressTaskInit etc. Of course, if the plug-in is a DirectRun plug-in, no ProgressView is shown.

Plug-ins can be viewed from the main menu: Tools -> Plugins... which pops up the plug-in viewer, which displays per plug-in the information retrieved from the plug-in's Describe method, so it is key you supply enough information in that method. Each plug-in has to have a unique GUID to identify itself.

A plug-in can control if the project explorer has to be reset before execution, which can gain some performance on long running plug-ins. This is all done through the object returned from Describe. Please consult the LLBLGen Pro designer core assembly reference manual for more information about the various settings possible in the PluginDescription object.

Step 1: setting up VS.NET

Create a library project in your language of choice and add a reference to the SD.LLBLGen.Pro.ApplicationCore.dll assembly found in the LLBLGen Pro installation folder, as well as the SD.LLBLGen.Pro.Core.dll. Add a class which derives from PluginBase. This will be your plug-in class.

Use the designer core assemblies reference manual for the namespace names you have to specify to obtain access to public classes like the Entity Definition. All model classes are defined in the ApplicationCore assembly. All relational model data classes are defined in the DBDriverCore assembly.

Create three overrides in your plug-in class, one for Describe, one for Execute and one for GetConfigurationControl. There is no need to call base class' methods in these methods as they're empty.

Add a user control to your library project. This will be the control which will be returned from GetConfigurationControl. Open the code window of the control and make sure the control implements IPluginConfigurationControl and add the two event handlers.

Step 2: setting up Describe

Describe is an important method. In here you specify what your plug-in will do, who created it etc. etc. An example implementation is shown below.

public override PluginDescription Describe()
{
    PluginDescription toReturn = base.Describe();

    toReturn.Build = _build;
    toReturn.Description = "General plug-in to add custom properties to various elements of an LLBLGen Pro project." + 
        " It allows you to add the same custom property to all selected project elements.";
    toReturn.Id = new Guid("{E63E0064-E1CD-4726-8340-57290A3E144E}");
    toReturn.Name = "Add Custom Properties Plug-in";
    toReturn.ShowProgressViewerDuringExecution=true;
    toReturn.TargetType = PluginTargetType.SPCall | PluginTargetType.Entity | 
        PluginTargetType.Project | PluginTargetType.ValueType | 
        PluginTargetType.TypedList | PluginTargetType.TypedView;
    toReturn.TypeOfPlugin = PluginType.SingleAndMultiElementPlugin;
    toReturn.Vendor = "Solutions Design";
    toReturn.Version = _version;

    return toReturn;
}

Not all properties are set, which means the defaults are used. Consult the designer core assemblies reference manual for details about these properties. As you can see, the target type is specified with OR operators, to concatenate the flag enum values. If you specify just one target type, the plugin won't show up in context menus on elements the plug-in can't be run on.

Step 3: setting up the configuration control

The control's layout is up to you, as long as you fire the DataIsValid event as soon as the data is valid so the LLBLGen Pro designer can enable the run button to run the plug-in. Even if your plug-in doesn't have any settings, you still have to produce and return a valid winforms control and raise DataIsValid.

Below is a name validation event handler which is the element on the control which has to be filled in to make the example plug-in work. If validation fails, the data is invalid and the proper event is raised, otherwise the DataIsValid event is raised.

private void _nameTextBox_TextChanged(object sender, System.EventArgs e)
{
    if(_nameTextBox.Text.Trim().Length>0)
    {
        if(DataIsValid!=null)
        {
            DataIsValid(this, new EventArgs());
        }
    }
    else
    {
        if(DataIsInvalid!=null)
        {
            DataIsInvalid(this, new EventArgs());
        }
    }
}

To set up GetConfigurationControl in your plug-in class, simply add similar code as the snippet below:

public override System.Windows.Forms.Control GetConfigurationControl()
{
    _control = new AddCustomPropertiesConfigurationControl();
    return _control;
}

This method stores the instance of the control in a member variable, for later reference.

Step 4: implementing Execute

The heart of your plug-in is of course the Execute method. All data required to make the Execute method work and run your plug-in is provided by the base class through properties, like the ProjectToTarget property, the Entities property etc. etc. If your plug-in was described as a plug-in which should run on multiple entities, it is key you use the separate collections for the objects, like Entities, TypedLists, of the base class, as these will contain the selections the user made in the object selector.

Consult the LLBLGen Pro designer core assembly reference manual for detailed information about the various properties exposed to you through the base class PluginBase.

If you've described your plug-in as a plug-in which requires the progress window, you can interact with that window using the following methods. The progress window is divided into two areas: one for tasks and one for sub tasks.

Per task you should use the sub task oriented methods to show the progress within the current task. For controlling the task area, you have three methods, which are offered to you by the base class PluginBase: ProgressTaskInit, ProgressTaskStart and ProgressTaskComplete. With ProgressTaskInit you specify the number of tasks to perform, for example 5. This will make sure that the top progress bar on the progress window will have 5 steps and will be full when the ProgressTaskComplete method is called 5 times. It is essential you specify the right number of tasks to perform.

ProgressTaskStart accepts a string which is displayed below the top progress bar on the progress window so the user can see which task is currently in progress. By calling ProgressTaskComplete, you signal the progress window that the task is done and it should increase the top progress bar.

The sub tasks have their own set of methods, but they work the same: ProgressSubtaskInit, ProgressSubtaskStart and ProgressSubtaskComplete.

Another way to give feedback to the user is through the ApplicationOutput window, docked at the bottom of the LLBLGen Pro designer. To write a line of text to that window, you use the base class method LogLineToApplicationOutput and specify the parameters it requires, like the source of the message, if it is a verbose message or not etc. Below is an example of how to use the discussed methods in action.

base.ProgressTaskStart("Processing SP Calls");
base.ProgressSubtaskInit(this.SPCalls.Count);
foreach(SPCallDefinition spCall in this.SPCalls)
{
    base.ProgressSubtaskStart("Processing sp call: " + spCall.Name);
    switch(workTarget)
    {
        case Target.Element:
        case Target.ElementAndField:
            CreateCustomProperty(spCall.CustomProperties, existsAction);
            base.LogLineToApplicationOutput(
                string.Format(
                    "New custom property '{0}' added to retrieval sp call '{1}'", 
                        _control.CustomPropertyName, spCall.Name), "AddCustomPropertiesPlugin", true, true);
            break;
    }
    base.ProgressSubtaskComplete();
}
base.ProgressTaskComplete();

workTarget is a setting selected in the plug-in control for the settings.

Step 5: compiling and debugging

After you've done all the previous steps, you have code which should work as a plug-in. Compile the assembly and place it in the Plugins folder of the LLBLGen Pro installation, or the Additional Plugins folder you've specified in the Preferences and start LLBLGen Pro and check if everything works as planned.

Now, it can happen that there is a bug in your plug-in. So first of all, be sure to set the LLBLGen Pro designer preference CreateBackupBeforeRunningPlugin to true, so you won't lose any precious work.

Then, if you want to debug your plug-in, take the following steps:

  • Load the plug-in project in VS.NET
  • Create a debug build and copy the .dll and the .pdb file into the Plugins folder (or additional plugins folder)
  • Run the LLBLGen Pro designer
  • In VS.NET, press cntrl-alt-p. This allows you to attach to a process, select LLBLGenPro.exe and just select the managed debugger.
  • Set a breakpoint in the method you want to break and switch to LLBLGen Pro and start your plug-in. You should now break at the breakpoint you've set.