WebEdit.NET allows third parties to create powerful AddIns to enhance the application's functionality. Unlike scripts, AddIns require no programming knowledge on part of the user, and are easy to set (by copying the DLL into the "bin" subdirectory, and selecting it via the Settings dialog). However, AddIns allow many forms of interaction, including the provision of macros as well as exposing their API to scripts, enabling power users to customize their behavior.
AddIns can be used in several ways:
AddIns provide a user-friendly method of enhancing WebEdit's functionality. To use an AddIn developed by a third party, follow these steps:
Alternatively, you can install and start an AddIn from the Console tool window, if you happen to know the path to the AddIn DLL. Open the Console, and enter the following commands (this will copy the DLL to WebEdit.NET's bin directory, and start the AddIn):
addin:install?C:\AddIns\SomeAddIn.dll addin:start?SomeAddIn
How you use the AddIn depends on the particular AddIn you have installed. See the documentation that the AddIn author provides.
Sometimes, the AddIn offers macros. There are several ways to call them:
Here's how you invoke a macro with a command:
macro:SomeAddIn.Utilities.EnhanceDocument
Script AddIns are per-session AddIns that are coded directly in WebEdit.NET, and are set up with a simple menu action. They provide support for ad-hoc scripting and temporary macros.
For information on how to write regular AddIns for permanent use, please refer to "Developing AddIns" further down this page.
Script AddIns are even easier to write, build, and set up than regular AddIns. Basically, all you need to do is write some code you want to execute in an editor window, and choose "Build/Build Script AddIn ..." from the menu.
In the dialog box that pops up, select a language (C# or VB.NET). Then, choose an option for WebEdit to complete the code before it sets up the Script AddIn, which depends on the type of code you're using:
There are a couple of program settings that affect the compile process. In the "Code & Scripting" setting group, the "Assemblies" setting lets you configure additional references to DLLs that live either in the application's "bin" directory or are part of the .NET framework; the "Imports" setting can be used for additional imports (effective only if you choose "Wrap Method Arround Code" or "Wrap Module Arround Methods" above).
Build results (compiler output) will be written to the Output tool window. You can now start your macros from the Macros tool window, the "Tools/Run Macro ..." menu action, or with a command of the "macro:" protocol. If you have chosen to wrap a module arround methods (see above), a namespace called "N" and a module called "M" will be wrapped arround the code:
macro:N.M.MyRoutine
Additionally, if you have elected to wrap a method arround code snippets, the one and only macro will be named "Foo":
macro:N.M.Foo
With the "Default Namespace" and "Default Module" settings in the "Code & Scripting" setting group, you can change the names of the namespace and module wrapped arround the code. See below for more information on how you can control macro usage through .NET custom attributes.
Use can browse your code in the Assembly Browser using a command like this:
type:N.M
In your code, you can temporarily store and later retrieve the results of your work (any object, that is) using the SetValue/GetValue methods of the Gregor.WebEdit.Vars module. The values are valid during the entire session, even between compiles. Because they are managed through WebEdit's C# Code Interpreter, you can also enrich that environment by creating new variables useable, for example, in parameterized templates.
If a code snippet is wrapped with a method, then that method will return System.Object. While you don't have to code a return statement explicitly, you can return any calculated value, and thus make the wrapped method more useful to call from the Code Interpreter.
You can handle events in Script AddIns either through explicit event management code, or you can handle events in a declarative fashion, using a custom attribute that defines the sender type or object and the event name:
[Handler("WebEditApp.MainForm", "ActiveDocumentChanged")]
private static void HandleIt(object sender, EventArgs e){
WebEditApp.Trace("active doc changed");
}
Declarative event handling only works for static handler methods. However, both static and instance events may be handled, including events raised by the Script AddIn itself. The event arguments parameter can be a base class or interface of the parameter type that the event delegate declares ("EventArgs" is usually just fine).
The attribute arguments are processed as follows: The first string, which identifies the sender type or object, is interpreted as a fully-qualified type name, failing that, any of the namespaces in the "Imports" setting is prepended, in order to find a type. If a type is found, a static event of the name given in the second string is searched, and connected to the method (if found). If no type is found, the sender string is evaluated with WebEdit's code interpreter (any options or imports apply). If the expression results in a valid object reference, an instance event of the name given in the second string is searched, and connected to the method (if found).
Note that the Gregor.Core namespace, which defines the HandlerAttribute class, is usually imported (see the "Imports" setting).
Note that Script AddIns are not persisted between sessions. If you want to use the code later on, you need to save the source to disk, or create a regular AddIn (see below).
For script AddIns to work, Syntax Providers, Code DOM providers, as well as Fixup Providers must be configured for the languages desired. By default, these objects are available for both C# and VB.NET. See the languages configuration file (WebEditLanguages.xml).
You can script against an AddIn either through the C# Code Interpreter, or using code (macros especially) in another AddIn (often a Script AddIn).
Depending on the particular AddIn, you might have to retrieve the connector object of the AddIn. Unless the AddIn vendor has specified differently, you should do the following (this example uses a fictitious spell checker AddIn):
First, get the instance of Gregor.AppCore.Extensions.CAddIn representing the AddIn, (use the name of the AddIn as shown in the first column of the "AddIns" setting in the setting dialog):
Dim addIn As CAddIn = WebEditApp.AddInManager.AddIns.Item("SpellChecker")
Then, get the connector object, and cast it to its actual type (information about the type will be provided by the creator of the AddIn):
Dim ac As Object = addIn.FindConnector() Dim sc As CSpellCheckConnector = DirectCast(ac, CSpellCheckConnector)
Next you can call the methods and properties defined by the AddIn:
sc.IncludeGrammarCheck = True sc.CheckActiveDocument()
AddIns can be written in any language the targets the .NET framework. AddIns are managed class libraries; the assembly is loaded dynamically (if and when the user has included it in the "AddIns" setting and either has both set the "AutoStart" option to "True" and restarted the application, or has set the "Running" option to "True").
Whenever an AddIn is started, the AddIn assembly is searched for types that implement the Gregor.AppCore.Extensions.IAddInConnector interface. When such a type is found, it will be instantiated, calling the default constructor (which must be public). The application keeps a reference to this instance (which can be retrieved by scripts).
While the AddIn connector class itself may expose members to scripts, the IAddInConnector interface should be implemented privately, in order to discourage user scripts to call its methods (which are suitable only for the application, as the AddIn's controller).
The IAddInConnector interface contains two methods: Initialize() and Terminate(). Both are called by the application. Use the first one, for example, to set up event handlers, add menu actions, create tool windows, etc.; use the second one for clean-up. It is guaranteed that both Initialize() and Terminate() are never called twice without calling the other one (respectively). However, an AddIn may be started and stopped many times by the user. If an AddIn is set to "AutoStart", it is first initialized before the command line is processed.
The AddInConnector2 interface in Gregor.AppCore.Extensions allows dynamic discovery of further AddIn features (Supports() method; may be called at any time) and performing additional operations (Perform() method) in an extensible manner.
Features and operations are disambiguated through so-called feature tokens, which are instances of System.Object, obtained through static readonly fields of certain types (in other words, the .NET type system is used to guarantee that feature tokens are unique). The AddInConnectorFeatures module in Gregor.AppCore.Extensions already defines some feature tokens, and WebEdit.NET defines others in WebEditScriptAddInConnectorFeatures.
AddIns always allow script interaction; implementing IAddInConnector2, and testing for AddInConnectorFeatures.AllowScripting as the feature token in the Supports() method is no longer necessary.
If you want your AddIn to expose macros to the user (GUI-callable, public static routines, possibly with string or string-convertible parameters), implement IAddInConnector2, and return True from the Supports() method if the feature token passed is identical to AddInConnectorFeatures.AllowMacros. Any public static method can be a macro regardless of its return type. If the method doesn't have parameters, it's macro. If if has parameters, and the type of each parameter is System.String or else can be converted from System.String, the method becomes a macro if it's decorated with a special attribute. Likewise, it's possible to exclude public static parameterless methods from the macro set:
// not a macro by default
public static void Foo1(Control ctl){}
// macro by default
public static string Foo2(){}
// macro by attribute
[Gregor.Core.Macro(true)]
public static void Foo3(string s){}
// macro by attribute
[Gregor.Core.Macro("Quadruples the foo.")]
public static string Foo4(char c, int i){}
// not a macro by attribute
[Gregor.Core.Macro(false)]
public static void Foo5(){}
On macros with parameters, consider applying the Gregor.Core.DefaultValue custom attribute:
[Gregor.Core.Macro(true)]
public static void Bar([DefaultValue("Hershey Bar")]string sName){
WebEditApp.Trace(sName);
}
Note that the user cannot invoke macros implemented by overloaded routines.
WebEdit.NET supports declarative event handling in AddIns by using a custom attribute (Gregor.Core.HandlerAttribute) as discussed above. To enable this mechanism, the Supports() method must recognize the object set in WebEditScriptAddInConnectorFeatures.AllowHandlers. Keep this in mind when migrating Script AddIns (which use a connector that is so implemented). However, for regular AddIns, explicit imperatively coded event management is preferred for performance reasons.
You can access WebEdit's objects by importing the Gregor.WebEdit namespace and then using the static members of the class WebEditApp. Note that the user can alter application state by using Scripting. Also, other AddIns may manipulate WebEdit. Therefore, conservative programming techniques are strongly recommended. The AddIn should handle all exceptions it encounters (especially in event handlers), by calling WebEditApp.ProcessException(). For debugging purposes, call WebEditApp.Trace(). If you need to use keys in collection (i.g., the action list), prefix the key with the name of the AddIn in order to help avoid naming conflicts. With regard to AddIn-defined actions, set the Category property to the name of the AddIn.
An AddIn assembly must reference Gregor.AppCore.dll in order to use the IAddInConnector interface, and in virtually all cases it will need to talk to the types defined in Gregor.WebEdit.dll as well. Typically, referencing one or more libraries of the Gregor.NET application framework is required, too.
An AddIn will typically register objects at WebEdit's extension points, or provide other complex functionality. However, it's also possible to expose macros to the user.
AddIns can be built with any tool (for example, from the command line, or with a third-party IDE, such as Visual Studio), but note that WebEdit offers various little helpers for building AddIns, such as an AddIn connector class template, special build commands in the languages configuration file (WebEditLanguages.xml), as well as commands for installing and starting an AddIn.
Here's a step-by-step instruction of how to create an AddIn directly in WebEdit. This assumes the template and language configurations offered in the original distribution (see WebEditLanguages.xml and WebEditTemplates.xml), as well as "Imports" settings ("Code Interpreter" setting group) to include the import "Gregor.WebEdit":
For a "Hello, World!" example of AddIn features, usage and instructions for building from the command line, see the file Indigo.vb.
If you make changes to the code of an AddIn that's already installed, you can reload it without having to restart the application. Just stop, remove, install, and start the AddIn:
addin:stop?MyAddIn addin:uninstall?MyAddIn addin:install?C:\Data\MyAddIn.dll addin:start?MyAddIn
See Also: WebEdit.NET API | Extension Points | Default AddIns