Author Topic: Trouble adding a Custom Window to an Addin  (Read 10215 times)

philchudley

  • EA User
  • **
  • Posts: 740
  • Karma: +20/-0
  • UML/EA Principal Consultant / Trainer
    • View Profile
Trouble adding a Custom Window to an Addin
« on: October 22, 2017, 10:24:43 pm »
Hi All

I am attempting to add a custom window to an addin and have used Sparx's rather brief notes

http://sparxsystems.com/enterprise_architect_user_guide/13.5/automation/custom_docked_window.html

However the addiin window is not added:

I am using Visual Studio 2013 and C# .Net 4.5

My Code is as below:

The very simple Test Addin:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestAddIn
{
    public class TestAddIn
    {
        // ----------------------------------------------------------------------
        // Menu Strings
        // ----------------------------------------------------------------------

        private String menuHeader = null;
        private String[] menuOptions = { null };

        private bool safeDeleteIsEnabled;
        private MyCustomControl myCustomControl;       
 
        //-----------------------------------------------------------------------
        // Constructor
        //-----------------------------------------------------------------------

        public TestAddIn()
        {
            safeDeleteIsEnabled = true;
            menuHeader = "-&Tester";
            menuOptions = new String[] { "&Checked", "-", "&Unchecked", "-", "&Show Window", "&Hide Window"};

        }

        // Enterprise Architect standard extension methods

        public void EA_Connect(EA.Repository repository)
        {

            myCustomControl = (MyCustomControl)repository.AddWindow
                     ("Test Addin", "TestAddIn.MyCustomControl");
            if (myCustomControl == null)
            {
                MessageBox.Show("Test AaddIn Custom Window not added");
            }
            else
            {
                MessageBox.Show("Test AddIn Custom Window added);
         }
        }

        public void EA_Disconnect()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public object EA_GetMenuItems(EA.Repository repository,
                                      string menuLocation,
                                      string menuName)
        {

            if (menuName == String.Empty)
            {
                //return top level menu option
                return menuHeader;
            }
            if (menuName == this.menuHeader)
            {
                // return submenu options
                return menuOptions;
            }
            else
            {
                return string.Empty;
            }
        }

        public void EA_GetMenuState(EA.Repository repository,
                                    string menuLocation,
                                    string menuName,
                                    string itemName,
                                    ref bool isEnabled,
                                    ref bool isChecked)
        {
            switch (menuLocation)
            {
                case "MainMenu":
                    switch (itemName)
                    {
                        case "&Checked":
                            isEnabled = true;
                            isChecked = safeDeleteIsEnabled;
                            break;
                        case "&Unchecked":
                            isEnabled = true;
                            isChecked = !safeDeleteIsEnabled;
                            break;
                    }
                    break;
            }
        }

        public void EA_MenuClick(EA.Repository repository,
                                    string menuLocation,
                                    string menuName,
                                    string itemName)
        {
            switch (itemName)
            {
                case "&Checked":
                    safeDeleteIsEnabled = !safeDeleteIsEnabled;
                    break;
                case "&Unchecked":
                    safeDeleteIsEnabled = !safeDeleteIsEnabled;
                    break;
            }
        }
    }

}

The addin window has been added to the solutions as a Visual Studio Custom Control and the Code is:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestAddIn
{
    public partial class MyCustomControl : Control
    {
        public MyCustomControl()
        {
            InitializeComponent();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }
    }
}

Everything builds fine and the addin works fine, but the reference nyCustomControl is always null thus indicating the Custom Window has not not been added.

Using the Extend | Manage | Add-in Windows returns No loaded add-ins are using this window

I must be missing something, but what?

All namespaces and names are correct.s

One curious thing, the article by Sparx on Custom Windows, mentions an ActiveX Custom Control and and OCX file. I do not see any OCX file in my build, so if this is what is missing, how is such an OCX file created, and where should it be located.

Any help or advice will be most appreciated.

Phil
follow me on Twitter

@SparxEAGuru

EXploringEA

  • EA User
  • **
  • Posts: 172
  • Karma: +8/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #1 on: October 23, 2017, 06:30:38 am »
Hi Phil

A quick thought - you have said that you have registered the class, have you?
User guide says "Once the custom control has been created and registered on the target system"

EA will look for a class with the name (in your example) "TestAddIn.MyCustomControl" in the registry.
Use RegEdit to check that there is an entry under HKEY_CLASSES_ROOT

I've forgotten to do it before now.

Adrian



« Last Edit: October 23, 2017, 07:29:50 am by MrWappy »
EXploringEA - information, utilities and addins

Helmut Ortmann

  • EA User
  • **
  • Posts: 970
  • Karma: +42/-1
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #2 on: October 23, 2017, 06:43:05 am »
Hi,

To use a custom window you have to develop a DLL for each Custom Window according to the Microsoft COM model. Then you have to make sure that this DLL is correctly registered for COM. If this is done correctly adding it within your AddIn to EA is a simple call. I have seen this call in your code.

I usually start with a running solution and adapt it to my needs. This approach works for me.

The first time I used Geert Bellekens Add-Ins to get familiar. Over time I developed my own little Add-In which I usually use to start a new project. You can find it together with a lot of documented experiences at https://github.com/Helmut-Ortmann/EnterpriseArchitect_hoTools/wiki/AddIn. It's part of the free tool hoTools. To see adding Custom Windows, you have to look into hoTools or similar AddIns.

Of course, there are a lot of other useful AddIns to start with. Geert is always an excellent address to search for.

To summarize:
The rules for a Custom Window are the same as for the primary Addin. You have to develop a dll and register it as COM object. The difference is:
- The Custom Window contains a GUI (FORM or WPF)
- You make a Call to register the registered DLL to EA from your basic AddIn.

I admit I've invested a lot of time for my first running AddIn with a Custom Window. Most issues are around correctly registering it as a COM object. Make sure that there are no Exceptions in Constructors (not easy to find).

Kind regards,

Helmut

Coaching, Training, Workshop (Addins: hoTools, Search&Replace, LineStyle)

EXploringEA

  • EA User
  • **
  • Posts: 172
  • Karma: +8/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #3 on: October 23, 2017, 07:55:39 pm »
@Helmut - in view of your response I needed to check as in my experience I have not needed to have the custom window code in a separate DLL (unless it made sense for the design), as the AddIn is already a COM DLL, but did a simple check this morning in case things have changed!

@Phil - this is what I did to double check:

  • Created a simple user control, called "UC1", which was in my main AddIn DLL (Assembly "testAddIn") - MUST be a public class
  • Added code - see below

I added a simple menu item to show the window in the menu click (in VB.net):

Case "Show window"
 Dim myNewAddInWindow = Repository.AddWindow("My tab Label", "testAddIn.UC1")
 Repository.ShowAddinWindow("testAddIn.UC1")


Register the new class (VS doesn't do this automatically), so I re-registered the AddIn, running the batch file below.  I could then see a new class entry for "testAddIn.UC1"
When deployed the installer will handle the registration.

For testing I have an existing batch file to do the registration with the single line which runs RegAsm for the required .NET framework for my AddIn DLL.  So just re-running this did the job both the AddIn and User Control are in the same DLL.

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe   "./bin/x86/Debug/testAddIn.dll" /codebase

Note this is for .NET3.5, for other .NET versions use the appropriate RegAsm app e.g. for .NET4 use the app to register 

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe

In the case that you develop the code in a separate DLL then clearly Helmut's advice applies.

Hope this helps, but any specifics do ask

Adrian
EXploringEA - information, utilities and addins

philchudley

  • EA User
  • **
  • Posts: 740
  • Karma: +20/-0
  • UML/EA Principal Consultant / Trainer
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #4 on: October 24, 2017, 08:30:26 pm »
Thanks for all the help guys

I have now got the Add In window working perfectly

Cheers

Phil
follow me on Twitter

@SparxEAGuru

priombiswas89

  • EA User
  • **
  • Posts: 47
  • Karma: +0/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #5 on: December 03, 2020, 09:39:06 am »
Created a UserControl in a separate project, build it
Registered it in the COM assembly using the commands:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe   "C:\Users\priom\source\repos\PackageListGrid\bin\Debug\PackageListGrid.dll" \tlb \codebase
After registering, I can see the library in the registry of windows and also in visual studio COM
But whenever I try to reference this UserControl in my addin, I am getting the following error:

C:\Users\priom\source\repos\PackageListGrid\bin\Debug\PackageListGrid.tlb was exported from a .NET assembly and cannot be added as a reference.

I am actually confused whether the way I registered the dll is correct.

Geert Bellekens

  • EA Guru
  • *****
  • Posts: 13287
  • Karma: +557/-33
  • Make EA work for YOU!
    • View Profile
    • Enterprise Architect Consultant and Value Added Reseller
Re: Trouble adding a Custom Window to an Addin
« Reply #6 on: December 03, 2020, 05:47:07 pm »
I think you are referencing the assembly the wrong way in your visual studio project.

You can either include the project in your solution, and reference it like that, or include the dll itself.

Geert


priombiswas89

  • EA User
  • **
  • Posts: 47
  • Karma: +0/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #7 on: December 03, 2020, 08:58:15 pm »
Below is the code for my addin:
Code: [Select]
UserControl1 userControl1;
        private List<string> packageNames = new List<string>();
        public void EA_Connect(EA.Repository Rep)
        {
            //listControl = (PackageListView)Rep.AddWindow("Window1", "MyAddin2.UserControl");
            userControl1 = (UserControl1)Rep.AddWindow("Window1", "PackageListGrid.UserControl");
        }

        public object EA_GetMenuItems(EA.Repository Repository, string Location, string MenuName)
        {
            if (MenuName == "")
                return "-&My Addin 3";
            else
            {
                String[] ret = { "Show Window", "Show Button" };
                return ret;
            }

        }

        private void PrintPackage(EA.Package package)
        {
            EA.Collection packages = package.Packages;
            for (short ip = 0; ip < packages.Count; ip++)
            {
                EA.Package child = (EA.Package)packages.GetAt(ip);
                PrintPackage(child);
                packageNames.Add(child.Name);
            }
            //Form1 anAbout = new Form1();
            //anAbout.ShowDialog();
        }


        public void EA_MenuClick(EA.Repository Rep, string Location, string MenuName, string ItemName)
        {
            if (ItemName == "Show Window")
            {
                EA.Collection packages = Rep.Models;
                for (short ip = 0; ip < packages.Count; ip++)
                {
                    EA.Package child = (EA.Package)packages.GetAt(ip);
                    PrintPackage(child);
                }
                Rep.ShowAddinWindow("Window1");

            }

            else if (ItemName == "Show Button")
            {
                Rep.ShowAddinWindow("Window1");
            }
        }
        public void EA_Disconnect()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

PackageListGrid is a separate project where there is a UserControl which I built and registered in the registry of windows. Now I have added the .dll from debug folder of the project and referenced it to MyAddin3 project. Still it isn't working. Below is the code for my PackageListGrid control:
Code: [Select]
namespace PackageListGrid
{
    [ProgId("PackageListGrid.UserControl1")]
    [ClassInterface(ClassInterfaceType.AutoDual), ComSourceInterfaces(typeof(UserControlEvents))]
    public partial class UserControl1 : UserControl
    {
        public const String EventsId = "f87e75de-6a2f-4603-94bb-1530f20366c5";

        public UserControl1()
        {
            InitializeComponent();
        }

        // register COM ActiveX object
        [ComRegisterFunction()]
        public static void RegisterClass(string key)
        {
            StringBuilder skey = new StringBuilder(key);
            skey.Replace(@"HKEY_CLASSES_ROOT\", "");

            // récupérer GUID depuis ProgID="CsharpWindowsActiveX.ActiveXUserControl"
            Type myType1 = Type.GetTypeFromProgID("PackageListGrid.UserControl1");
            Console.WriteLine("ProgID=PackageListGrid.UserControl1 GUID={0}.", myType1.GUID);

            // ecrire GUID dans un fichier txt
            TextWriter tw;
            tw = File.CreateText("guid.txt");
            tw.WriteLine(skey.ToString());
            tw.WriteLine(myType1.GUID.ToString());
            tw.Close();

            RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(skey.ToString(), true);
            RegistryKey ctrl = regKey.CreateSubKey("Control");
            ctrl.Close();
            RegistryKey inprocServer32 = regKey.OpenSubKey("InprocServer32", true);
            inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
            inprocServer32.Close();
            regKey.Close();
        }


        // Unregister COM ActiveX object
        [ComUnregisterFunction()]
        public static void UnregisterClass(string key)
        {
            StringBuilder skey = new StringBuilder(key);
            skey.Replace(@"HKEY_CLASSES_ROOT\", "");
            RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(skey.ToString(), true);
            regKey.DeleteSubKey("Control", false);
            RegistryKey inprocServer32 = regKey.OpenSubKey("InprocServer32", true);
            regKey.DeleteSubKey("CodeBase", false);
            regKey.Close();
        }


        // ActiveX properties (Get/Set) //////////////////////////////////////////////////////////////////
        private int ptextVal;

        public int TextVal
        {
            get
            {
                ptextVal = (int)(numericUpDown1.Value);
                return ptextVal;
            }
            set
            {
                ptextVal = value;
                numericUpDown1.Value = ptextVal;
            }
        }

        // ActiveX methods/functions //////////////////////////////////////////////////////////////////
        public interface ICOMCallable
        {
            int GetTextBoxValue();
        }

        public int GetTextBoxValue()
        {
            int i = (int)(numericUpDown1.Value);
            MessageBox.Show("ActiveX method: GetTextBoxValue " + i.ToString());
            return (i);
        }


        // Eventhandler interface  //////////////////////////////////////////////////////////////////
        public delegate void ControlEventHandler(int NumVal);

        [Guid("14801B11-2293-4CA5-A40F-0406D0E0E461")]
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        public interface UserControlEvents
        {
            [DispId(0x60020001)]
            void OnButtonClick(int NumVal);
        }

        public event ControlEventHandler OnButtonClick;


        private void buttonOK_Click(object sender, EventArgs e)
        {
            int NumVal;

            if (OnButtonClick != null)
            {
                NumVal = (int)(numericUpDown1.Value);
                OnButtonClick(NumVal);
            }
        }

        //private void ActiveXUserControl_Load(object sender, EventArgs e)
        //{

        //}

        private void UserControl1_Load(object sender, EventArgs e)
        {

        }
    }
}

Geert Bellekens

  • EA Guru
  • *****
  • Posts: 13287
  • Karma: +557/-33
  • Make EA work for YOU!
    • View Profile
    • Enterprise Architect Consultant and Value Added Reseller
Re: Trouble adding a Custom Window to an Addin
« Reply #8 on: December 03, 2020, 09:50:23 pm »
Wow, you are doing all kind of weird COM registration stuff there.
I never needed to do any of that.

Geert

priombiswas89

  • EA User
  • **
  • Posts: 47
  • Karma: +0/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #9 on: December 03, 2020, 10:05:38 pm »
Wow, you are doing all kind of weird COM registration stuff there.
I never needed to do any of that.

Geert

I have changed my PackageListGrid control code to the following, according to your MyAddin solution below:
Code: [Select]
[Guid("FE28DE56-79C0-4704-B792-C7AD80321803")]
    [ComVisible(true)]
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }
        private void UserControl1_Load(object sender, EventArgs e)
        {

        }
    }

Unregistered the control and re-registered it again. Then added the dll to MyAddin3 project again. Still I am not able to see the control in addin window using ShowAddinWindow method.

Geert Bellekens

  • EA Guru
  • *****
  • Posts: 13287
  • Karma: +557/-33
  • Make EA work for YOU!
    • View Profile
    • Enterprise Architect Consultant and Value Added Reseller
Re: Trouble adding a Custom Window to an Addin
« Reply #10 on: December 03, 2020, 10:48:49 pm »
So what happens when debugging? Is your usercontrol1 field properly filled?

Also see the option Specialize | Addins | Windows in EA.

Geert

priombiswas89

  • EA User
  • **
  • Posts: 47
  • Karma: +0/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #11 on: December 03, 2020, 10:55:07 pm »
So what happens when debugging? Is your usercontrol1 field properly filled?

Also see the option Specialize | Addins | Windows in EA.

Geert

My UserControl1 has a numericdropdown and a button, and I have also tried specialize|addins|windows to check whether they are loading or not, but so far, no luck.
Also I have one question, do I need to re-reference the PackageListView.dll in MyAddins3 every time I modify the PackageListView project?

Geert Bellekens

  • EA Guru
  • *****
  • Posts: 13287
  • Karma: +557/-33
  • Make EA work for YOU!
    • View Profile
    • Enterprise Architect Consultant and Value Added Reseller
Re: Trouble adding a Custom Window to an Addin
« Reply #12 on: December 03, 2020, 11:15:51 pm »
I wasn't asking for that. When you debug your code, is the field userControl1 filled in properly? (in other words, did the call to AddWindow() succeed)

You shouldn't need to re-reference, as long as the dll you are referencing is actually updated
In my experience it's easier to refence the project instead of the dll, but you might have your reasons for a dll reference.

Geert

priombiswas89

  • EA User
  • **
  • Posts: 47
  • Karma: +0/-0
    • View Profile
Re: Trouble adding a Custom Window to an Addin
« Reply #13 on: December 03, 2020, 11:18:41 pm »
I wasn't asking for that. When you debug your code, is the field userControl1 filled in properly? (in other words, did the call to AddWindow() succeed)

You shouldn't need to re-reference, as long as the dll you are referencing is actually updated
In my experience it's easier to refence the project instead of the dll, but you might have your reasons for a dll reference.

Geert

Ok, I am getting a null UserControl after I debugged the code, right now I am trying the following code:
Code: [Select]

UserControl1 userControl1;
        public void EA_Connect(EA.Repository Rep)
        {
            //listControl = (PackageListView)Rep.AddWindow("Window1", "MyAddin2.UserControl");
            userControl1 = (UserControl1)Rep.AddWindow("Window1", "PackageListGrid.UserControl") as UserControl1;
        }

public void EA_MenuClick(EA.Repository Rep, string Location, string MenuName, string ItemName)
        {
            if (userControl1 == null)
            {
                userControl1 = (UserControl1)Rep.AddWindow("Window1", "PackageListGrid.UserControl");
            }
           
            if (ItemName == "Show Window")
            {
                EA.Collection packages = Rep.Models;
                for (short ip = 0; ip < packages.Count; ip++)
                {
                    EA.Package child = (EA.Package)packages.GetAt(ip);
                    PrintPackage(child);
                }
                Rep.ShowAddinWindow("Window1");

            }

            else if (ItemName == "Show Button")
            {
                userControl1.changeLabelText("Test label text");
                Rep.ShowAddinWindow("Window1");
            }
        }

That means, somehow AddWindow is not loading the UserControl

Geert Bellekens

  • EA Guru
  • *****
  • Posts: 13287
  • Karma: +557/-33
  • Make EA work for YOU!
    • View Profile
    • Enterprise Architect Consultant and Value Added Reseller
Re: Trouble adding a Custom Window to an Addin
« Reply #14 on: December 03, 2020, 11:24:41 pm »
You reference "PackageListGrid.UserControl"

But your class is called UserControl1

What EA does is use that string to find the COM reference in the registry.
The registry will then tell EA where to find the dll to use to load this class.
Since this string is not found in the registry, EA can't create your control.

Geert