Calling your own Assembly from JScript

Ok, one of the topics I keep alluding to (but getting side tracked) is how unsuitable Jscript is for larger scale projects – well at least in my mind :-). Jscript is a scripting language rather than a full blown development language, and though nifty, it really does show.

Take for example events, ok, you can subscribe to events within Jscript, but you can’t create them…

Multiple files – it’s kinda difficult to split your projects in to multiple files to make it more manageable.

But as per usual, there is a way around these things.

This post will go in to describe adding your own custom control to a standard Smart Office panel and with good reason…

We have a modification which essentially allows us to capture some additional information – it is a PITA mod and was poorly conceived (a lot of that blame can be firmly placed on our shoulders, but I would have hoped the consultant would have told us we were being idiots :-)).

Figure 1 – A Very Bad Idea

In short we record information about an order going on to a vessel, it creates a route and will record some information about numbers of containers, ETAs, ETDs and if there is any pre-carriage vessels and ports we need to worry about. Sure, there are better ways to capture the information but bear with me.

In order to mitigate the pain whilst we look at streamlining the process, wouldn’t it be nice if I could create my own control and drop it over the top of a standard M3 panel. The user fills in the information and I write it out to the database – yanking the modification but for all intents and purposes any reports that we have won’t know anything has changed.

Now we can do it, but with Jscript it will be a pretty cumbersome, it will be far easier to use C# and design a proper user control with XAML in Visual Studio and then just use a Jscript to call functions from my C# code compiled in to a .dll. Using the .dll and Visual Studio we also get the luxury of Intellisense.

So this post is about creating a .dll and calling a function of that .dll from Jscript – the dlls function will add a button to a panel and subscribe to the buttons events. When we hit the button we will draw our custom control on to the panel. You’ll have to forgive the dropping of the controls over top of others – this is proof of concept 🙂 and something that I think is quite exciting!

So first of all, in Visual Studio I am going to create a Class Library, I am calling it AddButtonToPanelExample

I am then going to add a WPF User Control (Right click on the project, Add -> New Item, select WPF -> User Control (WPF)), we can then use the WPF editor to customise our control and start building code.

Below is the XAML

<UserControl x:Class="AddButtonToPanelExample.AddCustomControlToPanelExample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="352" d:DesignWidth="603">
    <Grid Background="#FF5C9F6D">
        <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBlock1" Text="Order Number:" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="98,9,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
        <TextBlock Height="23" HorizontalAlignment="Left" Margin="86,107,0,0" Name="textBlock2" Text="Vessel:" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="128,104,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
        <TextBlock Height="23" HorizontalAlignment="Left" Margin="80,136,0,0" Name="textBlock3" Text="Voyage:" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="128,133,0,0" Name="textBox3" VerticalAlignment="Top" Width="120" />
        <TextBlock Height="23" HorizontalAlignment="Left" Margin="38,53,0,0" Name="textBlock4" Text="Customer:" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="98,50,0,0" Name="textBox4" VerticalAlignment="Top" Width="120" />
    </Grid>
</UserControl>

Which looks like this (aesthetics aren’t my strong suit ;-))

I am going to add a new reference to my project (Right Click on References and select Add Reference). You will want to find the MForms.dll in the install of Smart Office, in my case “C:\Users\Scott\AppData\Local\Apps\2.0\K3HXJ5OH.H0Y\MHKPBLEQ.Z77\http..tion_3eefdc12643b7dbb_0009.0001_5f058381d83db388\MForms.dll”, we need this assembly so we can do the actual adding of our control to the panel.

Solution Explorer should show something like this:

And the code itself

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

// using MForms;



namespace AddButtonToPanelExample
{
    public class AddButtonToPanelExample
    {
        // a property where we get / set the InstanceController from Smart Office
        public MForms.InstanceController InstanceController { get; set; }

        // this method will go out and add our button to the panel
        public void AddButtonToPanel(string astrName)
        {
            // ensure that we have an InstanceController, no point in proceeding if we don't
            if (null != InstanceController)
            {
                // create our new button
                Button btnNewButton = new Button();
                if (null != btnNewButton)
                {
                    // set the content and position of the button
                    btnNewButton.Content = "PotatoIT!";
                    Grid.SetColumnSpan(btnNewButton, 10);
                    Grid.SetColumn(btnNewButton, 0);
                    Grid.SetRow(btnNewButton, 1);

                    // subscribe to the click event of the button
                    btnNewButton.Click += new RoutedEventHandler(btnNewButton_Click);

                    // finally add the button to our Smart Office Panel
                    InstanceController.RenderEngine.Content.Children.Add(btnNewButton);
                }
            }
        }

        // this is our event handler for the clicking of the button
        void btnNewButton_Click(object sender, RoutedEventArgs e)
        {
            // create a new instance of our custom control
            AddCustomControlToPanelExample ccCustomControl = new AddCustomControlToPanelExample();
            if (null != ccCustomControl)
            {
                // set its position, height and width
                Grid.SetColumnSpan(ccCustomControl, 60);
                Grid.SetColumn(ccCustomControl, 0);
                Grid.SetRow(ccCustomControl, 2);
                Grid.SetRowSpan(ccCustomControl, 10);

                // finally add it to the panel
                InstanceController.RenderEngine.Content.Children.Add(ccCustomControl);
            }
        }
    }
}

Compile this code as release code and you should get a .dll. We will be loading this .dll from our Jscript, so you need to know the path. In my case I have copied the .dll to a folder in my desktop (C:\Users\Scott\Desktop\x\AddButtonToPanelExample.dll)

Ok, now we are ready to tackle the jscript side. There’s really not a lot in it, so I will let the comments speak for themselves.

import System;
import System.Reflection;
import System.Windows;
import System.Windows.Controls;
import MForms;

package MForms.JScript
{
    class ExternalCodeExample
    {
        public function Init(element: Object, args: Object, controller : Object, debug : Object)
        {
            // load our assembly
            var clExampleClassLibrary : Assembly = Assembly.LoadFrom("C:\\Users\\Scott\\Desktop\\x\\AddButtonToPanelExample.dll");
            var content : Object = controller.RenderEngine.Content;

            if(null != clExampleClassLibrary)
            {
                // now we need to actually initialise an instance of the assembly / create an object of type AddButtonToPanelExample (from the AddButtonToPanelExample namespace)
                var objButtonToPanel = clExampleClassLibrary.CreateInstance("AddButtonToPanelExample.AddButtonToPanelExample", true);
                if(null != objButtonToPanel)
                {
                    // we set the controller on our object (we could pass this as an argument if we really want)
                    objButtonToPanel.InstanceController = controller;
                    // finally call the function which will add a button to the panel
                    objButtonToPanel.AddButtonToPanel("PotatoIT!");
                }
                else MessageBox.Show("Failed to create instance");
                
            }
            else
            {
                MessageBox.Show("Failed to load Assembly");
            }
        }
    }
}

Execute the script against say OIS300 and you should see something similar to below:

Our Jscript goes out and retrieves my assembly which is written in C#, it then calls the assembly function AddButtonToPanel() which adds the PotatoIT! Button. So we are out in managed code world within my assembly. The AddButtonToPanel() function will also subscribe to the Click event of the button we have added.

Clicking on the button adds the custom user control to the window.

But WHY?! WHY?! Would you do such a thing? Aside from “Because I can” ;-), this opens up a world of possibilities. With this I can now go out and create a .dll which talks back to other services, in the case of our Vessel Mod, I can record all of the information and write it to the database without the modification! We can also hide things like passwords in this dll which the user would be easily able to see within a Jscript. We can also develop more complex code in a friendly environment.

There are some considerations – distributing the .dll (and any supporting .dlls). You may be able to include them in your Smart Office install, or distribute them to your users by setting the dll path so it is a URL on a webserver, or create a separate installer that you roll out with your tool of choice. And you may need to recompile your .dll against the new mform if Smart Office changes.

Also, there is some credit that needs to go to Thibaud Schneider, I read a PDF entitled “How to consume a Lawson Web Service from a Personalized Script in Smart Office”, there some examples of loading an assembly dynamically in Jscript – and though they didn’t work for my project due to some of the dodgy stuff I am doing, but they pointed me in the right direction. Thank you!

Happy coding… 🙂

This entry was posted in Development, How Far is Too Far?, M3 / MoveX and tagged , . Bookmark the permalink.

2 Responses to Calling your own Assembly from JScript

  1. Paul Grooby says:

    Scott – once again magic stuff (particulary given that we had a substantial modification that needs to be removed from the process – and I like the fact that I can hide the passwords particulary since this mod does some really scary calc )

    Paul

    • potatoit says:

      Hi Paul,

      thanks for the comment 😉

      Yes, I am quite excited about this route as it is so much quicker and easier to develop in C# or VB.Net than Jscript. I hope to do some more detailed exploration over the next few weeks to discover potential pitfalls.

      Cheers,
      Scott

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s