如何使用JScript连接Microsoft Forms 2.0事件处理程序

时间:2019-06-06 19:23:03

标签: enterprise-architect jscript

我正在尝试向Sparx EA添加自定义的UI页面。它提供了通过脚本添加ActiveX控件的功能。使用JScript,我已经做到了,但是由于ActiveX必须在每个客户端上注册,因此我宁愿使用已经安装在所有客户端上的Microsoft Forms。

我已经通过添加“ Forms.Form.1” ActiveX对象并将文本框,标签和按钮添加到所创建表单的控件属性中,成功构建了UI(外观方面)。 这些对象支持事件,但是我不知道如何分配事件处理程序。

这是我用来获取屏幕布局的JScript代码:

function _addControl(parentControl, controlProgId, controlName, left, top, width, height){
    var newControl = parentControl.controls.add(controlProgId, controlName,1);
    newControl.Name=controlName;
    newControl._SetLeft(left);
    newControl._SetTop(top);
    newControl._SetWidth(width);
    newControl._SetHeight(height);
    return newControl;
}

function main(){
    //Create main form
    var form = Repository.AddTab("ScriptedForm", "Forms.Form.1");
    if (null != form){
        //Add control
        var textBox1 = _addControl(form, "Forms.TextBox.1","TextBox1", 18,21,94,93);
        var textBox2 = _addControl(form, "Forms.TextBox.1","TextBox2", 120, 21, 91, 93);
        var btnTest = _addControl(form, "Forms.CommandButton.1", "btnTest", 60, 140, 90, 30);
        btnTest.Caption = "Test";

        //Here's where I assign the click event, but it's unhappy.
        btnTest.add_Click(this.TextBox1_Click);
    }
}
function TextBox1_Click(Object){
    Session.Prompt("Click", promptOK);
}

add_Click事件需要一个类型为CommandButtonEvents_ClickEventHandler的参数。
我无法创建任何可以作为参数提交的内容。我尝试创建一个复制接口的JScript类,但没有任何乐趣。

1 个答案:

答案 0 :(得分:2)

我认为您一次遇到了几个问题。

(1)流程生命周期

据我了解您的问题及其上下文,您正在以某种方式手动执行JScript脚本。进行此EA将从内部开始SScripter.exe。您会在 Debug 窗口中看到以下内容:

Debug window showing a new debug instance backed by SScripter.exe

脚本完成后,该过程实际上终止了(因此也终止了您可能已经在UserControlForm对象中注册的所有事件处理程序)。

(2)将JScript对象实例作为.NET delegate

传递

如果您可以某种方式延长脚本环境的寿命,并且可以将某些内容传递给事件,则您将意识到JScript代码中的任何对象都将作为System.__ComObject传递给EA中的.NET运行时。因此,您不能仅注册事件处理程序。 但是,当您从.NET评估对象时,您会发现它不是IDispatch接口:

MemberNames: 
ToString, 
GetLifetimeService, 
InitializeLifetimeService, 
CreateObjRef, 
Equals, 
GetHashCode, 
GetType

TargetInvocationException@mscorlib: 'COM target does not implement IDispatch.' 

我用下面的代码做了一个小测试:

function MyClass(name)
{
  this.name = name;
}

MyClass.prototype.Invoke = function(value) 
{
  Session.Output("name " + value);
    return true;
}

function main()
{

  var myClass = new MyClass("Hotzenplotz");
  myClass.Invoke("some Value");

  var ctrl = new ActiveXObject("IMASE.TestUserControl2");
  ctrl.Repository = Repository;
  ctrl.JavaScriptObject = myClass;
}
[ProgId(Global.ADDIN_NAME + Global.DOT + "TestUserControl2")]
[Guid("87156dd9-e947-44bf-92a9-e9554a5b1844")]
[ComVisible(true)]
public partial class TestUserControl2 : ActivexControl
{
  public static string TabName { get; } = Global.ADDIN_NAME;

  private static readonly Lazy<string> _controlId = new Lazy<string>(() =>
  {
    var attribute = typeof(TestUserControl).GetCustomAttribute<ProgIdAttribute>();
    return attribute.Value;
  });

  private Timer timer;

  public static string ControlId = _controlId.Value;

  public Repository Repository { get; set; }
  public object JavaScriptObject { get; set; }

  public TestUserControl2()
  {
    timer = new Timer();
    timer.Elapsed += TimerEvent;
    timer.Interval = 5000;
    timer.Enabled = true;
    timer.Start();
  }

  ~TestUserControl2()
  {
    Logger.Default.TraceInformation("I'm gonna die ... " + this.GetHashCode());
  }

  private void OnDispose(object sender, EventArgs e)
  {
    timer.Dispose();
  }

  private void TimerEvent(object source, ElapsedEventArgs e)
  {
    Logger.Default.TraceInformation("I'm still alive ... " + this.GetHashCode());

    if(null == JavaScriptObject) return;

    try
    {
      var memberNames = JavaScriptObject.GetType().GetMembers(BindingFlags.Instance|BindingFlags.FlattenHierarchy|BindingFlags.Public).Select(p => p.Name);
      Logger.Default.TraceInformation("memberNames: " + string.Join(", ", memberNames));

      var result = JavaScriptObject.GetType().InvokeMember("Invoke", BindingFlags.InvokeMethod, null, JavaScriptObject, new object[] {"arbitraryString"});
      Logger.Default.TraceInformation("result: " + result);
    }
    catch (Exception ex)
    {
      Logger.Default.TraceException(ex);
    }
  }
}

(3)另一种方法

在外接程序中创建UserControl(使用WinFormForms),然后将ClearScript用作ScriptEngine。

将EA脚本中的SessionRepository传递给您的控件(或执行其他操作,例如使用菜单来解决生命周期问题),并让您的表单代码从存储库中加载脚本(或任何其他来源)。然后对事件处理程序做出反应,以根据需要执行JScript代码。我创建了一个简单的示例,该示例演示如何从EA JScript调用控件,并从表单代码内部调用另一个JScript,然后依次登录到Debug会话或常规脚本输出窗口:

function main()
{
  var ctrl = new ActiveXObject("IMASE.TestUserControl2");
  ctrl.Repository = Repository;
  ctrl.Session = Session;

  Session.Prompt("wait", promptOK);
}

main();

在表单代码中,您可以使用JScript和其他类似的对象来调用Repository

public Repository Repository { get; set; }
public object Session { get; set; }

using (var engine = new JScriptEngine())
{
  engine.AddHostObject("Repository", this.Repository);
  engine.AddHostObject("Session", this.Session);
  engine.Execute("Session.Output('Repository.ConnectionString: ' + Repository.ConnectionString);");
}

以下是上述脚本交互的输出:

Debug Window with messages from ClearSCript Engine inside ActiveX Control

侧面说明:我个人认为不需要使用表单,因为我们可以在AddIn启动时动态注册ActiveX控件。有关执行此操作的代码,请查看以下要点:

https://gist.github.com/dfch/6a27bb1b9320c93456cee6d5b2b9d551

此外,如果您使用ClearScript作为脚本宿主,则可以按照ClearScript FAQtorial问题#16中的描述,通过脚本代码直接连接到(UI)事件。