What's new

Adventures in V24 Add-on Development

bolsover

Senior Member
OK... So I think I've solved the problem.
I've added an event (Terminate) to the AddOnCommand. This is invoked on completion of the OnTerminate routine. The event args are simply a reference to the AddOnCommand.

In the IAlibreAddOn implementation, I add a listener for these events when the AddOnCommand is created:

C#:
alibreDataViewerAddOnCommand.Terminate+= AlibreDataViewerAddOnCommandOnTerminate;

The new AlibreDataViewerAddOnCommandOnTerminate method simply clears out the dead reference from the Dictionary:

C#:
private void AlibreDataViewerAddOnCommandOnTerminate(object sender, AlibreDataViewerAddOnCommandTerminateEventArgs e)
        {
            AlibreDataViewerAddOnCommand alibreDataViewerAddOnCommand;
            if (dataViewerAddOnCommands.TryGetValue(e.alibreDataViewerAddOnCommand.session.Identifier, out alibreDataViewerAddOnCommand))
            {
                dataViewerAddOnCommands.Remove(e.alibreDataViewerAddOnCommand.session.Identifier);
            }
        }

So far as I can establish this now correctly closes the AddOnCommand irrespective of whether the command is active or not when the file is closed.

Compiled copy attached.

I'll post the code to my GitHub next..
 

Attachments

  • UtilitiesForAlibre1.0.0.4.zip
    830.8 KB · Views: 2

simonb65

Alibre Super User
Whilst the above works, the issue now is that closing a part design without first closing the property view leaves a reference to the closed design in the Dictionary.
If I stick with the code I have now, I need to find a good way of clearing unused objects from the Dictionary.
Pass a reference to the dictionary 'dataViewerAddOnCommands' in the AlibreDataViewerAddOnCommand constructor, then the command can clean itself up the dictionary in OnTerminate() during the close ... or pass in a reference to the IAlibreAddOn and make the dictionary public so you can reference it in OnTerminate() directly.

In your UserRequestedClose(), just change it to ...

C#:
public void UserRequestedClose()
        {
            if (CommandSite != null)
                CommandSite.Terminate();
        }

OnTerminate() will then get called and do all your clean up in one place.
 

bolsover

Senior Member
Hi Simon

I'll certainly try out your suggestion of passing the Dictionary into the AddOnCommand; seems like a good clean solution.

I'd actually approached differently by adding a Treminate event to the AddOnCommand whcih is invoked as part of the OnTerminate() method.
The IAlibreAddOn adds a listener for these event when a new AddOnCommand is instantiated. The listener in turn calls a routine that does the Dictionary clean up.

TBH, I think yours is the cleaner solution but mine keeps all the Dictionary related activity in one place.

I'm in the process of cleaning up all the other code in the project; trying to make it more understandable by putting individual tools and associated classes in their own namespace. I'm a bit new to C# so keep making poor choices for variable and field names.. But I'm learning new stuff which is always good.

I think I'll add an empty shell of an AddOnCommand that just creates a tool panel with a dummy UserControl. Will be helpful as/when I decide to add more utilities or for others to create their own.

Back to the day job now.. Writing a custom tool (to be used by client) for transferring a PervasiveSQL database to SQLServer.

David
 

bolsover

Senior Member
Update on project

I started this thread partly to document my progress as I was starting to improve my C# skills (long way to go yet!) but also in an effort to help others overcome what looked like a steep learning curve for Alibre AddOn development by sharing what I discovered.

Here are a few notes on what I have found so far.. Just what I think important.

1 The Alibre documentation is not good! It does not provide any detail of many important interfaces that need to be implemented in order to develop a properly integrated AddOn.

2 Knowledge of the following files and interfaces is essential:

AddOn.adc. (see my UtilitiesForAlibre.adc)
This is an XML text file. The Alibre documentation appears to be correct unless you want to develop using C# in which case, you MUST add the directive type="Managed" in the DLL section.

AlibreAddOn.cs (see my AlibreAddOn.cs)
This class is the main link between Alibre and your custom AddOn. It MUST have the class name "AlibreAddOn" and MUST be in the namespace "AlibreAddOnAssembly". To date, I have only implemented the AddOnLoad method where I initialise my custom implementation of IAlibreAddOn. The AlibreAddOn also requires a method (not documented by Alibre) "public static IAlibreAddOn GetAddOnInterface()". This method should return your custom implementation of IAlibreAddOn (in my code this is the UtilitiesForAlibre class.

IAlibreAddOn (see my UtilitiesForAlibre)
This interface is where you add menus to your AddOn that will show up in the Alibre menu and tool bar. You will need to implement this interface (see my implementation UtilitiesForAlibre).
Launching of your UserControl is done in the public IAlibreAddOnCommand InvokeCommand(int menuID, string sessionIdentifier) method.
If like me you want several menu items, just use a switch statement to call routines that launch your UserControl in response to an individual menu.
There are a few traps to be aware of when launching a new UserControl. These all hinge around the need to clean up when you are done with a control and not to launch a control twice in the same Alibre design window. There are of course multiple ways this can be achieved but I chose to have each menu open/close he relevant control. To keep track of opened controls, I use a Dictionary<string, IAddOnCommand> for each control. When a control is launched, entry is added to the dictionary with the string being the current session identifier and the IAddOnCommand being the name of your implementation of IAddOnCommand for that control. When a control is closed, the entry is removed from the Dictionary. There is also a need to take care of the situation of the Alibre design window being closed while the control is still open. I took care of this by adding an event to my implementation of IAddOnCommand which is called in the IAddOnCommand.OnTerminate() method. The listener for this event is in IAlibreAddOn and simply removes and entry from the Dictionary.

IAlibreAddOnCommand (see my EmptyAddOnCommand for a simple example)
An implementation of this interface allows you to get information from the Alibre Design window. So, if you want your control to 'know' when you select a plane or sketch or what button you have clicked this is where you do it. Your implementation of this interface is also responsible for instantiating your UserControl and adding it to a panel in the Alibre Design window. There are a few methods here where some care is needed.

Constructor
C#:
public EmptyAddOnCommand(IADSession session)
        {
            this.session = session; // a reference to the current design session
            PanelPosition = (int) ADDockStyle.AD_RIGHT; // where do you want the docked panel
            EmptyUserControl = new EmptyUserControl(session);  // finally get to create your user control
        }


DockedPanelHandle
C#:
 /// <summary>
        /// Get/Set the PanelHandle
        /// set adds the User control to the Parent, docks and autosizes.
        /// </summary>
        public virtual long DockedPanelHandle
        {
            get => PanelHandle;
            set
            {
                Debug.WriteLine(value);
                if (value != (long) IntPtr.Zero)
                {
                    var control = Control.FromHandle((IntPtr) value);
                    if (control != null)
                    {
                        EmptyUserControl.Parent = control;
                        EmptyUserControl.Dock = DockStyle.Fill;
                        EmptyUserControl.AutoSize = true;
                        EmptyUserControl.Show();
                        control.Show();
                        PanelHandle = value;
                    }
                }
            }
        }


Terminate event - invoked in OnTerminate()
C#:
 public event EventHandler<EmptyAddonCommandTerminateEventArgs> Terminate;


OnTerminate.. do some clean up
C#:
/// <summary>
/// Called when Alibre terminates the add-on command; add-on should make sure to release all references to its CommandSite
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void OnTerminate()
{
    Debug.WriteLine("OnTerminate");
    WriteToUserControl("OnTerminate");
    if (EmptyUserControl != null) EmptyUserControl.Dispose();
    if (CommandSite != null)
    {
        CommandSite.RemoveDockedPanel(DockedPanelHandle);
        DockedPanelHandle = (long) IntPtr.Zero;
        CommandSite = null;
    }
    EmptyAddonCommandTerminateEventArgs args = new EmptyAddonCommandTerminateEventArgs(this);
    Terminate.Invoke(this, args);
}

OnComplete() This is where the DockedPanelHandle IntPtr is generated
C#:
/// <summary>
        /// Called when Alibre has successfully initiated this command; gives it a chance to perform any initializations
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public void OnComplete()
        {
            Debug.WriteLine("OnComplete Starting");
            WriteToUserControl("OnComplete Starting");
            try
            {
                DockedPanelHandle = CommandSite.AddDockedPanel(PanelPosition, "Empty Add On");
            }
            catch (Exception ex)
            {
                var num = (int) MessageBox.Show(ex.ToString(), Application.ProductName, MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);
                throw ex;
            }
            Debug.WriteLine("OnComplete Done");
            WriteToUserControl("OnComplete Done");
        }


UserRequestedClose() More clean up code...
C#:
/// <summary>
        /// Actions to take when closing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void UserRequestedClose()
        {
            EmptyUserControl.Dispose();
            CommandSite.RemoveDockedPanel(DockedPanelHandle);
            DockedPanelHandle = (long) IntPtr.Zero;
            CommandSite = null;
        }

Sample code

Copies of source code is available on GitHub here: UtilitiesForAlibre
I use Jetbrains Rider but Visual Studio should wok OK if you want to work with my code.
As of writing, the code is at version 1.0.0.6
I would like to thank all Alibre forum members for their encouragement and particularly simonb65 for helpful hints and tips.

A compiled copy of my AddOn is attached. If you want to try out, you need to extract the files into directory Alibre Design/Program/Addons/UtilitiesForAlibre
Directory.jpg

That's all for now but I'm sure to make changes - and will accept all (constructive) criticism and suggestions for new features.

David
 

Attachments

  • UtilitiesForAlibre1.0.0.6.zip
    841.6 KB · Views: 5

Cator

Senior Member
Hi David,
I just installed and tried your work and really something fantastic! I'm not a programmer but I really hope to be able to open your work to be able to implement some of my little ideas......
I thank you for the time you invested for this and I really hope to be able to read through the many information you shared!
Regards
Francesco
 

Cator

Senior Member
David,
today I got to try your Data Browser utility at work. It works fine but in the drawing sheet workspace it doesn't work ... nothing serious but here I wanted to inform you knowing that you wanted constructive opinions and criticisms!
Regards
Francesco
 

bolsover

Senior Member
David,
today I got to try your Data Browser utility at work. It works fine but in the drawing sheet workspace it doesn't work ... nothing serious but here I wanted to inform you knowing that you wanted constructive opinions and criticisms!
Regards
Francesco
Hi Francesco.
Thanks for the feedback. I had disabled all the menu items in the Drawing workspace but the Data Browser could be enabled...

C#:
//Line 159 of UtilitiesForAlibre..
case SUBMENU_ID_DATA_BROWSER: return ADDONMenuStates.ADDON_MENU_GRAYED;

//Could be changed to:
case SUBMENU_ID_DATA_BROWSER: return ADDONMenuStates.ADDON_MENU_ENABLED;

I'm thinking about adding another tool..
I always find 3D sketches difficult. In particular constructing 3D lines seems a real pain.
My general idea is to have a table of X,Y,Z coordinates that represent nodes on a line.
All the user has to do is to add lines to the table; Alibre adds lines between the nodes.
Just an idea at present though.
 

Cator

Senior Member
David I think I have guessed the possibilities of the 3d sketch generator and I think I saw something similar in the add on alibre point developed some time ago .... I ask you a question that goes beyond ... How does a script compiled in C # become what you shared? It will seem like a naïve request but a video where you take your code and turn it into add on would be of great help ... I hope I can have the right indications for try to enter this complex world and thank you in advance if you can!
 

NateLiquidGravity

Alibre Super User
I'm thinking about adding another tool..
I always find 3D sketches difficult. In particular constructing 3D lines seems a real pain
If you could clamp the user input to an imaginary plane that is always parallel with the screen to a depth of the previous (or first) click I think it would be much more intuitive.
 

simonb65

Alibre Super User
If you could clamp the user input to an imaginary plane that is always parallel with the screen to a depth of the previous (or first) click I think it would be much more intuitive.
+1. I hate doing 3D sketches with a passion. Nothing worse than moving a node to find its also moved miles into the screen! As you say, the XY Screen Co-ordinate move of a node should be translated into the appropriate model world XYZ but maintain the same distance from the viewport to the node (i.e. your imaginary plane).
 

HaroldL

Alibre Super User
I hate doing 3D sketches with a passion.
The few times I have used 3D sketches it was for sweeps. Just as a point of reference, I found it much easier to create 3d sketches in SolidWorks than Alibre. (I say "found" because I no longer have access to SW since I retired.) There's something about it in Alibre that just doesn't seem quite right.

I have resorted to creating a solid model that I can convert edges from, then suppress, and I am left with the 3D sketch to use as the sweep path.
 

bolsover

Senior Member
Plans to write a simple 3DLine generator have hit a small snag...
According to the AlibreX API docs... "
Sketching
3D sketching is not yet supported in the API."

Despite this, I have been able to create a 3DSketch and add one line (haven't tried adding more). The main issue is that the IAD3DSketch.BeginChange() does not put the 3DSketch into edit mode. So whilst it 'may' be possible to generate sketch lines form input data, I have not yet found a way to do this whilst a 3D sketch is active.

Need to explore the API some more..

David
 

bolsover

Senior Member
David I think I have guessed the possibilities of the 3d sketch generator and I think I saw something similar in the add on alibre point developed some time ago .... I ask you a question that goes beyond ... How does a script compiled in C # become what you shared? It will seem like a naïve request but a video where you take your code and turn it into add on would be of great help ... I hope I can have the right indications for try to enter this complex world and thank you in advance if you can!
Hi Francesco
Making a video is rather out of my comfort zone but I'll happily guide you through the process.

I'm fortunate enough to have a licence for Jetbrains Rider but it is possible to compile the sources using Visual Studio Community edition. So your first job is to obtain that from https://visualstudio.microsoft.com/vs/community/ When installing, you'll need to select the option for .NET desktop development.

Once you have a copy of Visual Studio installed, grab a copy of my source code from https://github.com/bolsover/UtilitiesForAlibre The easiest way is to follow the green 'Code' button and download a .zip of the codebase. Just unzip somewhere on your PC.

From Visual Studio, you need to open the UtilitiesForAlibre.sln file in the unzipped code. This should load the project into Visual Studio.
With the project open in Visual Studio, you can edit the code. However, before you can build the code, you will have to make sure all the required references are satisfied. If you open up the Solution Explorer, References node, you will probably see some are broken. This is because the required AlibreX, AlibreAddOn and other references are NOT included in the .zip. You will need to fix any broken references before proceeding.

If/when you get this far and still have problems, let me know and I'll try to help.
 

Cator

Senior Member
Hi Francesco
Making a video is rather out of my comfort zone but I'll happily guide you through the process.

I'm fortunate enough to have a licence for Jetbrains Rider but it is possible to compile the sources using Visual Studio Community edition. So your first job is to obtain that from https://visualstudio.microsoft.com/vs/community/ When installing, you'll need to select the option for .NET desktop development.

Once you have a copy of Visual Studio installed, grab a copy of my source code from https://github.com/bolsover/UtilitiesForAlibre The easiest way is to follow the green 'Code' button and download a .zip of the codebase. Just unzip somewhere on your PC.

From Visual Studio, you need to open the UtilitiesForAlibre.sln file in the unzipped code. This should load the project into Visual Studio.
With the project open in Visual Studio, you can edit the code. However, before you can build the code, you will have to make sure all the required references are satisfied. If you open up the Solution Explorer, References node, you will probably see some are broken. This is because the required AlibreX, AlibreAddOn and other references are NOT included in the .zip. You will need to fix any broken references before proceeding.

If/when you get this far and still have problems, let me know and I'll try to help.
Thanks David,
I will try to follow your directions. You are really very helpful and I thank you!
Regards
Francesco
 

bolsover

Senior Member
I've been doing some more testing of 3DSketching...
So far, I have a tool that will allow entry of X,Y,Z coordinates and will then generate a series of line segments between each point.
I also added the facility to import XYZ data from Excel.
With a bit of fiddling in excel, I generated a series of points representing a spiral.
OK - so I know I can generate spirals with Alibre tools - but this is way more fun.
I'll share the code soon - when I've fixed a few bugs!
David
spiral.jpg
 

DavidJ

Administrator
Staff member
David,

I finally got around to installing your Utilities (1.2.0.0) - tried the Data Browser, some folders won't expand. In one (where I hold a lot of my Alibre experiments and tests) I got this error when attempting to expand the folder

Unknown or unsupported file format

Code:
   at com.alibre.automation.ExceptionMap.handleException(Exception inputException)
   at com.alibre.automation.AlibreRoot.OpenFileEx(String filePath, Boolean openEditor)
   at Bolsover.DataBrowser.AlibreConnector.RetrieveSessionForFile(AlibreFileSystem alibreFileSystem) in D:\Repository\Jetbrains\Bolsover\UtilitiesForAlibre\UtilitiesForAlibre\DataBrowser\AlibreConnector.cs:line 51
   at Bolsover.DataBrowser.AlibreFileSystem.RetrieveAlibreData() in D:\Repository\Jetbrains\Bolsover\UtilitiesForAlibre\UtilitiesForAlibre\DataBrowser\AlibreFileSystem.cs:line 151
   at Bolsover.DataBrowser.AlibreFileSystem.GetFileSystemInfos() in D:\Repository\Jetbrains\Bolsover\UtilitiesForAlibre\UtilitiesForAlibre\DataBrowser\AlibreFileSystem.cs:line 360
   at Bolsover.DataBrowser.DataBrowserForm.<setupTree>b__13_1(Object rowObject) in D:\Repository\Jetbrains\Bolsover\UtilitiesForAlibre\UtilitiesForAlibre\DataBrowser\DataBrowser.cs:line 86
   at BrightIdeasSoftware.TreeListView.Branch.FetchChildren() in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 2099
   at BrightIdeasSoftware.TreeListView.Branch.Expand() in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 2054
   at BrightIdeasSoftware.TreeListView.Tree.Expand(Object model) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 1374
   at BrightIdeasSoftware.TreeListView.Expand(Object model) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 616
   at BrightIdeasSoftware.TreeListView.ToggleExpansion(Object model) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 876
   at BrightIdeasSoftware.TreeListView.ProcessLButtonDown(OlvListViewHitTestInfo hti) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\TreeListView.cs:line 975
   at BrightIdeasSoftware.ObjectListView.HandleLButtonDown(Message& m) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\ObjectListView.cs:line 6277
   at BrightIdeasSoftware.ObjectListView.WndProc(Message& m) in D:\ObjectListViewFull-2.9.1\ObjectListViewDemo\ObjectListView\ObjectListView.cs:line 5651
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Don't know where the D:\ comes from, that's a (currently empty) DVD drive.

Not at all urgent - I was just taking a look at what functions are available in your Utilities.
 
Top