【问题标题】:How to hook up Microsoft Forms 2.0 event handlers using JScript如何使用 JScript 连接 Microsoft Forms 2.0 事件处理程序
【发布时间】:2019-06-06 19:23:03
【问题描述】:

我正在尝试将自定义 UI 页面添加到 Sparx EA。它提供通过脚本添加 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 类,但没有任何乐趣。

【问题讨论】:

    标签: enterprise-architect jscript


    【解决方案1】:

    我认为您在这里同时遇到了几个问题。

    (1) 进程生命周期

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

    脚本完成后进程会被有效终止(因此也会终止您可能在UserControlForm 对象中注册的任何事件处理程序)。

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

    如果您能以某种方式延长脚本环境的生命周期,并且如果您可以将某些内容传递给您的事件,您将意识到 JScript 代码中的任何对象都将作为 System.__ComObject 传递给内部的 .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 用作脚本引擎。

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

    function main()
    {
      var ctrl = new ActiveXObject("IMASE.TestUserControl2");
      ctrl.Repository = Repository;
      ctrl.Session = Session;
    
      Session.Prompt("wait", promptOK);
    }
    
    main();
    

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

    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);");
    }
    

    这是上述脚本交互的输出:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多