Malpass Luke.-SolidWorks 2008 API - Programming and Automation (2011) PDF
Malpass Luke.-SolidWorks 2008 API - Programming and Automation (2011) PDF
Published by AngelSix
©2011 Luke Malpass contact@angelsix.com
Trademark Information
Their enthusiasm towards this project has been a key driving factor
in pushing me to do this. I just hope that I do not let any of them
down.
Introduction
When I was first introduced to computers at the tender age of 9, I
have always been intrigued how things worked. Not on a basic level
of being told that if you write this portion of code, this will happen,
but to know the reason for every line of code and its purpose. To
know the reason why X comes before Y, and to analyse it and find its
extremes, where it fails and to know its limitations.
I have tried to give the best explanation of all code provided on its
purpose, and what the point of every line of code is. I hope you enjoy
reading this book as much as I have enjoyed writing it.
http://www.angelsix.com/CODE/SW2008.zip
Table of Contents
The Basics .......................................................................11
Recording your first macro ................................ 12
Writing a macro from scratch ............................ 19
Connecting with Visual Studio Express ........................ 29
Download and Install Visual Studio Express ...... 30
Connect to SolidWorks in C#..............................31
Connect to SolidWorks in VB.Net ...................... 52
Starting SolidWorks Programming .............................. 59
Saving Drawing Sheets as DXF .......................... 60
Get Document Information ............................... 68
Displaying Document Information ..................... 77
Working with Selected Objects..................................... 89
Identifying Selected Objects ............................. 90
Mating Selected Objects ................................... 94
Setting Material of Selected Objects ................. 98
Manipulating Dimensions.................................101
Selecting Objects ............................................. 107
Setting a Selection Filter ..................................112
Property Manager Pages.............................................. 113
Deriving the base class .....................................114
Adding items to the Page ................................ 128
Responding to events ...................................... 144
Traversing ..................................................................... 153
Traversing through an Assembly ..................... 154
Traversing through a Component.................... 159
Displaying the results ...................................... 164
Playing with Components and Features ........... 171
Custom Property Manager ...........................................181
Acquiring a Custom Property Manager ........... 182
Adding Custom Properties .............................. 185
Deleting Custom Properties ............................ 188
Table of Contents
Check Custom Property Existence ...................190
Updating Custom Properties............................194
The ConfigSearcher program ..........................196
Working with Drawings ............................................... 207
Automatically create Drawing Sheet............... 208
Counting Views ................................................219
Printing Drawing Sheets .................................230
Add-ins ........................................................................ 249
The basics of an Add-in ....................................250
Removing Add-in entries ................................ 268
10
The Basics
11
The Basics
Start by opening SolidWorks, and before opening any file from the
menu select Tools->Macro->Record. Most of the actions you
perform will not be recorded by SolidWorks; it’s just to get started.
For now we are simply going to create a new part. Select File->New,
and then File->Save, and save the new empty part wherever you
want.
Now you are returned to SolidWorks like normal. In order to see, and
more importantly to edit and run the macro you just created, you
need to go to Tools->Macro->Edit... Then select the macro you just
saved. This will open up the Visual Basic for Applications
development environment, or for short the VBE.
The first thing you will see is a lot of code to the right, with a tree-
view panel to the left. If you are unfamiliar with the VBE, then just
press F1 within the VBE environment and take a look at the help
topics within there. Don’t get too concerned with that right now, all
12
The Basics
you need to know is that the panel on the left is where you will find
your macro’s files and the properties, and the remaining space to the
right is where your currently selected files content from the left
panel is displayed.
13
The Basics
Leave the VBE window open and go back to
SolidWorks. Close down all files and on the top file
menu, right-click and select Macro from the menu.
This will save us having to go through the Tools
menu all the time to access the Macro tools Run,
Edit, Record etc... You should now notice that the
Macros toolbar should
now be visible
somewhere on the
window.
Before we get into understanding and editing this macro, let’s first
get this macro to run, check it is working, and to see exactly what it
does.
With no files open, click the Play button on the macro toolbar. This
will play the selected macro instantly. Select the macro you just
created to run it.
Your very first lesson about using the built-in recording functionality
of SolidWorks starts here. Trying to run this macro generates the
following error:
If you click debug, it will take you to the coding in the macro and
highlight the error in yellow:
14
The Basics
OK, so we know this line is causing the error. Without getting too
involved for now, and to get you started, remove the lines so that
you end up with the code below:
Sub main()
Set swApp = Application.SldWorks
Now, click the Save button in the VBE environment to save the
changes and run the macro again (by going back to SolidWorks and
clicking the Run button or by clicking the Run button from within the
VBE environment).
This time running the macro should create a new part and save it. If
you still get errors double-check your code. Remember all code from
this book is found of the accompanying CD.
15
The Basics
But before we get to this line, you will notice that there are several
lines before this. Anything outside of the Sub Main() routine and not
inside another Sub or Function, is classed as a global variable.
16
The Basics
Dim is the name used in VBA to declare a variable, following that is
the variables name, then ‘As’, and then its type.
Above, the macro has declared two variables. Two variables of type
Object, which is a universal type that all other types come from;
these will be used in the main() routine.
Firstly, once the macro is run, it enters the main() routine after
declaring its global variables. The first line within the main function
is the line which creates a new instance of the SolidWorks program
through COM as we discussed previously:
17
The Basics
Don’t worry too much about the parameters until later; all will be
explained.
Finally, the macro calls a function of the newly created Part variable
called SaveAs2.
The first thing you will notice about this call unlike the previous, is
that this one is calling a function from the Part variable, whereas the
previous was from the SolidWorks object itself (swApp). The second
is that even though we are calling a function still, there are no
parentheses. In VBA, if a function is called without ()’s then it is not
returning a value. That is not to say that the function itself doesn’t
return a value, just that we are not making any use of it on this call.
And that is it; you have just created your first SolidWorks Macro!
18
The Basics
This time, from SolidWorks, select New from the Macro Toolbar and
save the macro to your desired location. This will automatically open
the VBE environment for you to start coding, as well as add some
lines of code, and behind the scenes add references for you as well.
To truly start from scratch, delete the code from the main window.
As well as the code, SolidWorks added a reference to itself, so I am
going to explain how to add this yourself (something you will need to
do later using Visual Studio).
19
The Basics
With a fresh clean empty macro ready to work with, we will begin by
re-creating the recorded macro, but do it the correct way this time.
Start by declaring the variables we had in the first macro, only this
time notice the types of the variables:
With this macro, we want to create a new part and save it, so we
need another variable of type ModelDoc2. I have also renamed the
variable to swPart for easier reading.
On to the actual main function where all code starts; press enter
twice to give your code some space from the variables, and then
type:
Sub main()
End Sub
20
The Basics
We begin a Sub or Function with exactly that, followed by its name
(in this case main), followed by parentheses and then within them
any parameters, and finally, to close the Sub/Function, we type End
Sub/Function respectively.
Within this sub is where we are going to start coding. Firstly, instead
of creating an instance of the SolidWorks application using the usual
recorded method, we are going to use a more portable method:
Let’s stop here and add a little error handling. The GetObject
function either returns an object of that class is, or nothing. If we test
the variable as to whether it is nothing or not, then we know if we
succeeded in getting a handle to a running SolidWorks application.
21
The Basics
All we have done here is test if swApp is Nothing, and if it is,
meaning we failed to get a handle to any running SolidWorks
applications, then display a message box error, and exit the sub
effectively quitting the macro.
22
The Basics
Now let’s add the line of code to create a new part. For simplicity at
this stage just type in the location of your required part template file;
we could create a drop down list with all available templates of the
currently installed templates, but that would just overwhelm the
exercise at this point.
And finally, let’s save this part to any location you select. Later on we
will create some user interaction that prompts the user for a save
location, but for now just type it in.
Start by searching the help file for “SaveAs2” like we saw in the first
macro. Double-clicking on ModelDoc2::SaveAs2 you will notice that
the help file states that this function is obsolete in red writing, so
click the suggested link of ModelDoc2::SaveAs3. This will again tell
you that this is obsolete. Keep clicking until you get to
ModelDocExtension::SaveAs. Now read the help to find out about
the parameters required.
23
The Basics
First, Name is declared as type BSTR, which is a string. This is again
the name and location of where to save the file, which we will type in
manually for now.
Now onto the Version parameter; the help states that this is of type
Long, which is a number, yet the document says it is a format of type
swSaveAsVersion_e. Let me explain; the type swSaveAsVersion_e
is called an enumerator, or enum for short. This is basically a
collection of numbers (Long’s) given names for ease of
understanding for the programmer. Instead of typing a number all
the time you can type a descriptive name. This enum, and all of the
others you will encounter for SolidWorks, is located in the
SolidWorks Constant library we referenced at the very beginning, so
all we need to do is access the constant library with SwConst name.
As you type you will notice the menu popping up showing you all
available properties and methods of the previous object. Drill down
until you get to the swSaveAsVersion_e, and you will see the
following options:
24
The Basics
swSaveAsOptions_Copy + swSaveAsOptions_Silent
Notice for the last two parameters Errors and Warnings, the help file
states they are outputs, not inputs. This means that whatever
variables we pass in here will be updated and set to new values after
the function has run, so we can check the values of these two
variables to understand what has happened and if there are any
errors or warnings.
25
The Basics
Finally, the help file states this function returns a Boolean value
(true/false) indicating whether the save succeeded or failed, so we
can do another check for failure, and if so, we could access the error
and warning variables for more information on the failure, but for
now, we will just accept a failure as a failure.
And that is it. Save your macro and test it. If you get any errors
review your code, or look at the complete macro on the
accompanying CD. Find the final listing below.
26
The Basics
Sub main()
27
The Basics
End If
End Sub
Hopefully you have learned quite a lot from this section. Although
we have not covered many calls or seemingly done much with
SolidWorks, we have covered a lot of technical ground that will
provide a solid foundation for the rest of the book.
You will find once you know these main foundations, the rest is
simply a case of calling the functions you would like, and through
trial and error, or research or the aid of others, you find what you are
after.
28
Connecting with Visual Studio Express
Connect to SolidWorks in C#
29
Connecting with Visual Studio Express
Go to http://www.microsoft.com/express/download/default.aspx and
simply download and install either C# Express and/or VB.Net
Express. All guides to installing them are on the site.
When you have installed the program, run it. You may be prompted
to select layout style or preferred language, just select any.
30
Connecting with Visual Studio Express
Connect to SolidWorks in C#
When you first open C# Express you are displayed with the following
screen:
This will then create your project and automatically create a blank
form for you. We are going to create a button that on clicking will
effectively initiate our “macro” as such.
To the left you will notice a toolbar icon. Click or hover over that
to expand the menu. Find the Button item and drag it onto your
form to create a new button. Once the button is on the form,
31
Connecting with Visual Studio Express
make sure it is selected. On your right will be the properties panel; in
here is where the current selected items properties will appear. For
our button we are going to change the text within it to “Start”. Scroll
down the properties until you find the property Text, and then type
in “Start” without the quotations and press enter. The button will
instantly update.
Event Handlers
Now all that is left to do is to add what is called an Event Handler to
the button, so that when the user clicks it, something happens (an
event, or more literally, a function in code, is called). To do this you
can just double-click the button, but to understand it better I will
show you how to do it another way.
In the property window where you just set the text of the button,
notice the icon. This switches between properties and events.
Click it to take you to the buttons events (make sure the button is
still selected). Now scroll down and find the event called Click, and
double-click it. This will automatically create you the event handler
code and take you to it.
32
Connecting with Visual Studio Express
You will notice you are now in a coding window not a visual design
window. Let me explain; above the Property Panel to the right is the
Solution Explorer. This is the same as the Project Explorer in VBA
that showed all files in the macro. We use this like windows explorer
to open, close, delete and
rename files in our project. The
only file you need to be aware of
right now is the Form1.cs (or
whatever you called your form.
To open the form designer
(where we added the button
visually) just double-click
Form1.cs in the Solution
Explorer; to get to the coding
behind the form right-click and
select View Code.
Back in the code view, you will have some code already in the
window to get you started. This is what Visual Studio created
automatically when you selected to create a Windows Form project.
Just like VBA, VS has added the required reference libraries for
creating a form and added the required code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
33
Connecting with Visual Studio Express
namespace ConntectingWithCS
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
}
Let’s go through each line of this code again so that you understand
what is going on.
34
Connecting with Visual Studio Express
remove all the using statements and still make your code work, but
the advantage of using is that, like in VBA where I excluded typing
swConst and swSaveAsVersion_e, it tells the compiler that if we
typed Environment (which is actually a member of the System
Library) it knows that we are accessing the System.Environment
object.
System.Drawing.Point
Point
Namespaces
Namespaces, again like using statements, are not strictly needed. To
keep this short, you can remove the namespace statement and its
open and closing curly braces or leave them in. For our usage
throughout this book it will make no difference. They are basically
used to group classes and coding blocks into a single accessible
name, to be accessed like an object.
35
Connecting with Visual Studio Express
The next line is the declaration of the class for the form we created
(the form with the button). This will always be generated
automatically for our use so you do not need to type this part
manually at any stage.
The { } braces are used for all classes, functions, if, else, while, do
and other statements in C# to determine the start and end of its
block of code. This is equivalent to VBAs If ... Then and End If, or
While... Wend.
public Form1()
{
InitializeComponent();
}
36
Connecting with Visual Studio Express
This is called the constructor class, and this gets called as soon as the
form is created. A constructor class has the same name as the class it
is within (in this case Form1), and has no return value. The only
function within it is a call to the function defined in the
Form1.Designer.cs class that is created automatically by VS to
create the form, create all the items like our button, add event
handlers, set properties, and show the form. If you placed code after
this InitializeComponent function call, that code would get run as
soon as the form is created, so to the user it would appear that the
code runs as soon as they run the program. As this is not the
behaviour we want, we go on to create a function that is called on
the click of a button instead.
37
Connecting with Visual Studio Express
Accessibility is either public, private or protected as standard. For
our usage we are going to be using public throughout.
The variable type is any type like the return value, such as a number,
text, object etc... A variable parameter type cannot be void.
The variable name can be any name again, starting with a letter
containing no special characters or reserved names or spaces.
38
Connecting with Visual Studio Express
So using that knowledge we know that this event handler function
that VS has created for us when the button is clicked returns no value
(void) back to its caller, is private so inaccessible to certain areas of
code, is called button1_Click, and takes 2 parameters.
And then finally, at the top of the code where the using statements
are, add:
using SldWorks;
39
Connecting with Visual Studio Express
40
Connecting with Visual Studio Express
So first let’s define our variables again; we can define these either
inside this function, or inside the main class function. For example:
For best practise in the future I will be writing the main variables in
the main class not the event handler.
41
Connecting with Visual Studio Express
VBA
Now, let’s do exactly this but in C# within our event handler button
function.
42
Connecting with Visual Studio Express
Firstly, because C# does not use the GetObject function to acquire a
handle to SolidWorks, we must use another function found in the
System.Runtime.Interop library. Add the following line to the using
section:
using System.Runtime.InteropServices;
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
Casting
Because C# is stricter on variable types, unlike VBA, we must do all
the type conversions ourselves. Because the function
GetActiveObject returns a variable of type object, we must convert
it to the correct type, using a method called Casting.
43
Connecting with Visual Studio Express
To cast the object returned from GetActiveObject, we simply write
the type of variable we would like it converting to inside parentheses
before the method and after the equals sign. This does the job of
converting the object to our variables type. In this case we typed
SldWorks.SldWorks, which is the type of the swApp variable.
Try/Catch block
Now we wish to check if we have managed to get the SolidWorks
object; however, this time we do this a little differently. Because the
GetActiveObject function actually throws a system error if it fails we
must try the function and catch any system errors that get thrown. If
we do not handle this error our program will simply crash. We do this
by placing the statement within a try/catch block.
try
{
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch
{
MessageBox.Show("Error getting SolidWorks Handle");
return;
}
When you place something inside a try block, your program will
handle any system errors that are caused by any code you place
within there, and instead of crashing your program, it passes them to
the catch block. So in effect, we try the GetActiveObject function,
44
Connecting with Visual Studio Express
and if it succeeds our code continues to execute after the catch
block. If it fails, our code jumps out of the try block at the point the
error occurs, and goes straight to the catch block to process any
code within there, not executing any code in the try block after the
point of the error.
swModel = (ModelDoc2)swApp.NewDocument(@"C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0);
The only thing different here is the @ sign placed within the
parentheses but before the opening quotes. All that this means is the
following string contains back/forward slashes as literal characters
instead of escape characters. If we did not put the @ in, we would
need to put \\ for every single slash we wrote, because as standard \
is used to place special characters in strings, such as \t for a tab.
45
Connecting with Visual Studio Express
When typing in any .Net language, you will find you get much better
feedback from the Vs IntelliSense telling you about the object you
are using.
if (swModel == null)
{
MessageBox.Show("Error creating new part");
return;
}
C#
== Is Equal To
!= Is Not Equal To
<= Is Less Than or Equal To (Numeric)
>= Is Greater Than or Equal To
< Is Less Than
> Is Greater Than
= Set variable to left
*= Multiple left variable by right
/= Divide left variable by right
^= XOR left variable by right
+= Add right variable to right
-= Subtract right variable from left
46
Connecting with Visual Studio Express
VB.Net
= Is Equal To / Set variable to left
<> Is Not Equal To
<= Is Less Than or Equal To (Numeric)
>= Is Greater Than or Equal To
< Is Less Than
> Is Greater Than
*= Multiple left variable by right
/= Divide left variable by right
^= XOR left variable by right
+= Add right variable to right
-= Subtract right variable from left
using SwConst;
if (!bRet)
47
Connecting with Visual Studio Express
{
MessageBox.Show("Error saving new part");
}
Firstly, we defined the variables needed for the error and warning
values. Then at the same time as declaring the variable bRet (true or
false), we also set it by calling the SaveAs function.
We also cast the constant enums to int as the function requests int
values. And if you notice the Intellisense tooltip you see that it
states that the error and warning variables should be passed as ref.
If you recall earlier me telling you about ref and out keywords; by
passing these two variables as ref means that when the SaveAs
function alters them within its own code, the two variables can be set
inside the SaveAs function. The easiest way to explain it is that if you
pass variables without a ref or out keyword, they are copied to the
function, not sent directly, so any alterations made are made to
copies of the variables, not the originals. By passing them with ref or
out, the originals are sent.
48
Connecting with Visual Studio Express
passing them as they are passed as ref, not out; that is the reason for
assigning them the value 0.
Once the SaveAs function has been called and the return value has
been saved in the bRet variable, we then check whether this bRet is
true or false. If you place a Boolean value inside an if statement,
there is no need to do a check such as bRet == true or bRet == false,
you just put the variable in the parentheses. Because we do not want
to check if bRet is true (success), but false, we place the not sign (!)
before it to say “if bRet is not true”, instead of “if bRet is true” so the
code within the if statement will only run if bRet is false.
Make sure SolidWorks is open then go to your form and click the
button to see the magic. If you have errors check you code with the
code on the CD. Complete listing below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using SldWorks;
49
Connecting with Visual Studio Express
using SwConst;
namespace ConntectingWithCS
{
public partial class Form1 : Form
{
SldWorks.SldWorks swApp;
ModelDoc2 swModel;
public Form1()
{
InitializeComponent();
}
swModel = (ModelDoc2)swApp.NewDocument(@"C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0);
50
Connecting with Visual Studio Express
if (swModel == null)
{
MessageBox.Show("Error creating new part");
return;
}
if (!bRet)
{
MessageBox.Show("Error saving new part");
}
}
}
}
One thing you may notice is that C# requires every statement line to
end with a semi-colon (;).
51
Connecting with Visual Studio Express
This will then create your project and automatically create a blank
form for you. We are going to create a button that on clicking will
effectively initiate our “macro”.
From this point on up until you add the event handler to the button,
is exactly the same process as the C# section, so refer to that. Once
you have created the event handler you will be taken to the coding
section, but this time it looks rather different from the C# code.
52
Connecting with Visual Studio Express
You will also notice that in
the Solution Explorer, by
default, there is no
Reference item. Just click
the second icon in the
explorer called “Show All
Files” to show the
References folder.
Add the SolidWorks references just like you did in the C# project.
Now you are ready to begin.
VB.Net does not use the using statements like C#, it uses Imports
instead. So at the very top of the forms’ code (right-click on
Form1.vb->View Code) place this code to import the SolidWorks
library in for use. Again this is not essential it just saves typing the
library name before every variable.
Imports SldWorks
Place the variables inside the main class function, not the button
click function. You entire code so far should look like this:
53
Connecting with Visual Studio Express
Imports SldWorks
End Sub
End Class
54
Connecting with Visual Studio Express
With a working SolidWorks application instance we will now attempt
to create a new document and at the same time assign the new
document to the swModel variable.
swModel = swApp.NewDocument("C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0)
Imports SwConst
And define the required variables and pass the errors and warning
variables as references, like in C#. Only in VB.Net we do not need to
state the keyword ByRef as VB.Net automatically passes the
variables as references without any keywords.
55
Connecting with Visual Studio Express
And that’s it for VB.Net. Easy huh? Just press Ctrl+F5 to build and
start your project and test it again just like the C# version.
As usual, if you have any problems, double-check your code, and if all
else fail, compare it to the working version on the CD.
This template will be used throughout the rest of the book just like
the C# template, and only the coding within the event handler will
be displayed and explained from now on.
56
Connecting with Visual Studio Express
VB.Net
Imports SldWorks
Imports SwConst
swModel = swApp.NewDocument("C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0)
57
Connecting with Visual Studio Express
swSaveAsVersion_e.swSaveAsCurrentVersion,
swSaveAsOptions_e.swSaveAsOptions_Silent, Nothing, lErrors,
lWarnings)
End Sub
End Class
58
Starting SolidWorks Programming
59
Starting SolidWorks Programming
Right, let’s get into some interesting SolidWorks programming!
C#
swModel = (ModelDoc2)swApp.ActiveDoc;
if (swModel == null)
{
MessageBox.Show("Failed to get active document");
return;
}
if (swModel.GetType() != (int)swDocumentTypes_e.swDocDRAWING)
{
MessageBox.Show("Active document is not a drawing");
return;
}
60
Starting SolidWorks Programming
VBA
Notice we get the ActiveDoc object from swApp, which will return
either nothing or the handle to the document that is visible in
SolidWorks. We then check that we have a document, and if we do
we check that it is a drawing, else we stop. We do this by calling the
ModelDoc2 function GetType, which returns an integer value
representing the type of either None, Part, Assembly or Drawing. If
you read the API help and type in ModelDoc2::GetType as the
search you will find that it doesn’t tell us much regarding this, but at
the bottom it does give us the possible return values:
Remarks
The retval argument might be one of the following values:
swDocNONE - no document
swDocPART - part document
swDocASSEMBLY- assembly document
swDocDRAWING - drawing document
61
Starting SolidWorks Programming
Object Browser
Here is how I found out that the different type values I needed were
in the enumerator swDocumentType_e. If you are in VBA, go to
View->Object Browser or press F2, and if you are in .Net then go to
View->Other Windows->Object Browser. This will bring up a new
window that’s a bit like a search tool. From here you can look at all of
the properties and methods (functions) that any library object has. It
comes in handy when you simply need to search for a function as no
other help or documentation tells you what you need.
In the search box, type in any one of the values that the help file told
us was a possible return value, let’s use “swDocNONE” without
quotes.
With a handle to the active drawing sheet, we get a list of all the
drawing sheets, activate each one and then save them as a DXF.
62
Starting SolidWorks Programming
C#
VBA
With the DrawingDoc object, we get all of the sheet names of this
drawing by calling GetSheetNames. In .Net we cast this to an array
(collection) of strings (text values), which is basically a list of the
sheet names. In VBA we use the variable equivalent Variant.
63
Starting SolidWorks Programming
Next we call a function that will ask the user to specify a save
location for the DXF files that will be created. We will write this
function later.
Then, we use a For Each statement, which will loop all of the code
within its block for each item it finds within an array. So in this case
we loop through every sheet name in the collection of sheet names,
so that we can process each one.
Within the for each statement block, we write the code to firstly
activate the sheet, and secondly, save it as DXF.
C#
VBA
64
Starting SolidWorks Programming
swModel.ActivateSheet sheetname
If bRet = False Then MsgBox "Failed to save " & sheetname & " as
DXF."
Next sheetname
Within the for each loop we start by activating the current sheet we
are iterating through by calling the ActivateSheet function of the
DrawingDoc object, and passing the current sheet name as the
parameter.
65
Starting SolidWorks Programming
As for the save location; once we get our path where the user would
like to save the file, we add the current sheet name and then the dxf
extension.
In .Net, we can use a System library function to ask the user to select
a folder, and then return the selected location. We place the
terminating backslash to the path as the function we use leaves it
off.
66
Starting SolidWorks Programming
Run your code and watch as the drawing sheet flicks between each
drawing sheet and saves as a DXF.
67
Starting SolidWorks Programming
Start with the usual template connecting into SolidWorks, and then
get a handle to the active model document using swApp.ActiveDoc
setting it to the swModel variable like in the last example. It is this
model handle we will be performing all our inspection on.
If you are using C# or VB.Net, first add the following line to the
using/Imports section respectively to allow us to use functions from
the Input/Output library.
C#
68
Starting SolidWorks Programming
Using VBA is quite a lot more work to achieve the same goal. Firstly,
we must create a function that will return the last position of string
within another string. Explaining this function is beyond the scope of
this book so if you wish to understand this function you can discuss it
on the AngelSix forum, but basically the concept is to find the last
occurrence of a certain character such as a period (.) to find the
extension, or a backslash (\) to find the directory.
VBA
Do Until iPos = 0
If iPos = -1 Then iPos = 0
iPos = InStr(iPos + 1, stringToSearch, searchFor)
If iPos <> 0 Then iTemp = iPos
Loop
iPos = iTemp - 1
LastIndexOf = iPos
End Function
69
Starting SolidWorks Programming
Now with this function at our disposal, it is still a bit messier than the
.Net version:
VBA
fullname = swModel.GetPathName()
filelocation = Left(fullname, LastIndexOf(fullname, "\"))
You have already seen how to get the type of file from the previous
chapter, but not how to change the numeric value we got back into a
text or descriptive name for the user.
C#
70
Starting SolidWorks Programming
VBA
Due to its very limited powers VBA has no easy way to get a string
representation of an enumerator, so we simply get a number.
Obviously not much use for the user to see, but if you came to that
level anyway I would recommend using C# or VB.Net.
File/Model Modes
As well as titles and paths, we may need to know if the file we are
working on is capable of having itself altered and saved, or if we can
edit it or not. This is where file modes come in to play.
We will get all 3 file modes at once and store them in Boolean
variables:
71
Starting SolidWorks Programming
C#
VBA
bReadOnly = swModel.IsOpenedReadOnly()
bViewOnly = swModel.IsOpenedViewOnly()
bLargeMode = swModel.LargeAssemblyMode
Documents Material
Another piece of potentially useful information is the current
material that is set for the part. The material has several values; the
visible user-friendly name, and the internal ID.
C#
72
Starting SolidWorks Programming
VBA
That was easy enough wasn’t it? If you retrieve these values from an
assembly or drawing, or a part without a material set, you simply get
a blank string returned.
Configuration Names
Sometimes you may need the names of all configurations within a
part or assembly, for things such as retrieving preview images,
settings custom properties or printing.
C#
Here in the first line we create a new array of string values, and then
cast the object returned from the swModel function
GetConfigurationNames to the correct type.
73
Starting SolidWorks Programming
Then, to access each configuration name one by one, we use a
foreach loop.
VBA
Summary Information
Mainly useful for tracking, searching or archiving, the summary
information is a powerful piece of information to have at your
disposal. The Summary Information is the information set in the
File->Properties form such as title, subject, author, keywords,
comments, saved by, creation date and save date.
The .Net versions may seem more complicated, but this is purely
because we are using an automated method, that will update itself
whenever the SolidWorks enumerator values change, whereas the
VBA version is simply a static code that may well become invalid:
74
Starting SolidWorks Programming
C#
string[] summmaryNames =
Enum.GetNames(typeof(swSummInfoField_e));
foreach (string summaryName in summaryNames)
{
int sumId = (int)Enum.Parse(typeof(swSummInfoField_e),
summaryName);
string summaryValue = swModel.get_SummaryInfo(sumID);
}
With the summary names (which will be Title, Subject, Author etc...),
we then go through them one at a time using another foreach loop
and call the swModel function get_SummaryInfo, which takes a
single parameter of an ID, which we retrieve by parsing the name of
the enumerator back to an actual enumerator, and then casting it
into an integer. This seems a bit messy but it is in fact about the only
way to automatically iterate through a set of enumerator values.
Now we access all the information we require through the
summaryName, sumID and summaryValue each time we enter the
loop.
75
Starting SolidWorks Programming
VBA
sumInfoTitle = swModel.summaryinfo(swSumInfoTitle)
sumInfoSubject = swModel.summaryinfo(swSumInfoSubject)
sumInfoAuthor = swModel.summaryinfo(swSumInfoAuthor)
sumInfoKeywords = swModel.summaryinfo(swSumInfoKeywords)
sumInfoComment = swModel.summaryinfo(swSumInfoComment)
sumInfoSavedBy = swModel.summaryinfo(swSumInfoSavedBy)
sumInfoCreateDate = swModel.summaryinfo(swSumInfoCreateDate)
sumInfoSaveDate = swModel.summaryinfo(swSumInfoSaveDate)
sumInfoCreateDate2 = swModel.summaryinfo(swSumInfoCreateDate2)
sumInfoSaveDate2 = swModel.summaryinfo(swSumInfoSaveDate2)
76
Starting SolidWorks Programming
77
Starting SolidWorks Programming
Now drag the textbox to any size you would like and position it as
such. Then making sure it is still selected, go to the Properties
Window to the right, and set the following properties:
Now switch back to the coding of the form. Within the buttons event
handler function, we write the code to connect to SolidWorks and to
get the active document code, and then all of the code we have just
been writing above. It should all make sense if you have been
following along, and the only thing different is that instead of storing
the information in separate variables, we have simply made one
string variable that stores all of the values, separated by a new line.
78
Starting SolidWorks Programming
C#
try
{
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch
{
MessageBox.Show("Error getting SolidWorks Handle");
return;
}
swModel = (ModelDoc2)swApp.ActiveDoc;
if (swModel == null)
{
MessageBox.Show("Failed to get active document");
return;
}
string nl = System.Environment.NewLine;
string strInfo = "File Information" + nl;
79
Starting SolidWorks Programming
80
Starting SolidWorks Programming
textBox1.Clear();
textBox1.Text = strInfo;
Any here is the finished result when you run the code:
81
Starting SolidWorks Programming
82
Starting SolidWorks Programming
Now double-click the button to create the event handler for the click.
Like VS we will place all of our code in here, and modify the module
containing the main() function
to simply open our form.
83
Starting SolidWorks Programming
open the form upon running of the macro.
Sub main()
UserForm1.Show
End Sub
If you left the form Name property as default, then it will be called
UserForm1. If you do not get the IntelliSense menu popping up
when you press the period key after its name and before typing
Show, then you have the wrong name, so check the name property
of the form in the properties of the form designer.
Do Until iPos = 0
If iPos = -1 Then iPos = 0
iPos = InStr(iPos + 1, stringToSearch, searchFor)
If iPos <> 0 Then iTemp = iPos
Loop
84
Starting SolidWorks Programming
iPos = iTemp - 1
LastIndexOf = iPos
End Function
Now it s time to place all of our code for getting the information that
we wrote above into the event handler function of the button that
was created earlier, so that the information will be shown in the
textbox when the
user clicks the
button. To get
back to the coding
window with the
button event
handler just right-
click the
UserForm1 item in
the Project
Explorer and select
View Code.
VBA
85
Starting SolidWorks Programming
fullname = swModel.GetPathName()
AddToStr "Full Path: " & fullname
If fullname <> "" Then
filelocation = Left(fullname, LastIndexOf(fullname, "\"))
AddToStr "Directory: " & filelocation
86
Starting SolidWorks Programming
87
Starting SolidWorks Programming
TextBox1.Text = ""
TextBox1.Text = strInfo
End Sub
* Find the complete C#, VBA and VB.Net examples on the CD if you
have any troubles.
88
Working with Selected Objects
Manipulate Dimension
Selecting Objects
89
Working with Selected Objects
Another major need for any good SolidWorks programmer is the
ability to work with and manipulate the selected objects. And the
first task once you obtain the selection is to identify it, so you know
how to proceed.
C#
swModel.ClearSelection2(true);
VBA
swModel.ClearSelection2 True
Now, say you wanted to actually use the selected objects that the
user has picked to do something. We must identify each of these
selections and make sure that they are of the correct type, and in
some cases, even in the correct order. I am going to show you how to
identify the selected objects of an assembly, add a coincident mate
and then error check.
Start with the usual template or whichever one you please, and
connect to SolidWorks and get the active document as usual.
90
Working with Selected Objects
swModel variables and call it swSelMgr (short for Selection
Manager), and make it of type SelectionMgr.
C#
SelectionMgr swSelMgr;
VBA
C#
swSelMgr = (SelectionMgr)swModel.SelectionManager;
VBA
91
Working with Selected Objects
C#
if (swModel.GetType() != (int)swDocumentTypes_e.swDocASSEMBLY)
{
MessageBox.Show("Can only add mate in an assembly");
return;
}
if (swSelMgr.GetSelectedObjectCount2(-1) != 2)
{
MessageBox.Show("You must select 2 entities, no more, no less");
return;
}
else
{
int selID1 = swSelMgr.GetSelectedObjectType3(1, -1);
int selID2 = swSelMgr.GetSelectedObjectType3(2, -1);
if ((selID1 != (int)swSelectType_e.swSelEDGES && selID1 !=
(int)swSelectType_e.swSelFACES && selID1 !=
(int)swSelectType_e.swSelVERTICES)
|| (selID2 != (int)swSelectType_e.swSelEDGES && selID2 !=
(int)swSelectType_e.swSelFACES && selID2 !=
(int)swSelectType_e.swSelVERTICES))
{
MessageBox.Show("You must select only edges, faces or
vertices");
return;
}
}
92
Working with Selected Objects
Here we first check the type of document that is active. You have
seen this before so if you need an explanation return to the previous
chapter. Then we use a function of the acquired selection manager
called GetSelectionObjectCount2; this returns an integer value
representing the number of objects that are selected by the user.
Because we are doing a coincident mate we want exactly two.
&& = AND
|| = OR
93
Working with Selected Objects
int iErrors;
((AssemblyDoc)swModel).AddMate3(
(int)swMateType_e.swMateCOINCIDENT,
(int)swMateAlign_e.swMateAlignCLOSEST,
false, 0, 0, 0, 0, 0, 0, 0, 0, false,
out lErrors);
if (iErrors != (int)swAddMateError_e.swAddMateError_NoError)
{
MessageBox.Show("Error mating components: " +
((swAddMateError_e)iErrors).ToString());
}
else
{
swModel.ClearSelection2(true);
}
94
Working with Selected Objects
matter for us are the first 3 and the last 2. After we cast our
ModelDoc2 swModel variable to an AssemblyDoc variable we can
use this function.
VBA
95
Working with Selected Objects
96
Working with Selected Objects
In VBA, we have had to specify each error value and its
corresponding string value. For this we use a Switch statement; it
works by returning the variable after the first value that returns true.
So in this case, we check if “iErrors = 0”, if it does it returns the
variable after that, which is “Error Unknown”, if it is false, it carries
on until it finds one.
97
Working with Selected Objects
You know how to check for the number of selected objects and their
types now so I will just get down to the raw code of getting a handle
to the Component of the selected object, and setting its material.
C#
if (comp != null)
{
ModelDoc2 model = (ModelDoc2)comp.GetModelDoc();
if (model.GetType() == (int)swDocumentTypes_e.swDocPART)
((PartDoc)model).SetMaterialPropertyName2("", materialDB,
material);
98
Working with Selected Objects
}
}
swModel.EditRebuild3();
Now we create a loop for every selected object. Within that loop we
firstly get the component that is associated with this selected object.
The GetSelectedObjectsComponent3 function accepts the
selection index, which we pass each time. All the function does is
simply jump up the feature tree until hitting a Component object, so
no matter what item or feature of a part you select, this function will
get its component.
99
Working with Selected Objects
Doing this procedure in VBA is no different and due to the looseness
of the language it actually looks a bit simpler in comparison as we do
not need any casting.
VBA
Dim i As Integer
For i = 1 To swSelMgr.GetSelectedObjectCount2(-1)
Dim comp As Component2
Set comp = swSelMgr.GetSelectedObjectsComponent3(i, -1)
100
Working with Selected Objects
Manipulating Dimensions
One of the major features of any 3D software package is dimensions,
and even through altering them through code it as simple as 1-2-3,
here is a quick code snippet of how to double the value of the
selected dimension. Again, presuming you are up to the point of
having a working selection manager and the user has selected a
dimension.
C#
if (swSelMgr.GetSelectedObjectCount2(-1) == 0)
{
MessageBox.Show("You must select one or more objects");
return;
}
if (swSelMgr.GetSelectedObjectType3(1, -1) !=
(int)swSelectType_e.swSelDIMENSIONS)
{
MessageBox.Show("You must select a dimension");
return;
}
DisplayDimension dispDim =
(DisplayDimension)swSelMgr.GetSelectedObject6(1, -1);
Dimension dim = (Dimension)dispDim.GetDimension2(0);
if (dim.DrivenState !=
(int)swDimensionDrivenState_e.swDimensionDriving || dim.ReadOnly)
{
101
Working with Selected Objects
double[] oldVals =
(double[])dim.GetSystemValue3((int)swInConfigurationOpts_e.swThisCo
nfiguration, null);
oldVals[0] *= 2;
int retVal = dim.SetSystemValue3(oldVals[0],
(int)swInConfigurationOpts_e.swThisConfiguration, null);
if (retVal != (int)swSetValueReturnStatus_e.swSetValue_Successful)
{
MessageBox.Show("Error setting dimension: " +
((swSetValueReturnStatus_e)retVal).ToString());
}
swModel.EditRebuild3();
We start by checking that the user has selected an item. If they have
we check that the selection is of type swSelDimensions before
proceeding to attempt to alter it.
In order to get and set dimension values we must get the actual
Dimension object, not the DisplayDimension that is retrieved by
the user selection. We use the DisplayDimension method
102
Working with Selected Objects
GetDimension2 to achieve this; passing 0 into this function gets the
first chamfer dimension and 1 gets the second if you have multiple
dimension display. In this example we are working with single
dimensions, so just pass 0.
Now with the original values in an array, we access the first value in
that array and multiply it using the statement:
oldVals[0] *= 2;
oldVals[0] = oldVals[0] * 2;
103
Working with Selected Objects
With the value doubled we must now set this new value back to the
dimension. We do this with the equivalent function
SetSystemValue3. This however accepts a single double value not
an array, so we pass the first value of our array as the first parameter,
and then the enum for this configuration again, and null for the
configuration names. This function returns an error checking
enumerator value, so we retrieve this like any other function we have
checked before and then check it against the successful enum value.
If it is not successful something went wrong so we display the error
by casting the integer return value back to an enumerator value, and
then to a readable string.
This code will work on assemblies, parts and drawings, provided the
dimension is alterable.
The VBA is exactly the same except the usual lack of enum names,
and no need to cast any objects.
VBA
If swSelMgr.GetSelectedObjectCount2(-1) = 0 Then
MsgBox "You must select one or more objects"
Exit Sub
End If
If swSelMgr.GetSelectedObjectType3(1, -1) <> swSelDIMENSIONS
Then
MsgBox "You must select a dimension"
Exit Sub
End If
104
Working with Selected Objects
swModel.EditRebuild3
And there you have it! Hopefully by now you are getting the hang of
things. Once you know the basics such as casting objects to the
correct value in .Net languages, and retrieving enumerator values
105
Working with Selected Objects
and checking them for errors and the likes, the procedure is pretty
much the same for all functions and properties.
The main problem with the SolidWorks API documentation is its lack
of explanations in regards to these topics, but now you know how to
convert enumerators, integers, double arrays, ModelDoc2 to any of
the 3 children AssemblyDoc, PartDoc and DrawingDoc, as well as
converting errors into messages and other techniques, you should be
well on your way.
106
Working with Selected Objects
Selecting Objects
One more thing I would like to cover before we move on is how you
actually select objects within your code. Because there are many
different ways of doing this and it all comes down to your situation, I
will just list the single lines of code you use to select specific objects.
We will encounter some of them later in the book, but they are
simple enough to understand.
ModelDocExtension::SelectByID2
In order to select almost any object from any location, you can use
the universal SelectByID2 of ModelDocExtension of ModelDoc2,
where you pass the fully qualified name of the entity you would like
to select. This name is based on the location of the entity within the
ModelDoc2 object that you are calling the function from. The best
way to find your required ID name is to record a macro selecting the
part you would like.
Name String
Type String
X, Y, Z Double
Append Boolean
Mark Integer
Callout Pointer
SelectOption Enumerator (Integer)
107
Working with Selected Objects
The Name is the fully qualified name of the object to select that I will
explain further in a second.
The Type is the string name of the type of entity you are going to
select. If you take a look at the help it will give you all of the types.
The Callout is used mainly in add-ins and again is beyond this book.
In order to select anything by its ID, in most cases you need the fully
qualified name. In order to work out this name you must follow the
following format:
[Name]@[Feature Name]@[Part]-[InstanceID]@[Top-
Level Assembly]/[SubAssembly]-[InstanceID]@[Bottom-
Level Assembly]
Omitting any field that you don't need. Here are a few examples:
108
Working with Selected Objects
Part.Extension.SelectByID2("Thickness@Sheet-Metal1@0Bracket-
1@MachineContainer", "DIMENSION", ...
Where
Thickness = Dimension name
Sheet-Metal1 = Feature Name
Bracket-1 = Part Name & Instance ID
MachineContainer = Assembly Name
Where
WebThickness = Dimension name
Web Thickness Offset = Plane Name
MachineContainer = Assembly Name
109
Working with Selected Objects
Where
Front Centre = Feature Name (Plane)
Generic 01CS-015-1 = Part Name & Instance ID
01 Bedplate = Top-Level Assembly
/Generic 01CP-035-1 = Sub-Assembly & Instance ID
Generic 01CS-015 = Bottom-Level Assembly
110
Working with Selected Objects
Find below a list of methods to use for each type of object to select.
You should by now be able to figure out from the API reference, how
to pass the correct parameters. As for actually getting a handle to
the object in the first place; we will cover some of them in the next
chapter.
Annotation Annotation::Select3
Body Body2::Select2
BreakLine BreakLine::Select
Component Component2::Select3
Configuration Configuration::Select2
Edge Point EdgePoint::Select
Entity Entity::Select4
Feature Featur::Select2
Sketch Contour SketchContour::Select2
Sketch Hatch SketchHatch::Select4
Sketch Point SketchPoint::Select4
Sketch Segment SketchSegment::Select4
111
Working with Selected Objects
C#
swApp.SetSelectionFilter(swSelectType_e.swSelFACES |
swSelectType_e.swSelEDGES);
VBA
swApp.SetSelectionFilter swSelFACES Or swSelEDGES
112
Property Manager Pages
Responding to events
113
Property Manager Pages
Many of you who have already attempted to create Property
Manager Pages from the provided documentation of SolidWorks
have probably not understood 90% of the code, but rather done
what the documentation has told you, accepted it as the way to
create a page, and modified the bits you want to work with in the
hopes that it will work. Am I correct?
You can treat PMP’s more like forms in the way that you can have
your code working from events instead of loops and straight through
until completion. You can run some initialisation code, wait for the
user to select components and click a button, and then react to that.
Sound exciting? Let’s get started.
114
Property Manager Pages
example, to ensure that any car created will follow these basic rules.
We would derive our SuperFly car class from the base Car class, so
that it inherited, and was forced to follow, these base rules.
When you derive from a base class, your new class first needs to
state that it is deriving from that class, and secondly you must create
all of the functions that the base class asks you too. So, we are going
to create a new class, derive from the PMP base class, and specify all
of the required blank functions.
Start by creating a new project in.Net from the normal template with
the button to start the code, and go to the coding view. Add the
following using code:
using SWPublished;
Now create a new class outside of the main form class but inside the
namespace, and call it MyFirstPMP. Now for the important part;
after the class name, but before the opening curly braces, place a
colon (:), followed by the name of the base class to derive from, in
this case PropertyManagerPage2Handler5.
115
Property Manager Pages
You will also notice that within each function there is one line of code
throwing an error to the system. This is inserted by default and when
the function is called it will throw a system error stating that the
function is not yet implemented. This is not the functionality we
want as we don’t really care if some functions are not implemented.
So remove that line of code to be left with empty functions. The end
result looks like this:
C#
116
Property Manager Pages
117
Property Manager Pages
#endregion
}
I have moved the curly braces up to the same line instead of on new
lines to save space in the coding; it just makes for easier reading.
The #region and #endregion are Visual Studio styling tags, they are
nothing to do with the program code and do not get compiled when
you create your program. Their function is to tidy up your code.
Anything within the #region/#endregion block can be expanded
and collapsed using the + and – box to the left of the #region tag.
This is very useful when your code gets long in order to keep it tidy.
Before we move on, let’s create the same coding but in VBA. Start by
creating the usual VBA template that connects to SolidWorks, and
acquires the active document. Then leave the main function like that
for now.
118
Property Manager Pages
Go to Insert->Class Module to insert a new class coding file. Open
up this class in the coding window if not already, by expanding the
Class Module folder and double-clicking the Class1 file. This should
display a blank file in the coding window.
Before we start writing our code in here let me explain exactly where
we are now; if this were C#, we would effectively be within the
following code block:
VBA
Implements PropertyManagerPage2Handler5
Now, unlike .Net languages VBA does not have the power to
automatically create all of the required base functions for us, but
don’t panic! It does have a semi-automated method. In the coding
window, notice the 2 drop-down boxes right above the first line of
code. The one on the left is the Object Box, and the one on the right
is the Procedures/Events Box.
119
Property Manager Pages
Procedures is yet another word for a function or a method or a
subroutine; each language uses its own term but they all mean
exactly the same, unless you want to get pedantic and state that a
subroutine should not return a value whereas all others can, but I
wasn’t going to say anything.
120
Property Manager Pages
This will populate the Procedures Box to the right, and have inserted
a single function for you.
Private Sub
PropertyManagerPage2Handler5_OnClose(ByVal Reason As
Long)
End Sub
What has happened here is that whenever you select an item from
the Procedure Box to the right, VBA will create an empty function
for that item if it doesn’t exist, and because by selecting the base
class from the Object Box populates the Procedure Box to the right,
it has selected the first item in its new list by default, and so create
an empty function for it.
121
Property Manager Pages
Now we need an empty procedure for all of the base class functions
for this to work so from the Procedure Box just go through one by
one and select each and every item in the list.
Once you have selected an item it will turn bold indicating there is
already a function for it, so selecting it again will just take you to it.
Select all items so they are all bold and then you will have the
following code:
122
Property Manager Pages
VBA
Implements PropertyManagerPage2Handler5
Private Function
PropertyManagerPage2Handler5_OnActiveXControlCreated(ByVal Id As
Long, ByVal Status As Boolean) As Long
End Function
Private Sub
PropertyManagerPage2Handler5_OnComboboxEditChanged(ByVal Id
As Long, ByVal Text As String)
End Sub
123
Property Manager Pages
Private Sub
PropertyManagerPage2Handler5_OnComboboxSelectionChanged(ByVa
l Id As Long, ByVal Item As Long)
End Sub
................................................................................
................................................................................
Private Function
PropertyManagerPage2Handler5_OnSubmitSelection(ByVal Id As Long,
ByVal Selection As Object, ByVal SelType As Long, ItemText As String)
As Boolean
End Function
Private Sub
PropertyManagerPage2Handler5_OnTextboxChanged(ByVal Id As
Long, ByVal Text As String)
End Sub
124
Property Manager Pages
I have cut out the middle code as I am sure you get the point. With
our new class created we are ready to create a new instance of it (a
variable of it), and then call a function of the class to tell it to show.
In .Net we must first add another function to our PMP class; add the
following function before all of the base class functions:
C#
Now we can call this function from our main function that connects
to SolidWorks. Go back to the main function of our code (the place
where we connected to SolidWorks, in .Net this is usually the button
click event). In our main function, after we have got the active
document, we want to create a new instance of our class and call a
method to tell it to initialise and show the PMP.
C#
125
Property Manager Pages
Show; this will run whatever code we place within the Show function
of our class and pass in the active SolidWorks Application variable.
As far as our main code is concerned we are now done, all that
remains is to do something with our Property Manager Page. But
before we do that, let’s run over the VBA coding too.
VBA
End Sub
We can call this function from our main function that connects to
SolidWorks. Back to the main function in our main module, after we
have acquired the active document, we want to create a new
instance of our class and call the Show function.
By default our class is called Class1. To find out the name of the
class, single-click the class module file in the Project Explorer and
look at the Property Window below it. The field (Name) is the name
of the class; alter this to MyFirstPMP, and press enter.
126
Property Manager Pages
Now go back to our main module and after we acquired the active
document, place the following code:
VBA
We are now ready to run the project, however this will not do
anything yet as we have not created anything to display.
127
Property Manager Pages
128
Property Manager Pages
Let’s start by creating the variables that we require such as the actual
Page, a single Group and a Label so that we can see something on
the page to begin with. Once we have done this and we know that
our page is working we will go on to create some more advanced and
useful pages.
C#
PropertyManagerPage2 pmPage;
PropertyManagerPageGroup pmGroup;
PropertyManagerPageLabel pmLabel;
int idGroup = 0;
int idLabel = 1;
Now in the Show function we will create a few more variables ready
for use in a second:
C#
int pageoptions =
(int)(swPropertyManagerPageOptions_e.swPropertyManagerOptions_Ok
ayButton |
129
Property Manager Pages
swPropertyManagerPageOptions_e.swPropertyManagerOptions_Cancel
Button |
swPropertyManagerPageOptions_e.swPropertyManagerOptions_Locked
Page);
int groupoptions =
(int)(swAddGroupBoxOptions_e.swGroupBoxOptions_Expanded |
swAddGroupBoxOptions_e.swGroupBoxOptions_Visible);
int iErrors = 0;
int controloptions;
short controltype;
short controlalign;
swPropertyManagerOptions_OkayButton
This adds an OK button to the top of the page
swPropertyManagerOptions_CancelButton
This adds a cancel button to the top of the page
swPropertyManagerOptions_LockedPage
This prevents the page from automatically closing if the user tries to
edit the part, or select an object, or change to another document
etc...
swPropertyManagerOptions_CloseDialogButton
This has no use for us.
130
Property Manager Pages
swPropertyManagerOptions_MultiplePages
This property is set to show the previous/next page buttons if you
create multiple pages.
swPropertyManagerOptions_PushpinButton
This shows the pushpin button.
swPropertyManagerOptions_PreviewButton
This shows a default Preview button. The benefit of this is that a
LockedPage option will treat this button specially, and won't close
the page on click.
swPropertyManagerOptions_DisableSelection
This prevents the user from selecting any models in the document.
swPropertyManagerOptions_WhatsNew
This shows a “What's New” button.
swPropertyManagerOptions_AbortCommand
This forces any current command in progress such as mating to be
aborted when the page gets displayed.
swPropertyManagerOptions_UndoButton
This shows the user an undo button, and calls the OnUndo function
when clicked.
swPropertyManagerOptions_CanEscapeCancel
This allows the user to exit the Page by pressing escape. This is not
work if your page has a selection box.
131
Property Manager Pages
swPropertyManagerOptions_HandleKeystrokes
This sets the page up to handle all keystrokes, which get sent to the
OnKeystroke function for you to handle.
swPropertyManagerOptions_IsDragDropCmd
Allows drag-dropping on page.
For our purpose we just want to show the OK and Cancel Buttons,
and lock the page.
The groupoptions variable stores the options for our group box we
will create later. Again this is an enumerator like the page options,
but with far fewer options; whether the group is visible or not, if it is
expanded, and if it has a checkbox and whether that checkbox is
checked or not. For our example we simply show the group box and
make it expanded.
The other variables will come into play in a minute. Now it is time to
create the actual page:
C#
pmPage =
(PropertyManagerPage2)swApp.CreatePropertyManagerPage("My First
PMP", pageoptions, null, ref iErrors);
132
Property Manager Pages
CreatePropertyManagerPage
In order to create and setup a new PropertyManagerPage object we
must call the CreatePropertyManagerPage function of the
SolidWorks Application object, in our case the swApp object. You
can create PMP’s without any documents open, but you must have a
document open before you can display one.
The title is a string value that is shown in the title of the page;
this is the white writing that appears on the blue-background for
windows such as the Mate window, or the Dimension Property
Window.
The Options are the options we have just been over regarding
what buttons and styles to apply.
What this means is that our .Net examples here will not actually
receive any feedback to any of the functions we have declared in
our class such as AfterActivation or OnClose etc... Advanced
add-ins is a dedicated topic that could take a whole book to
explain in itself.
133
Property Manager Pages
For now we will stick to creating the pages with .Net just to show
you how it is done, and then move onto VBA (which is run in the
same memory space as SolidWorks) to handle feedbacks.
C#
if (iErrors !=
(int)swPropertyManagerPageStatus_e.swPropertyManagerPage_Okay)
{
MessageBox.Show("Failed to create page: " +
((swPropertyManagerPageStatus_e)iErrors).ToString());
return;
}
C#
134
Property Manager Pages
pmGroup =
(PropertyManagerPageGroup)pmPage.AddGroupBox(idGroup, "My
groupbox", groupoptions);
controloptions =
(int)(swAddControlOptions_e.swControlOptions_Enabled |
swAddControlOptions_e.swControlOptions_Visible);
controltype =
(int)swPropertyManagerPageControlType_e.swControlType_Label;
controlalign =
(int)swPropertyManagerPageControlLeftAlign_e.swControlAlign_Indent;
pmLabel = (PropertyManagerPageLabel)pmGroup.AddControl(idLabel,
controltype, "My label", controlalign, controloptions, "My tip");
pmPage.Show();
We start by setting the page message. This will appear at the top of
the page and is usually used for a brief description of what is going
on. The function to do this is called SetMessage3, of the
PropertyManagerPage object. You do not have to call this function
and if you chose not to then the PMP will simply not display this
standard message box.
In this example we are going to set it to show you what it looks like:
135
Property Manager Pages
SetMessage3
We use this function to set a top message of a page.
AddGroupBox
Once we set this message, we then create a new group, adding it to
the pmPage page. The function is as follows:
136
Property Manager Pages
We then pass in the groupoptions variable we specified earlier for
the group box so that it is visible and by default expanded not
collapsed.
With the group box create we then add a label inside the group box
be calling the general AddControl function. This function is available
from any PropertyManagerPage object and any
PropertyManagerPageGroup object.
In this example, we do not add the label to the pmPage object, but
instead we add it to the group we just created. Because every other
control other than a page or a group box is added using a unique
AddControl function.
AddControl
The AddControl function looks like this:
And, the Tip is the text that will be displayed in a tooltip if the
user hovers over the control. This is usually a brief description or
help.
137
Property Manager Pages
We pass in our unique ID for the ID, and the enumerator for the
Label type. The other two enumerators we specify are for the
alignment of the control, and the visibility and state; these options
are self explanatory so just play around with them if you wish to see
the difference.
Finally, we call the pages Show function to display the page to the
user. Compile your code and give it a go. You should see the
following results.
138
Property Manager Pages
You can continue to add other controls and play around with the
PMP’s in .Net, but unless you plan on creating an add-in then you
cannot really do much with them. Personally, I find very little use in
PMP’s when using .Net as you have the entire power of the .Net
framework and a programming language at your disposal to get user
information or display to them whatever you like. That’s not to say
PMP’s don’t have their place.
Let’s carry on exploring them but using VBA, as this will allow us to
quickly and easily handle the events back from the page. I will not
explain the coding that has already been explained in the .Net
version as the principles are all the same.
First, let’s add the items to the page in VBA just like we have done in
.Net.
In the MyFirstPMP class, before all of the functions but after the
Implements PropertyManagerPage2Handler5 line, place the
following variables:
VBA
139
Property Manager Pages
Those are the only variables we need for now to get our example up
and running.
VBA
Sub Show()
lErrors = 0
idGroup = 0
idLabel = 1
pageoptions = swPropertyManager_CancelButton +
swPropertyManager_OkayButton +
swPropertyManagerOptions_LockedPage
groupoptions = swGroupBoxOptions_Expanded +
swGroupBoxOptions_Visible
140
Property Manager Pages
controltype = swControlType_Label
controlalign = swControlAlign_Indent
controloptions = swControlOptions_Enabled +
swControlOptions_Visible
Set pmLabel = pmGroup.AddControl(idLabel, controltype, "My Label",
controlalign, controloptions, "My Tip")
pmPage.Show
Else
MsgBox "Error creating Property Manager Page"
End If
End Sub
As you can see this is identical to the .Net version, just with the
syntax of VBA. The advantage we have here however is that notice
the call we have made to CreatePropertyManagerPage:
141
Property Manager Pages
Set pmPage = swApp.CreatePropertyManagerPage("My
First PMP", pageoptions, Me, lErrors)
Now compare that with the .Net version, and see the difference:
pmPage =
(PropertyManagerPage2)swApp.CreatePropertyManagerPag
e("My First PMP", pageoptions, null, ref iErrors);
142
Property Manager Pages
events, we will start by placing a simple message box in the OnClose,
AfterClose, and AfterActivation event functions:
VBA
Run your macro and you will notice that the AfterActivation event
fires before the PMP is visible; this is because it receives activation
control as soon as the message process starts for that control, not
when it gets drawn (displayed).
Now close the PMP using the X button, and you will see that the
OnClose message box appears before the PMP is closed, and the
AfterClose event fires once it is destroyed.
143
Property Manager Pages
Responding to Events
In order to best demonstrate working with events and responding to
them we are going to create a new PMP that has a selection tool in it
that limits the user to selecting faces. From there we are then going
to change the colour of those selected faces to Red, Blue or Green
based on the button the user clicks.
The first modification to add a check in the main function that the
active document is not a drawing; after the check for an active
document place the following:
VBA
VBA
144
Property Manager Pages
You will recognise all of these variables, except two new types for
the Selectionbox and the Buttons. There is nothing really to explain,
there are no different options for the buttons than there was for the
label control, and for creating the selection box. You will see how to
set the Selectionbox options up in a minute.
145
Property Manager Pages
Now inside the Show function we want to create the usual page,
settings the initial variables, and checking for success before
attempting to add our controls. Here is the code we should have so
far:
VBA
lErrors = 0
idGroup = 1
idSel = 2
idButtonRed = 3
idButtonGreen = 4
idButtonBlue = 5
pageoptions = swPropertyManager_CancelButton +
swPropertyManager_OkayButton +
swPropertyManagerOptions_LockedPage
groupoptions = swGroupBoxOptions_Expanded +
swGroupBoxOptions_Visible
Else
MsgBox "Error creating Property Manager Page"
End If
146
Property Manager Pages
Where I have placed the comment we will add our new controls. We
start by setting the pages message up to tell the user what our macro
is going to do:
VBA
VBA
VBA
controltype = swControlType_Button
controlalign = swControlAlign_Indent
147
Property Manager Pages
controloptions = swControlOptions_Enabled +
swControlOptions_Visible
VBA
pmPage.Show
VBA
filters(0) = swSelFACES
pmSelection.SingleEntityOnly = True
pmSelection.Height = 50
pmSelection.SetSelectionFilters filters
148
Property Manager Pages
pmSelection.SetStandardPictureLabel swBitmapLabel_SelectFace
The first line sets our Long array variable filters first and only item to
swSelFACES, which means we are only allowing faces to be
selected. This value is from the swSelType_e enumerator.
The last line sets the icon to use for this control. This function can be
called from any control you add such as a label, button, drop-down
list and all others. There are many icons:
swBitmapLabel_LinearDistance
swBitmapLabel_AngularDistance
swBitmapLabel_SelectEdgeFaceVertex
swBitmapLabel_SelectFaceSurface
swBitmapLabel_SelectVertex
swBitmapLabel_SelectFace
swBitmapLabel_SelectEdge
swBitmapLabel_SelectFaceEdge
swBitmapLabel_SelectComponent
swBitmapLabel_Diameter
swBitmapLabel_Radius
swBitmapLabel_LinearDistance1
swBitmapLabel_LinearDistance2
swBitmapLabel_Thickness1
swBitmapLabel_Thickness2
swBitmapLabel_LinearPattern
swBitmapLabel_CircularPattern
swBitmapLabel_Width
swBitmapLabel_Depth
swBitmapLabel_KFactor
149
Property Manager Pages
swBitmapLabel_BendAllowance
swBitmapLabel_BendDeduction
swBitmapLabel_RipGap
swBitmapLabel_SelectProfile
VBA
Private Function
PropertyManagerPage2Handler5_OnSubmitSelection(ByVal Id As Long,
ByVal Selection As Object, ByVal SelType As Long, ItemText As String)
As Boolean
PropertyManagerPage2Handler5_OnSubmitSelection = True
End Function
As you can see this function does one thing; return true all the time,
allowing any selection that passes our filter to be selected and added
to the selection box.
Now you are ready to run the macro, give it a go and select a face,
then close the page.
150
Property Manager Pages
You will notice that clicking the buttons
does nothing at the moment. That is
because we haven’t handled the event of
clicking the button. So let’s do that now!
VBA
151
Property Manager Pages
Dim v As Variant
v = swModel.MaterialPropertyValues
swModel.ClearSelection2 (True)
End Sub
All we have done here is to create 3 variables for the red, green and
blue colours, set them all to 0, and then depending on which button
was pressed, set that specific colour value. With the RGB values set
we get the default part material properties then we set the selected
face properties to the colour we defined above, and finally clear the
selection so we can see the results.
152
Traversing
153
Traversing
Traversing is the art of looping through every item in a list. By
traversing we are going to loop through every single component in
an assembly, later we will take it a step further and traverse though
all features of all of those components, and then after we are done
we will have some fun with .Net by creating a tree-view program and
adding some functionality to work with features and components.
The way we create this loop so that the coding will traverse all
children of all children so we get every component in the assembly is
to create a function that accepts a Component2 object as a variable.
Later on we will actually mimic the SolidWorks feature tree list that
is displayed on the left.
154
Traversing
Start by creating the usual template and getting the active
document. Now add the following variables next to the swApp and
swModel variables:
C#
Configuration swConf;
Component2 swRootComponent;
After the code where you get the active document, place the
following code to get the root component of the active document:
C#
swConf = (Configuration)swModel.GetActiveConfiguration();
swRootComponent = (Component2)swConf.GetRootComponent();
155
Traversing
C#
TraverseComponent(swRootComponent);
C#
if (children.Length > 0)
{
foreach (Component2 comp in children)
{
TraverseComponent(comp, tn);
}
}
}
156
Traversing
To show that we have passed over each component we will add a
message box showing the component title before the call to
TraverseComponent:
C#
MessageBox.Show(((ModelDoc2)comp.GetModelDoc()).GetTitle());
Now for the same code in VBA; start with the usual template, adding
the following additional variables;
VBA
VBA
This will get the root component as explain in the .Net version. Now
we call the TraverseComponent function:
VBA
TraverseComponent swRootComponent
157
Traversing
And here is the TraverseComponent function to do just the same as
the .Net version:
VBA
End Sub
And that is all there is to it really. Using this form of loop we can get
access to all of the Component2 objects in an assembly and do what
we want with them. You will see some useful things to do later.
158
Traversing
The following code will show how to traverse a component. You can
place this code within the assembly traversal loop if you wish. I will
take you through creating a complete tree-view program next to see
this in use.
C#
TraverseFeatures((Feature)comp.FirstFeature());
C#
159
Traversing
As you can see this is much similar to the previous looping function,
however we only run through the top-level features. As I mentioned
earlier, features can have sub-features themselves, so before the
message box line, place the following line to call another function
that will loop all sub-features:
C#
TraverseSubFeatures(feat);
And the function to loop all sub-features will also loop itself for any
sub-features of sub-features, just like the assembly components
functions did, this way we get every single feature there is:
C#
160
Traversing
}
}
Now for the VBA version; wherever you have a component object
you wish to traverse place the following function call:
VBA
TraverseFeatures (children(i).FirstFeature())
VBA
MsgBox feat.Name
TraverseSubFeatures feat
161
Traversing
Wend
End Sub
This function loops through all features, starting with the feature
passed in. It shows a message box to the user with the name of the
current feature within the loop. It then calls the
TraverseSubFeatures function to loop through all sub-features:
VBA
MsgBox feat.Name
TraverseSubFeatures feat
Set feat = feat.GetNextSubFeature()
Wend
End Sub
162
Traversing
And that covers that. We have now looped through every
component and every feature within an assembly. However, there is
a much better way to view all of these components and features
other than a message box to the user; through an actual tree-view
list just like in SolidWorks. That is what we are going to do next.
163
Traversing
164
Traversing
Into the coding view; add the following variables below the typical
swApp and swModel variables:
C#
Configuration swConf;
Component2 swRootComponent;
These will be used in a second. Now inside the button click event
function, after you have acquired the active document, you want to
do a check that we aren’t working with a drawing:
C#
if (swModel.GetType() == (int)swDocumentTypes_e.swDocDRAWING)
{
MessageBox.Show("Active document cannot be a drawing");
return;
}
C#
treeView1.Nodes.Clear();
165
Traversing
Next get the root component:
C#
swConf = (Configuration)swModel.GetActiveConfiguration();
swRootComponent = (Component2)swConf.GetRootComponent();
We will start our tree view with the top item being the actual active
model, like it is in SolidWorks:
C#
TreeNode tn = treeView1.Nodes.Add(swModel.GetTitle());
The Nodes Add function adds a node to the parent node or top-level
TreeView control, and it returns the actual node added. Because we
will be using this as a reference to add our child components and
features to we store it in a variable called tn.
C#
TraverseFeatures((Feature)swModel.FirstFeature(), tn);
if (swRootComponent != null)
TraverseComponent(swRootComponent, tn);
166
Traversing
We do a check that the root component was acquired before we
traverse it. If we are in a part, then there is no root component.
C#
if (children.Length > 0)
{
foreach (Component2 comp in children)
{
TreeNode tn =
treeNode.Nodes.Add(((ModelDoc2)comp.GetModelDoc()).GetTitle());
TraverseFeatures((Feature)comp.FirstFeature(), tn);
TraverseComponent(comp, tn);
}
}
}
167
Traversing
Next is to place this same modification into the TraverseFeatures
function too, and to add another small check to prevent adding the
assemblies components as features as well as components:
C#
168
Traversing
features, again passing in the features TreeNode object as the node
to add results too. Finally here is the modified TraverseSubFeatures
function:
C#
TraverseSubFeatures(subfeat, subtn);
subfeat = (Feature)subfeat.GetNextSubFeature();
}
}
Try running your program. Click the button once you have an
assembly or part open and see as your results in the tree view. Once
you click the button it may take a few seconds to complete
depending on the size of your assembly.
Find the complete code listing below. The VB.Net example is also on
the accompanying CD as per-usual.
169
Traversing
170
Traversing
C#
using System.Collections;
C#
Hashtable hashStore;
After the code line where we clear the tree view list we also want to
initialise a new hash table, effectively clearing it at the same time,
ready for a fresh start:
C#
171
Traversing
A few more lines down after we add the first item to the tree view,
before traversing the root components, place the following code:
C#
hashStore.Add(tn, swModel);
What this does is add an item to our Hashtable; each item stores a
key, and a value. The key is used to lookup or find the value. Here
the tn variable is the TreeNode item we just added, and the value is
the actual swModel variable linked to our model. You will see how
we look the swModel back up later.
C#
hashStore.Add(tn, comp);
C#
hashStore.Add(subtn, feat);
172
Traversing
C#
hashStore.Add(subtn, subfeat);
All we have done here is every time we add any TreeNode to the
TreeView control we add a reference in the Hashtable to the tree
node item and the associated model, component or feature.
173
Traversing
Inside the AfterSelect function place the following code:
C#
if (treeView1.SelectedNode == null)
return;
if (hashStore.Contains(treeView1.SelectedNode))
{
object link = hashStore[treeView1.SelectedNode];
if (treeView1.SelectedNode.Index != 0)
{
try
{
Component2 comp = (Component2)link;
comp.Select3(false, null);
}
catch
{
Feature feat = (Feature)link;
feat.Select2(false, -1);
}
}
}
We first check that we have an item in our list selected, and if so that
our hash table actually contains a link from the selected node
(although from our current code it always will). Then we acquire the
associated SolidWorks object be retrieving it from the hash table
passing in the selected node as the key. We then check if the
174
Traversing
selected node is the first item, in which case it is the model we add in
the beginning. Because we cannot select the model itself we skip the
code if it is.
If the selected node is not the first node, then it will be either a
Component2 object or a Feature object, but because the object is a
COM object, we cannot simply test what type it is, so we use a
Try/Catch block. If casting the object to a Component2 object fails,
we know it is a Feature.
175
Traversing
Pretty good hu? That’s not all. We are now going to add the ability to
toggle the suppression state of the components and features using a
right-click menu to our list. This can then be easily expanded to call
any function or do any task you would like on any component or
feature within an assembly or part.
Go back to the Form Designer and this time we are going to drag a
new ContextMenuStrip from the toolbar to the form.
You will then notice at the bottom of the Form Designer that you
have your newly created menu called ContextMenuStrip1. If you
single-click on this the menu designer will appear at the top of your
form. To add a new menu item you single-click the box where it
states “Type Here”, type the name of the menu, and press enter.
176
Traversing
Add a new menu item called “Toggle Suppression”, and in order to
add an event function for OnClick, just double-click the menu item.
177
Traversing
Now it’s time to implement our suppression function. Within the
event function for the menu click that we created a minute ago place
the following code. This is mostly identical with the previous code
but where the Select coding is, we replace it with the suppression
toggle code:
C#
if (treeView1.SelectedNode == null)
return;
if (hashStore.Contains(treeView1.SelectedNode))
{
object link = hashStore[treeView1.SelectedNode];
if (treeView1.SelectedNode.Index != 0)
{
try
{
Component2 comp = (Component2)link;
int state = comp.GetSuppression();
if (state ==
(int)swComponentSuppressionState_e.swComponentSuppressed)
state =
(int)swComponentSuppressionState_e.swComponentFullyResolved;
else
state =
(int)swComponentSuppressionState_e.swComponentSuppressed;
comp.SetSuppression2(state);
}
178
Traversing
catch
{
Feature feat = (Feature)link;
Boolean[] suppression =
(Boolean[])feat.IsSuppressed2((int)swInConfigurationOpts_e.swThisConf
iguration, null);
int state;
if (suppression[0])
state =
(int)swComponentSuppressionState_e.swComponentFullyResolved;
else
state =
(int)swComponentSuppressionState_e.swComponentSuppressed;
feat.SetSuppression2(state,
(int)swInConfigurationOpts_e.swThisConfiguration, null);
}
}
}
179
Traversing
configurations. Since we are only interested in the current
configuration, we just check the first item in the returns array. Again,
if it is already suppressed we unsuppress it, and if it is unsuppressed
we suppress it. With the state defined, we set it using the function
SetSuppression2 just like the component.
Test the program, and this time you can select an object first by left-
clicking, and then right-click to show the context menu, and click
Toggle Suppression to toggle the suppression state of the item.
Here endith the lesson. You should now have a good understanding
of the traversing process, as well as some of the functions and
methods you can use upon components and features. You should be
able to easily expand this program to do anything you please.
180
Custom Property Manager
181
Custom Property Manager
Using the Custom Property Manager is fairly simple; once you have
the CPM, you can either Add, Delete or Update a custom property in
either the Custom Property or the Configuration Specific Property.
Once you have these basics down, we will create a simple user
interface for letting the user modify properties in the active
document.
Retval = ModelDocExtension.CustomPropertyManager (
ConfigName )
C#
CustomPropertyManager cpm;
182
Custom Property Manager
VBA
Then all that is left is to call the function to get the manager from
any ModelDoc2 object:
cpm = swModel.Extension.get_CustomPropertyManager("");
VBA
cpm = swModel.Extension.get_CustomPropertyManager("Default");
183
Custom Property Manager
VBA
C#
VBA
184
Custom Property Manager
o swCustomInfoUnknown
o swCustomInfoText
o swCustomInfoDate
o swCustomInfoNumber
o swCustomInfoYesOrNo
o swCustomInfoDouble
In the user interface for the custom properties the user can only
select 4 options (Text, Date, Number, Yes/No). The Double
185
Custom Property Manager
option is simple a non-whole number, and the Unknown option
is for errors.
The FieldValue is the Value / Text Expression field that the user
sees, not the Evaluated Value.
C#
VBA
We then just check this iRet value for true (1) or false (0):
C#
if (iRet == 0)
// Failed
else
// Succeeded
186
Custom Property Manager
VBA
If iRet = 0 Then
‘ Failed
Else
‘ Succeeded
End If
Another quick note: the Add2 function will return a failure if the
property already exists in the first place.
187
Custom Property Manager
C#
VBA
C#
if (iRet == 0)
// Failed
188
Custom Property Manager
else
// Succeeded
VBA
If iRet = 0 Then
‘ Failed
Else
‘ Succeeded
End If
Note: This function will return a failure if the custom property did not
exist in the first place, so before we move on to updating custom
properties let’s take a look at how to check if one already exists.
189
Custom Property Manager
C#
VBA
190
Custom Property Manager
C#
getnames = (string[])cpm.GetNames();
if (getnames != null)
foreach (string prop in getnames)
propnames.Add(prop.ToUpper());
191
Custom Property Manager
You may notice that I add and check the names with a function after
them called ToUpper. This function converts all letters to uppercase
because the Contains function is case sensitive, but the CPM is not,
so by them both uppercase it removes any case mismatch.
VBA
getnames = cpm.getnames()
bFound = False
192
Custom Property Manager
Here we have done things slightly different; instead of using a list,
we have simply checked the results as we go, and the end result is in
the bFound variable.
You can bundle this code up into a nice function to return true or
false, you will do this later.
Then to properly check for a deleted property you can first check if it
exists or not first.
193
Custom Property Manager
The FieldValue is the new value you want to give the property.
This is the Value / Text Expression field in the image.
C#
194
Custom Property Manager
VBA
If you want to get the original value before overwriting it use the
Get2 function we went over previously.
195
Custom Property Manager
This tool will connect to the active SolidWorks running, get the
active document, and if it is an Assembly or Part, do the following:-
Then, with that information, when the user clicks the button, the
tool will go through every configuration specific CPM object, check
all of the custom properties looking for the Property Name specified
for the user, and if its value contains the Value that the user entered,
add the results to the TreeView control.
196
Custom Property Manager
Change the other to:
“Search for:”
tbConfigProperty
tbFind
197
Custom Property Manager
Set the (Name) property of the TreeView control to:
tvList
C#
try
{
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch
{
MessageBox.Show("Error getting SolidWorks Handle");
return;
}
swModel = (ModelDoc2)swApp.ActiveDoc;
if (swModel == null)
{
MessageBox.Show("Failed to get active document");
return;
198
Custom Property Manager
}
if (swModel.GetType() == (int)swDocumentTypes_e.swDocDRAWING)
{
MessageBox.Show("Active document cannot be a drawing");
return;
}
C#
tvList.Nodes.Clear();
As you can see the first line is a call to our TreeView control that we
called tvList. By accessing the Nodes object we can call the function
Clear, to remove any nodes in the list.
We then gather the information from the TextBox controls using the
Text property of them which returns the text that the user has typed
in.
The third variable value will be used to store the original value of the
custom property we retrieve later, before comparing it the what the
user wants to find.
199
Custom Property Manager
We continue with a few more variables and retrieving all of the
configuration names for this document, so that we can check each
one later:
C#
Configuration config;
CustomPropertyManager cpm;
string[] modelconfigs = (string[])swModel.GetConfigurationNames();
No explanation needed here. Now for the main loop; don’t try to
understand it all straight away I will take you through it:
C#
if (modelconfigs != null)
foreach (string s in modelconfigs)
{
config = (Configuration)swModel.GetConfigurationByName(s);
cpm = config.CustomPropertyManager;
if (value.ToUpper().Contains(searchFor.ToUpper()))
AddConfig(cpm, s);
}
200
Custom Property Manager
Then we loop through every name in the list, and for each name we
acquire its Configuration using the ModelDoc2 function
GetConfigurationByName. This takes the name of the
configuration as a parameter:
With the Configuration object we can then acquire the CPM we have
been long waiting for by accessing it through the Configuration
object.
The only thing left to do is get the current custom property value for
the field we are after, and to check whether the value we are
searching for is contained within it. As you can see we get the value
using a function called GetCustomProperty which accepts a
CustomPropertyManager object and a string. This is not a built-in
function it’s one you will create in a moment.
With the value of the field the user was after, we check whether it
contains the string the user is searching for. To do this we must cast
both the original string and the one we are looking for to uppercase
to remove any case-sensitivity (such as ‘A’ = ‘a’ being false), unless
you want case-sensitivity. With uppercase values we then call the
string function Contains, which returns true or false if a match is
found.
201
Custom Property Manager
Finally, if the property value the user specified does contain the value
they were searching for, we want to add this configuration to our list
to show that it was found. Again we do this with a function called
AddConfig that takes a CustomPropertyManager object, and a
string. You will create this function as well as the
GetCustomProperty function now.
GetCustomProperty function
You have actually written this function before! If you go back several
pages to near the beginning of this chapter to the section Check
Custom Property Existence, you will find the following:
C#
getnames = (string[])cpm.GetNames();
if (getnames != null)
foreach (string prop in getnames)
propnames.Add(prop.ToUpper());
202
Custom Property Manager
}
else
// NOT FOUND
C#
if (cpm != null)
{
getnames = (string[])cpm.GetNames();
if (getnames != null)
foreach (string prop in getnames)
propnames.Add(prop.ToUpper());
}
203
Custom Property Manager
if (propnames.Contains(fieldName.ToUpper()))
{
cpm.Get2(fieldName, out theval, out thevalres);
}
else
theval = "";
return theval;
}
If the property we are looking for doesn’t exist, we just return a blank
string; else it calls the Get2 function I described in the previous
section to get the value of the property. The function returns the
theval variable to the caller.
AddConfig function
The last step is to actually display the results once they have been
found. For this we want to show each configuration that matches the
search criteria, and as a bonus we will then add every configuration
specific property to the tree view node of that configuration:
C#
204
Custom Property Manager
getnames = (string[])cpm.GetNames();
if (getnames != null)
foreach (string prop in getnames)
tn.Nodes.Add(prop + ": " + GetCustomProperty(cpm, prop));
}
With the TreeNode added, we then check whether the CPM object is
valid or not, and if it is we get all of the property names from the
CPM (all of the configuration specific properties), and then add a
child node for each property showing its name and value.
205
Custom Property Manager
And that is pretty much it in a nutshell! Compile and run your
program and with SolidWorks open and an assembly or part open
test it out.
As usual find the complete source code on the CD. This tool is only
made it the .Net languages.
206
Working with Drawings
Counting Views
207
Working with Drawings
We have focused mostly on parts and assemblies so far, so let’s take
a look at some things to do with drawings. We will start by creating a
macro/program that automatically creates drawing sheets from
parts and assemblies, and move on to analysing features of the
drawing and more.
The first thing we want to do when the user clicks the button or runs
the macro is to ask the user for the part or assembly to create a new
drawing from. In .Net we can do this using an OpenFileDialog, but in
VBA we must use a much more basic technique of having the user
type in. We could import Windows API calls from the comdlg32.dll,
but that’s too much to explain and would veer off the SolidWorks
programming topics.
C#
208
Working with Drawings
Before that we set the Filter property, which will limit the user to
selecting only the file formats we want them too. This filter follows
the format of starting with the name which will be displayed to the
user, separated by a pipe (|) then the actual regex string to match file
types, separated by semicolons (;).
Notice the filter list that we typed in and how it has limited the files
we can see to assemblies and parts.
209
Working with Drawings
If the user clicked cancel we will just ignore this and end our function,
but if they selected a file we will create a new variable to store this
value:
C#
if (filename == "")
return;
VBA
210
Working with Drawings
The InputBox function of VBA creates this little input box for us, so it
will do for now.
Unlike .Net, VBA has not prevented the user from selecting specific
files, nor whether the file even exists, so we must do this now:
VBA
211
Working with Drawings
End Function
This function will return True if a file or directory exists, and False if
not, so now we do this:
VBA
212
Working with Drawings
C#
string drwTemplate =
swApp.GetUserPreferenceStringValue((int)swUserPreferenceStringValu
e_e.swDefaultTemplateDrawing);
VBA
213
Working with Drawings
With the template name acquired we can now create a new drawing
using the following function of the SldWorks object. We actually
used this function back at the very beginning:
Only this time we will be using the paperSize, width and height
parameters. We will acquire all of these from the template name we
just got using another function:
214
Working with Drawings
Let’s get the sizes to complete the equation:
C#
if (sizes == null)
{
MessageBox.Show("Failed to get valid template sizes.");
return;
}
VBA
If IsEmpty(sizes) Then
MsgBox "Failed to get valid template sizes."
Exit Sub
End If
We also check that we managed to get the sizes before the next
step, as this will fail if the template is corrupt or has invalid data, or
does not exist.
With the template name, paper size, width and height, we call the
function NewDocument to create the new drawing sheet we are
after, and store the returned handle to a new variable to access later
for adding views to.
215
Working with Drawings
C#
DrawingDoc swDrawing =
(DrawingDoc)swApp.NewDocument(drwTemplate, (int)sizes[0], sizes[1],
sizes[2]);
VBA
C#
if (swDrawing == null)
{
MessageBox.Show("Failed to create new drawing document.");
return;
}
216
Working with Drawings
VBA
retval = DrawingDoc.Create3rdAngleViews2 (
modelName)
C#
if (!bRet)
217
Working with Drawings
{
MessageBox.Show("Failed to add 3 common views.");
return;
}
VBA
218
Working with Drawings
Counting Views
Another off the shelf random exercise for you now; this time we are
going to find out how many drawing views a drawing has in each
sheet. This may not seem of much use, but this demonstrates how to
access handles to drawing views to do what you please with. We will
be displaying the drawing view types and names in this example.
Start with the usual template, and get the active document, check
that it is a drawing and proceed.
219
Working with Drawings
If you remember some chapters back where we created the
program/macro to loop all drawing sheets and save them as DXF, we
will be using the same coding here to loop through each sheet, and
then within that loop get our information:
C#
GetSheetInformation(swDrwSheet);
}
The only difference here is that we clear our TextBox control of any
text before we begin so that any text already in is removed, and we
have added an extra variable called swDrwSheet, which is a Sheet
object. Once each sheet is activated, we get a handle to it using the
function GetCurrentSheet. With this variable we can then do what
we please with the sheet, so to keep things clean and tidy we pass
this Sheet object to a new function that will do our dirty work, called
GetSheetInformation. Place your function below the button event
function:
220
Working with Drawings
C#
GetSheetInformation(swDrwSheet,
(SldWorks.View)swDrwDoc.GetFirstView());
}
}
I have omitted some code; this is just to show you where to place
your function.
The function takes the Sheet object for obvious reasons, and a View
object that will be the first view of the sheet so that we can loop
221
Working with Drawings
through them all within the function, gather the information, and
add that info to our TextBox control.
C#
int iCount = 0;
string nl = System.Environment.NewLine;
string tab = " - ";
iCount++;
firstView = (SldWorks.View)firstView.GetNextView();
}
222
Working with Drawings
Next we loop every view within this sheet by checking if the current
view is null or not, and if it isn’t process it and then call the View
function GetNextView; this will return the next view of its parent
Sheet object, or null if it has reached the end.
For each view we then get the type of view we are dealing with by
converting the integer returned from the Type property of the View
object back to an enumerator variable of type
swDrawingViewTypes_e, and then back into a readable string like
we have done in previous examples.
With a readable type value, we then add the views’ name and type to
the text. Notice we have added the tab variable to the start; this
creates the effect of indenting our entries to show it is part of the
sheet.
Once the details have been added we increment the view count and
loop.
Finally when all views have been processed we want to tell the user
how many views there were on this particular sheet. All we need to
do for this is to show the user the iCount value:
With this being the final line of this sheet that we are adding to the
TextBox, we add two new lines instead of one to give it a clean
break from the next sheet.
Compile and test your program and take a look at the results:
223
Working with Drawings
You will notice here that the first view of all sheets is actually the
sheet itself; this is because the structure of the SolidWorks sheet is
basically just a bunch of View objects embedded inside other View
objects. If you want to exclude this item from the list just check the
Type value, and if it is the type swDrawingSheet, skip it.
Now onto the VBA version of this little tool; start with the usual
template. Insert a new User Form from the Insert menu and add a
CommandButton and a TextBox to the form.
224
Working with Drawings
Position them so they look something like this:
Double-click the button to add the button Click event function. Move
the usual code (for connecting to SolidWorks, getting the active
document and checking if it is a drawing) from the main function to
this Click event function, and within the main function place this
code to show the form:
VBA
Sub main()
UserForm1.Show
End Sub
Back to the event function below the code we just pasted in, we add
the same code I described above in the .Net version.
225
Working with Drawings
VBA
TextBox1.Text = ""
swDrwDoc.ActivateSheet sheetname
Set swDrwSheet = swDrwDoc.GetCurrentSheet()
Next sheetname
I will leave out the code line of acquiring the type value and show
that separately.
226
Working with Drawings
VBA
iCount = iCount + 1
Set firstView = firstView.GetNextView()
Wend
TextBox1.Text = TextBox1.Text & "Total views: " & iCount & vbCr & vbCr
End Sub
227
Working with Drawings
Notice the TODO note within the code. This is the like where we
statically convert the integer value of the Type property of the View
object to a user-friendly readable string variable.
VBA
viewType = Switch(
iType = swDrawingViewTypes_e.swDrawingAlternatePositionView,
"Alternate Position View",
iType = swDrawingViewTypes_e.swDrawingAuxiliaryView, "Auxiliary
View",
iType = swDrawingViewTypes_e.swDrawingDetachedView, "Detached
View",
iType = swDrawingViewTypes_e.swDrawingDetailView, "Detail View",
iType = swDrawingViewTypes_e.swDrawingNamedView, "Named View",
iType = swDrawingViewTypes_e.swDrawingProjectedView, "Projected
View",
iType = swDrawingViewTypes_e.swDrawingRelativeView, "Relative
View",
iType = swDrawingViewTypes_e.swDrawingSectionView, "Section
View",
iType = swDrawingViewTypes_e.swDrawingSheet, "Sheet",
iType = swDrawingViewTypes_e.swDrawingStandardView, "Standard
View")
All we have done here is done a Switch function to select the correct
value from the list. This function was explained in a previous chapter,
but basically it works by returning the variable after the first value
that returns True; so in this case, we check if “iType =
swDrawingDetailView”, if it does it returns the variable after that,
which is “Detail View”, if it is false, it carries on until it finds one.
228
Working with Drawings
You may notice
that the TextBox
object can
actually be
edited by the
user simply by
typing into it.
This is not really
a problem but
technically it is
not good practise
for a program to
allow the user to do anything they are not meant to be doing. If you
want to fix this all you need to do is go back to the Form Designer
and alter the Locked property to True. In .Net the property is called
Readonly.
229
Working with Drawings
I will start by describing the functions we are going to use, and then
we will go ahead and implement them into a nice little package.
The two options we have here are to print the document with one
line of code, that prints the document to the last printer it was
printed to, or the option to specify all details such as printer name.
Starting with the simplest method:
PrintDirect function
This function of the ModelDoc2 object takes no parameters and
returns no value, so it couldn’t be simpler. Once you have a
ModelDoc2 handle (or an AssemblyDoc, PartDoc or DrawingDoc to
cast back to), you can call this function:
void ModelDoc2.PrintDirect ()
Although this is nice and simple, that is pretty much the last thing
you want when you are printing something; it provides no options for
printing multiple copies, printing sheet ranges or printing to a
specific printer.
230
Working with Drawings
PrintOut2 function
If you require more control over your printing then this is the
function for you.
231
Working with Drawings
This function is part of the ModelDocExtension object, not the
ModelDoc2 object like the PrintDirect function, so in order to call it
just do the following (presuming swModel is a ModelDoc2 variable):
swModel.Extension.PrintOut2(...)
Although you can use this function to print any type of document,
the following example will be to print drawing sheets only.
This will allow the user to select multiple items from the list.
232
Working with Drawings
Next, change the DropDownStyle of the ComboBox control:
This will prevent the user from editing the list as we want them to be
fixed to the list here. Name the buttons so there Text value is as
shown, and add a Click event handle to each.
C#
using System.Drawing.Printing;
C#
SldWorks.SldWorks swApp;
ModelDoc2 swModel;
string[] printers;
This will store our printer list. Within the constructor function add
the following:
233
Working with Drawings
C#
public Form1()
{
InitializeComponent();
printers = new string[PrinterSettings.InstalledPrinters.Count];
PrinterSettings.InstalledPrinters.CopyTo(printers, 0);
comboBox1.Items.AddRange(printers);
comboBox1.SelectedIndex = 0;
}
With the array gathered, we load the list into our ComboBox control
and select the first item in the list by default.
A quick note here; I have not added error checking for if there are no
printers at all installed on the computer; if there are not this program
will just crash. I will leave the error-checking task up to you as a
simple test of what you have learned so far, so if you can manage it.
234
Working with Drawings
C#
try
{
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch
{
MessageBox.Show("Error getting SolidWorks Handle");
return;
}
swModel = (ModelDoc2)swApp.ActiveDoc;
if (swModel == null)
{
MessageBox.Show("Failed to get active document");
return;
}
235
Working with Drawings
if (swModel.GetType() != (int)swDocumentTypes_e.swDocDRAWING)
{
MessageBox.Show("Active document must be a drawing");
return;
}
listBox1.Items.AddRange(sheetNames);
}
So far when we start our program (feel free to at this point), the
ComboBox control will populate with all printer names installed on
the computer for the user to select, and upon clicking the “Get
Sheets” button the ListBox control will populate with all sheet
names of the currently active drawing allowing the user to select one
or more sheets which to print. So, all that is left for us to do now is to
print the selected sheets from the list to the selected printer from
the combo box.
236
Working with Drawings
C#
Not too much to do here. To get the user selection from the ListBox
control we access its property SelectedItems. We check how many
items have been selected, and if none have, exit.
Next, loop every item the user selected, but this time we use the
property SelectedIndices. The reason for this is because as shown
above using the PrintOut2 function requires page numbers, not
sheet names. The trick here is that the GetSheetNames function we
used to retrieve the sheet names in the first place retrieves them in
the page number order, so this works a treat. We add the 1 to the
237
Working with Drawings
position because the SelectedIndicies is a 0-based array, whereas
page numbers as 1-based arrays (starting at 1, not 0).
That is all there is to it. Run your program, start by getting the sheet
names, then select one or more (using the Shift and/or Ctrl keys) and
then click the “Print!” button to print the sheets!
Now onto the VBA version; everything is exactly the same as the
.Net version except for small differences in acquiring the printer list
and the likes.
238
Working with Drawings
VBA
Sub main()
UserForm1.Show
End Sub
We need to change
a few properties;
select the ListBox
control and change
the property
MultiSelect to
MultiExtended so
the user can select
more than one item
from the list.
239
Working with Drawings
VBA
240
Working with Drawings
The final task for this is to create a function that will use these calls
we declared to get a list of all printers installed on the computer and
return a simple array to the user. We do this like so:
VBA
iBufferSize = 3072
'EnumPrinters will return a value False if the buffer is not big enough
241
Working with Drawings
bSuccess = EnumPrinters(PRINTER_ENUM_CONNECTIONS Or _
PRINTER_ENUM_LOCAL, vbNullString, _
1, iBuffer(0), iBufferSize, iBufferRequired, iEntries)
242
Working with Drawings
End If
ListPrinters = StrPrinters
End Function
Within this Activate function add the following code to call the
function we created just and fill the ComboBox with the printer list:
VBA
StrPrinters = ListPrinters()
243
Working with Drawings
'Fist check whether the array is filled with anything, by calling another
function, IsBounded.
If IsBounded(StrPrinters) Then
For x = LBound(StrPrinters) To UBound(StrPrinters)
ComboBox1.AddItem StrPrinters(x)
Next x
ComboBox1.ListIndex = 0
Else
MsgBox "Failed to get printer list"
End If
End Sub
All we do is create a new Variant variable to store our printer list and
call the ListPrinters function. We check that the list is actually a valid
array by calling a IsBounded, a function yet to be defined. If OK we
loop through all items and add them to the ComboBox control.
VBA
244
Working with Drawings
VBA
245
Working with Drawings
ListBox1.Clear
End Sub
Now we are at the point where the user can get the sheet names and
select a printer, and all that is left to do is to print the selected
sheets.
246
Working with Drawings
VBA
Dim i As Integer
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
Dim pages(0) As Long
pages(0) = i + 1
swModel.Extension.PrintOut2 pages, 1, False, ComboBox1.Text, ""
End If
Next
End Sub
We start with a For loop to loop all of the items within the list. Within
the loop we check whether that item is selected, and if it is we create
a new variable for the page numbers and call the PrintOut2 function.
The difference with the VBA call to the PrintOut2 function is that it
doesn’t 2 items in the page array when printing a single page, it takes
just one. Other than that everything else is the same. Give it a try!
247
Working with Drawings
248
Add-ins
249
Add-ins
I was stuck with the decision of the last chapter either covering file
importing and exporting with Excel, XML and ini files, or a quick
brush over of add-ins; my choice was the latter as I feel many of you
will find it harder to create an add-in on your own without a guide
than you will to work with files as there are plenty of tutorials for you
on file input/output. OK, so let’s get straight to it.
When you open this solution file you will notice your project has
several items in the Solution Explorer:
250
Add-ins
menus; we will not touch this coding in this example, only use its
functions.
The 2 image files are used for our menu icons, and are basically
16x16 icons (small) and 24x24 icons (large) lines up horizontally to
create a strip.
The only file remaining is the main add-in code file that we will be
using called MyFirstAddin; this contains all the important code I am
going to explain.
Open the
MyFirstAddin
file and you will
see something
similar to the
image to the left.
251
Add-ins
C#
ISldWorks iSwApp;
ICommandManager iCmdMgr;
int addinID;
C#
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type t)
{
Microsoft.Win32.RegistryKey hklm =
Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey hkcu =
Microsoft.Win32.Registry.CurrentUser;
keyname = "Software\\SolidWorks\\AddInsStartup\\{" +
t.GUID.ToString() + "}";
addinkey = hkcu.CreateSubKey(keyname);
252
Add-ins
addinkey.SetValue(null, 0);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type t)
{
//Insert code here.
Microsoft.Win32.RegistryKey hklm =
Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey hkcu =
Microsoft.Win32.Registry.CurrentUser;
keyname = "Software\\SolidWorks\\AddInsStartup\\{" +
t.GUID.ToString() + "}";
hkcu.DeleteSubKey(keyname);
}
All that these two functions do is to add and delete a simple registry
entry containing details of your add-in, so that SolidWorks knows
where to look to load your file, that’s it. An entry looks like this:
253
Add-ins
C#
public MyFirstAddin()
{
}
//Setup callbacks
iSwApp.SetAddinCallbackInfo(0, this, addinID);
254
Add-ins
return true;
}
iSwApp = null;
GC.Collect();
return true;
}
255
Add-ins
The DisconnectFromSW function is called when SolidWorks is
closing, so we gracefully remove any items we added to the
SolidWorks interface such as our command menu.
C#
thisAssembly =
System.Reflection.Assembly.GetAssembly(this.GetType());
cmdGroup.LargeIconList =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarLarge.bmp"
, thisAssembly);
cmdGroup.SmallIconList =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarSmall.bmp"
, thisAssembly);
cmdGroup.LargeMainIcon =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarLarge.bmp"
, thisAssembly);
256
Add-ins
cmdGroup.SmallMainIcon =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarSmall.bmp"
, thisAssembly);
cmdGroup.Activate();
}
257
Add-ins
handle to ourselves (the assembly itself), so that we can access the
images embedded within it (i.e. the images in our Solution
Explorer).
thisAssembly =
System.Reflection.Assembly.GetAssembly(this.GetType(
));
This line is used to get the handle to ourself by using the keyword
this.
The end goal here is to create a few menu items, but before we can
have a menu item, we need a menu to store them in. As we are using
a Command Manager we do not add a menu, but a group. This
group will act as a menu as well as a toolbar if you choose to have
one.
LpGroup = CommandManager.CreateCommandGroup (
UserID, Title, Tooltip, Hint, Position)
258
Add-ins
The Title is the name that will appear on the menu or toolbar.
For example, the Title values for the standard SolidWorks
menus are File, View, Tools, Help.
The Tooltip is the little tip that pops up in a yellow box when the
user hovers over the item.
The Hint is the same thing as the Tooltip only this text appears
in the SolidWorks status bar at the bottom of SolidWorks when
the user hovers over the item.
The Position is the position that your menu will appear in the
main menu, stating at 0 for the beginning.
cmdGroup = iCmdMgr.CreateCommandGroup(1,
"MyFirstAddin", "This is my add-in", "", -1);
As you can see, the unique ID we assign to our group is just the
number 1. We call our group “MyFirstAddin”, and the tooltip as “This
is my add-in”, and nothing for the hint. We position it using -1 so it
gets positioned by default at the end of the menu items.
259
Add-ins
C#
cmdGroup.LargeIconList =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarLarge.bmp"
, thisAssembly);
cmdGroup.SmallIconList =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarSmall.bmp"
, thisAssembly);
cmdGroup.LargeMainIcon =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarLarge.bmp"
, thisAssembly);
cmdGroup.SmallMainIcon =
iBmp.CreateFileFromResourceBitmap("MyFirstAddin.ToolbarSmall.bmp"
, thisAssembly);
260
Add-ins
The Name is the name that will appear in the menu or toolbar,
such as the menu item of the File group for opening a file is called
“Open...”.
The Position is again the position of item, this time in the group;
zero-based index.
The ToolTip is the tip that appears in the yellow box when the
user hovers over the item.
261
Add-ins
The EnableMethod is again the name of a function to call, but
this time this function is called before the item gets displayed. It
specified whether to enable or disable the item.
The UserID is the unique identifier if this item for this group. It
only has to be unique for this group and is not needed; to ignore
it pass 0.
The function returns the index position the menu item has been
placed in the group.
cmdGroup.AddCommandItem2("Start my external
program", 0, "Click me", "Click me", 0,
"StartExternalProgram", "", 0,
(int)(SwConst.swCommandItemType_e.swMenuItem |
SwConst.swCommandItemType_e.swToolbarItem));
262
Add-ins
We ignore the UserID as well by passing in 0, and we tell the item to
appear in both the toolbar and menu controls.
Now with our menu item created all that is left is to activate the
group.
cmdGroup.Activate();
For this example however I will be creating the highly requested code
for running an external program or SolidWorks macro. The code is 2
short and sweet lines:
C#
263
Add-ins
wish to start. Because Notepad is within a system folder it is found
automatically without the need for its full location.
In our example we use a macro that looks like the picture above, and
as you can we passed in “Macro1” as the module name, and “main”
as the function.
264
Add-ins
Now compile your program making sure that SolidWorks is shut
down and upon successfully building your project VS will inform you
that your project cannot be run directly. Do not worry, your project
has already run the SolidWorks Registration section code and
added itself to the windows registry ready for SolidWorks to find on
next load.
265
Add-ins
As you can see the tooltip shows the add-in file location (which is our
project output folder location), the title and description we set.
Check the box to the left to load our add-in and click OK.
You will now notice that you have a new menu group and item,
named by the name that we gave it in our code, as well as a toolbar
that you can select to show as well:
266
Add-ins
containing all toolbars. In that menu you will see your new add-in.
Select it to show it:
One final note; every add-in you make must have a unique GUID
code at the top:
[Guid("07B6F2FD-74F6-4DEB-BBED-F7AAA0976FB1")]
public class MyFirstAddin : SWPublished.ISwAddin
267
Add-ins
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\Addins
268