Planimate calling dotNET: Difference between revisions

From Planimate Knowledge Base
Jump to navigation Jump to search
No edit summary
Line 127: Line 127:
== Modeless Dialog ==
== Modeless Dialog ==


A new paint object "Window" will enable a class to be instantiated within a window in a panel in Planimate. The window will receive show/hide messages when the panel it is on is shown/hidden and size messages if the paint object is anchored. A paint object property will give access to its window handle and will typically be passed via "New()" so a managed class can set it as its parent window.
A WPF Window has a Show() method that causes it to be displayed without becoming modal. Planimate can call this to show the window and enable interaction with the controls on it. However its important that the popup know its owner and this can be done by calling SetOwner before Show().
 
p.result = PLCLR:SetOwner(p.handle)
p.result = PLCLR:Call(p.handle,"Show")
 
This requires the SetOwner() handler be present in the C# class, as described in modal dialog.
 
The window styles configured in the XAML and c# code control the title bar, resizing etc. The TestWPF sample demonstrates dynamically changing the style of the window to enable resizing and the titlebar.
 
=== Embedded Window ===
 
A modeless dialog can be embedded into a Planimate panel, becoming in effect a viewport on a dotNET class. Anchoring and resizing is fully supported, if the dotNET window includes a control that supports dynamic layout (eg: Grid) it will adapt to resizes.
 
In Planimate all that is required to put a popup into a Window paint object is to pass the paint object id to PLCLR's SetOwner:
 
p.result = PLCLR:SetOwner(p.handle,l._Paint_objects[MyWindow])
 
An additional function is required in the C# code, SetChild(). PLCLR calls this instead of SetOwner() when a paint object label is included. Refer to the WPFTest sample for its implementation. Please keep in mind this function may change with subsequent PLCLR releases.


== Exceptions ==
== Exceptions ==

Revision as of 19:55, 29 June 2016

This describes Planimate's interface to the dotNET environment which gives access to a wide variety of modern UI elements, table editing and graphing libraries as well as libraries that easily interface to web APIs and databases.

PLCLR

PLCLR.DLL is a Planimate callable DLL which provides the interface to dotNET's Common Language Runtime. You'll see its name in all the calls to the API. In future versions the calls will be integrated and appear as internal Planimate routines.

PLCLR expects you to point it to dotNET DLLs (called Assemblies). All DLLs involved must be in the same folder as the MDL during editing and the PBA EXE for a PBA. In addition, users will need Visual Studio 2013 runtime libraries and dotNET 4.5 installed. These can be obtained from Microsoft. When shipping a PBA, an installer should check for these and install as required.

Creating Instances Of Classes

You start by creating an instance of a class written in C# (Windows Forms) or c# / XAML (WPF). The examples below assume you have access to the TestCLR model and WPFTest sample C# project.

To create an instance you need the DLL name and the class name including namespace, eg:

p.handle = PLCLR:New(“WPFTest.DLL”,”WPFTest.MainWindow”)

p.handle is a number that you'll need to work with the object. You can also use a table cell to store it.

For future maintenance, its good form to put the strings into portal text attributes, literal text is used here to make the example clearer.

You should test that p.handle is > 0 after a New. If its <= 0 an error occurred, in which case take away the negative (0 – p.handle) and you have the PLCLR error code.

When you've finished using an object you should dispose of it, though in some instances an object will persist the entire lifetime of the PBA so this is not necessary.

p.result = PLCLR:Del(p.handle)

PLCLR will recycle handle values for deleted objects.

Calling Methods

Lets say you c# class has a method:

public void SayHello()
{
 MessageBox.Show(“Hello”);
}

You can call a method as follows:

p.result = PLCLR:Call(p.handle,”SayHello”)

Note the result of the call is always the result of PLCLR's operation, return values of methods are not used. Instead, you can pass parameters by value or reference, eg:

public void ComplexCalculation(int value_in,out double value_out)
{
 value_out = value_in + 0.5;
}

In Planimate you use

p.result = PLCLR:Call(p.handle,"ComplexCalculation(p.value1,p.value2)

Since value_out is declared as an "out", Planimate will set p.value p.value2 to the value calculated in the method.

Properties

If in your c# class you have a public property with a getter/setter eg:

String username  {get; set;}

Then you can use PLCLR's Get and Set operations to access them. With p.name a portal attribute in text format, you can use:

p.name = PLCLR:get("username");

and

p.result = PLCLR:set("uername",p.name);

This also works for numbers, see the next section.

Data Types

PLCLR has an extensible translation system to map between Planimate's simple data types and c#'s types. It matches the type of data passed in a parameter from Planimate with the type in a method's parameter or class property and chooses a translation as follows.

Conversions work for inputs to a method and outputs ("ref" or "out" parameters in C#) unless otherwise noted.


Parameter/Property Type Translation
dotNET Planimate Notes
double value (portal,routine,item/system attribute), table cell
int,uint value,cell Nearest Integer used
bool value,cell Nearest Integer then 0=false, nonzero=true
string Text attribute or cell Not label but you can use string expression to convert any format to text
double[], int[], uint[], boolean[] t.table.Column() Conversions above applied per cell
double[], int[], uint[] t.tablep[] Just column one sent with less overhead than Column(). Conversions above applied per cell. Row number changes on return supported.
string[] t.table.Column() Column must be a text column, currently cannot return data.
double[][] t.table[] Entire table's raw numbers, as array of columns, ie: array of array of numbers
Dictionary<String,int> l.labellist Label list mapped so elements are index values, keyed by name
Dictionary<int,String> l.labellist Label list mapped so elements are strings, keyed by their index value
string[] l.labellist Label list names sent as a array of Strings. In order of index but the index numbers are lost. If returned, a new list is created indexed from one. The labels must not be repeated.

Dates And Times

Currently all Planimate date/times are sent as the number of seconds since the model run start date. A future Datetime interface may be added. The PLEngine module can assist with conversion.

Modal Dialog

A WPF Window can be made to "go modal" and show a modal dialog by calling ShowDialog(). Whilst "Call" could be used to call this directly, you cannot repeat use of a model dialog, you would need to create a New instance, set properties, Call ShowDialog (or a method you create which does so), read back any properties you want back from the dialog then Delete the instance.

PLCLR provides a Dialog() operation which reduces this all to one line in Planimate.

p.result = PLCLR:Dialog(ddllname,classname,... up to 8 optional parameters...)

Like New, this loads an assembly and creates an instance of a class. It then calls a function that you must provide in the class called DoDialog(), passing the parameters which can be by value as well as "ref" or "out". It then disposes of the instance.

The DoDialog in the WPFTest sample (file MainWindow.xaml.cs) demonstrates using the parameters to set up fields in a simple dialog and to pass back information.

The Dialog() operation also calls another function in the class, if present, SetOwner(). This is used to ensure the model dialog knows Planimate is its owning window. You should include the sample SetOwner() you'll find in WPFTest in your class.

Modeless Dialog

A WPF Window has a Show() method that causes it to be displayed without becoming modal. Planimate can call this to show the window and enable interaction with the controls on it. However its important that the popup know its owner and this can be done by calling SetOwner before Show().

p.result = PLCLR:SetOwner(p.handle)
p.result = PLCLR:Call(p.handle,"Show")

This requires the SetOwner() handler be present in the C# class, as described in modal dialog.

The window styles configured in the XAML and c# code control the title bar, resizing etc. The TestWPF sample demonstrates dynamically changing the style of the window to enable resizing and the titlebar.

Embedded Window

A modeless dialog can be embedded into a Planimate panel, becoming in effect a viewport on a dotNET class. Anchoring and resizing is fully supported, if the dotNET window includes a control that supports dynamic layout (eg: Grid) it will adapt to resizes.

In Planimate all that is required to put a popup into a Window paint object is to pass the paint object id to PLCLR's SetOwner:

p.result = PLCLR:SetOwner(p.handle,l._Paint_objects[MyWindow])

An additional function is required in the C# code, SetChild(). PLCLR calls this instead of SetOwner() when a paint object label is included. Refer to the WPFTest sample for its implementation. Please keep in mind this function may change with subsequent PLCLR releases.

Exceptions

PLCLR will have a flag that will enable it to catch all exceptions caused in managed code and display them in a dialog, passing the failure back to Planimate as an error so model development can continue instead of being closed by the managed code exception.

Callbacks

Beyond passing parameters, PLCLR will provide an API to C# virtually identical to the Planimate-As-A-DLL API, enabling managed code to call back Planimate to get/set data.

This will be particularly useful for modeless UIs.

TBD: merging PL Call DLL callbacks with PL as a DLL callbacks.