SolidWorks API Series 1 - Programming & Automation PDF
SolidWorks API Series 1 - Programming & Automation PDF
Published by AngelSix
Second Edition
Trademark Information
SolidWorks and PDMWorks are registered
trademarks of SolidWorks Corporation.
Excel is a registered trademark of Microsoft
Corporation.
Other brand or product names are
trademarks or registered trademarks of their
respective holders.
Contributors
Throughout this project I have received many enthusiastic responses
and people contributing their own ideas and knowledge to areas of
this book. It has been a great experience and an eye opening one.
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.
It is this drive for deeper understanding that has pushed me in every
aspect of my life, to fully understand computers, electronics, people,
and ultimately the universe. Whenever I come across something I do
not understand, I take it upon myself to learn; learn through
observation, through trial-and-error, through others, through
experience. So far I would like to think I have succeeded in all that
which I set out to understand.
I have always had a unique way of thinking when it comes to
computers and logic; I find most people learn programming through
the process of finding a solution and remembering the answer, such
as knowing that COS(1) = 0.5403, yet they do not know that
.
Whereas I feel most people simply remember coding, I like to fully
understand it, to the point where I could tell you what would happen
with code before even compiling it; I often write 1000s lines of code
before even compiling and testing it, as I already have in my head the
architecture and theory of what is going to be output, and I simply
translate my thoughts into writing.
By writing this book I hope to pass on my understanding of
computers and programming to the next generation.
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
Recording your first macro
Writing a macro from scratch
11
The Basics
12
The Basics
you need to know is that the panel on the left is where you will find
your macros 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, lets 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:
Run-time error 91:
Object variable or With block variable not set
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:
Dim swApp As Object
Dim Part As Object
Sub main()
Set swApp = Application.SldWorks
Set Part = swApp.NewDocument("C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0#, 0#)
Part.SaveAs2 "C:\Users\luke\Desktop\Part1.SLDPRT", 0, False, False
End Sub
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
Understanding the code
Now we have a working macro that creates a new part and saves it to
a location. Lets now take a closer look at the code that did this and
understand it.
Firstly, let me explain how macros interact with SolidWorks; in
programming, assemblies can make themselves visible to other
programs running on the system. This is called COM-Visibility.
Basically this allows us to create an instance of the SolidWorks
program (new or currently running) and access any functions that it
makes visible to us. So, the first stage of any macro or program is to
get an instance of SolidWorks to work with. This is done on this line:
Set swApp = Application.SldWorks
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.
Variables are things such as numbers, Booleans (true, false), arrays,
custom objects; this book presumes you have enough knowledge to
know about variables, functions and conditional statements, if not
search google, there are plenty of tutorials and resources available.
Dim swApp As Object
Dim Part As Object
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:
Set swApp = Application.SldWorks
17
The Basics
Dont 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.
Part.SaveAs2 "C:\Users\luke\Desktop\Part1.SLDPRT", 0, False, False
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 doesnt
return a value, just that we are not making any use of it on this call.
Again, the SaveAs2 function takes 4 parameters; this first is the
name and location of where to save the file. The other 3 I shall cover
later.
And that is it; you have just created your first SolidWorks Macro!
18
The Basics
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.
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:
Set swApp = GetObject(, "SldWorks.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.
Now its time to create a new part; in order to better understand
what we need to do to create a part, go back to SolidWorks, leaving
VBE open, and from the menu select Help->API Help, to open up the
reference documentation for SolidWorks API calls. This comes in
handy and is almost always the starting point for any new task.
In the help file, go to the search tab and type in NewDocument
without quotations. In the results to the left double-click
SldWorks::NewDocument. The help file simply uses a double colon
instead of a period to signify that the right hand side is a method or
property of the object to the left.
Reading the help gives us a brief description of what the
NewDocument function does and requires. The help shows that the
function takes 4 parameters:
SldWorks.NewDocument ( templateName, paperSize,
width, height )
22
The Basics
Now lets 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.
Set swPart = swApp.NewDocument("C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0)
And finally, lets 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.
Retval = ModelDocExtension.SaveAs ( Name, Version,
Options, ExportData, Errors, Warnings)
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 (Longs) 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
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.
Putting all this together, with error checking, we get:
Dim bRet As Boolean
Dim lErrors As Long
Dim lWarnings As Long
bRet = swPart.Extension.SaveAs("C:\Part1.SLDPRT",
swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, lErrors,
lWarnings)
If bRet = False Then
MsgBox "Error saving new part"
Exit Sub
End If
The Basics
Dim swApp As SldWorks.SldWorks
Dim swPart As ModelDoc2
Sub main()
Set swApp = GetObject("", "SldWorks.Application")
If swApp Is Nothing Then
MsgBox "Error gettings SolidWorks Handle"
Exit Sub
End If
Set swPart = swApp.NewDocument("C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0)
If swPart Is Nothing Then
MsgBox "Error creating new part"
Exit Sub
End If
Dim bRet As Boolean
Dim lErrors As Long
Dim lWarnings As Long
bRet = swPart.Extension.SaveAs("C:\Part1.SLDPRT",
swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, lErrors,
lWarnings)
If bRet = False Then
MsgBox "Error saving new part"
Exit Sub
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
29
30
Connect to SolidWorks in C#
When you first open C# Express you are displayed with the following
screen:
31
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
33
namespace ConntectingWithCS
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
Lets go through each line of this code again so that you understand
what is going on.
The only lines that can be outside of a class in VS are special
declarations such as #Regions, pre-processor tags, namespaces
and using statements. The only one we are concerned about is the
using statement.
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
36
37
38
And then finally, at the top of the code where the using statements
are, add:
using SldWorks;
39
40
swModel;
...
private void button1_Click(object sender, EventArgs e) { ... }
}
For best practise in the future I will be writing the main variables in
the main class not the event handler.
Declaring a variable in C# is a little different than VBA; you simply
state the type first, followed by the name. As you can see, the two
variables specified above are the same types of the ones we wrote in
VBA.
With these variables defined, we move on to the main SolidWorks
code. Take a look at the original VBA code; see the slight changes:
41
Now, lets do exactly this but in C# within our event handler button
function.
42
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
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
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.
Also, we have done another casting from object to ModelDoc2 as
our variable is of type ModelDoc2.
45
46
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
47
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.
A few differences this time compared to the VBA version. In VBA, to
represent nothing we pas the special name Nothing, in C# the
equivalent is null.
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
49
swModel;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
swApp =
(SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch
{
MessageBox.Show("Error getting SolidWorks Handle");
return;
}
swModel = (ModelDoc2)swApp.NewDocument(@"C:\Program
Files\SolidWorks\data\templates\Part.prtdot", 0, 0, 0);
50
One thing you may notice is that C# requires every statement line to
end with a semi-colon (;).
51
52
Place the variables inside the main class function, not the button
click function. You entire code so far should look like this:
53
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
And thats 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.
Important Note: All examples on the CD are compiled with
references to SolidWorks 2008 SP3.0. If for any reason you get
errors when building them, remove and re-add your SolidWorks
references and try again. This is a common problem for most people
when using examples from another machine.
56
57
58
59
60
62
VBA
Dim sheetNames As Variant
sheetNames = swModel.GetSheetNames()
Dim saveAsLocation As String
Dim sheetname As Variant
For Each sheetname In sheetNames
' Activate and save drawing sheet here
Next sheetname
63
VBA
Dim lErrors As Long, lWarnings As Long
lErrors = lWarnings = 0
64
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.
Then, we call the SaveAs function just as we did in the previous
chapter, only this time instead of specifying a name ending in
.sldprt, we end it with .dxf. SolidWorks will automatically save
the file as a DXF without you needing to do anything else.
65
66
Run your code and watch as the drawing sheet flicks between each
drawing sheet and saves as a DXF.
Note: At the time of
writing, SolidWorks still
has a bug in setting the
DXF properties to save
only the active sheet and
not all when we run the
SaveAs command, so I
left out the code for
setting up the DXF
options to save just the
active sheet. If your
output DXF files have all
the sheets within them, just go to manually save a drawing, select
DXF, and then click the Options button and make sure the last
setting is selected as Active Sheet Only.
67
C#
string fullpath = swModel.GetPathName();
string filelocation = Path.GetDirectoryName(fullpath);
string filename = Path.GetFileNameWithoutExtension(fullpath);
string extension = Path.GetExtension(fullpath);
68
69
70
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
VBA
Dim bReadOnly As Boolean
Dim bViewOnly As Boolean
Dim bLargeMode As Boolean
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#
string materialName = swModel.MaterialUserName;
string materialID = swModel.MaterialIdName;
72
VBA
Dim materialName As String
Dim materialID As String
materialName = swModel.MaterialUserName
materialID = swModel.MaterialIdName
That was easy enough wasnt 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#
string[] configNames = (string[])swModel.GetConfigurationNames();
foreach (string configname in configNames)
{
// Do anything with configname here
}
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
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
75
76
77
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
79
80
Any here is the finished result when you run the code:
81
82
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.
Start by editing the Macro1 file
module by double-clicking it in
the Project Explorer.
Within the main() function
place the following code to
83
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.
Below this main subroutine we can place the LastIndexOf function
we created earlier, or we can put this in the form code. I have placed
it below the main for clarity.
Function LastIndexOf(stringToSearch As String, searchFor As String) As
Integer
Dim iPos As Integer
Dim iTemp As Integer
iPos = -1
iTemp = 0
Do Until iPos = 0
If iPos = -1 Then iPos = 0
iPos = InStr(iPos + 1, stringToSearch, searchFor)
If iPos <> 0 Then iTemp = iPos
Loop
84
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 rightclick the
UserForm1 item in
the Project
Explorer and select
View Code.
VBA
Dim strInfo As String
Private Sub CommandButton1_Click()
Set swApp = GetObject("", "SldWorks.Application")
If swApp Is Nothing Then
85
86
87
* Find the complete C#, VBA and VB.Net examples on the CD if you
have any troubles.
88
89
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.
If we are to work with selections we must add another variable to our
program. Please this in the same location as the swApp and
90
VBA
Dim swSelMgr As SelectionMgr
VBA
Set swSelMgr = swModel.SelectionManager
91
92
93
94
95
96
97
98
99
100
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
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.
Once we know we have a dimension selected, we get it using
GetSelectedObject6 and pass 1 as the index to retrieve the first
selected object, and just ignore any more after that.
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
103
104
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
106
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.
The definition is as follows:
retval = ModelDocExtension.SelectByID2 ( Name, Type,
X, Y, Z, Append, Mark, Callout, SelectOption )
Name
Type
X, Y, Z
Append
Mark
Callout
SelectOption
String
String
Double
Boolean
Integer
Pointer
Enumerator (Integer)
107
Omitting any field that you don't need. Here are a few examples:
108
Where
Thickness
Sheet-Metal1
Bracket-1
MachineContainer
=
=
=
=
Dimension name
Feature Name
Part Name & Instance ID
Assembly Name
Where
WebThickness = Dimension name
Web Thickness Offset = Plane Name
MachineContainer = Assembly Name
109
Where
Front Centre
Generic 01CS-015-1
01 Bedplate
/Generic 01CP-035-1
Generic 01CS-015
=
=
=
=
=
110
Annotation::Select3
Body2::Select2
BreakLine::Select
Component2::Select3
Configuration::Select2
EdgePoint::Select
Entity::Select4
Featur::Select2
SketchContour::Select2
SketchHatch::Select4
SketchPoint::Select4
SketchSegment::Select4
111
selType
state
Enumerator (swSelectType_e)
Boolean
VBA
swApp.SetSelectionFilter swSelFACES Or swSelEDGES
112
113
114
116
117
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.
Some functions you have to add a return statement for as they
require feedback, but since we wont be using them in this book just
type what you see. I will explain some functions later on and explain
the return values and what they mean, but for now just accept what
is written and write your code the same.
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, lets 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
Now, unlike .Net languages VBA does not have the power to
automatically create all of the required base functions for us, but
dont 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
120
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 doesnt 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
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
123
Private Sub
PropertyManagerPage2Handler5_OnComboboxSelectionChanged(ByVa
l Id As Long, ByVal Item As Long)
End Sub
Private Sub PropertyManagerPage2Handler5_OnGroupExpand(ByVal Id
As Long, ByVal Expanded As Boolean)
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 Function PropertyManagerPage2Handler5_OnTabClicked(ByVal
Id As Long) As Boolean
End Function
Private Sub
PropertyManagerPage2Handler5_OnTextboxChanged(ByVal Id As
Long, ByVal Text As String)
End Sub
124
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#
MyFirstPMP myPMP = new MyFirstPMP();
myPMP.Show(swApp);
125
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
127
128
pmPage;
pmGroup;
pmLabel;
int idGroup = 0;
int idLabel = 1;
129
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
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
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
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.
The handler is a handle (think of it as a link) to the class that
should receive the feedback events, but in order to receive these
feedbacks our program has to be run in the same memory space
as SolidWorks, and for .Net that means we must create a add-in;
stand-alone applications cannot receive these feedbacks, so we
pass null instead.
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
134
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
AddGroupBox
Once we set this message, we then create a new group, adding it to
the pmPage page. The function is as follows:
retval = PropertyManagerPage2.AddGroupBox ( Id,
Caption, Options )
AddControl
The AddControl function looks like this:
retval = PropertyManagerPage2.AddControl ( Id,
ControlType, Caption, LeftAlign, Options, Tip )
138
In the MyFirstPMP class, before all of the functions but after the
Implements PropertyManagerPage2Handler5 line, place the
following variables:
VBA
Dim pmPage As PropertyManagerPage2
Dim pmGroup As PropertyManagerPageGroup
Dim pmLabel As PropertyManagerPageLabel
Dim idGroup As Integer
Dim idLabel As Integer
Dim lErrors As Long
139
Those are the only variables we need for now to get our example up
and running.
Go to the Show function we created and add the following code.
This code will create a new page and set it to the variable pmPage,
then give the page a message and add a group and label.
VBA
Sub Show()
lErrors = 0
idGroup = 0
idLabel = 1
pageoptions = swPropertyManager_CancelButton +
swPropertyManager_OkayButton +
swPropertyManagerOptions_LockedPage
groupoptions = swGroupBoxOptions_Expanded +
swGroupBoxOptions_Visible
140
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
Now compare that with the .Net version, and see the difference:
pmPage =
(PropertyManagerPage2)swApp.CreatePropertyManagerPag
e("My First PMP", pageoptions, null, ref iErrors);
142
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.
Good, so lets create something useful and start handling those
events!
143
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.
We will begin by creating a new macro, connecting to SolidWorks,
getting the Active Document, and creating a new instance of a class
that implements PropertyManagerPage2Handler5; so basically we
are creating an identical copy of the macro we have just created.
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
If swModel.GetType() = swDocDRAWING Then
MsgBox "Cannot set faces in a drawing"
Exit Sub
End If
144
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.
The filters variable is used to store selection filter data, it will be
explained later.
145
146
147
148
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.
Next, we set up the selection mode to single-select, the height of the
selection box to 50 pixels, and the filter we just defines to the
selection box.
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
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
151
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
Traversing through an Assembly
Traversing through a Component
Displaying the results
Playing with Components and Features
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.
Within this function we check if the Component2 object passed in
has children, and if it has any, pass each childs Component2 back
into the function, so that child gets checked for children also. This
loop will continue for every child so long as there are children. This
loop will effectively pass over every single component in an
assembly. For now we will just display a message box each time we
pass over a component.
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);
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());
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
Sub TraverseComponent(component As Component2)
Dim children As Variant
children = component.GetChildren()
If UBound(children) > 0 Then
Dim i As Integer
For i = 1 To UBound(children)
MsgBox children(i).GetModelDoc().GetTitle()
TraverseComponent children(i)
Next
End If
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
159
Traversing
while (feat != null)
{
MessageBox.Show(feat.Name);
feat = (Feature)feat.GetNextFeature();
}
}
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#
private void TraverseSubFeatures(Feature feature, TreeNode node)
{
Feature subfeat = (Feature)feature.GetFirstSubFeature();
while (subfeat != null)
{
TraverseSubFeatures(subfeat, subtn);
subfeat = (Feature)subfeat.GetNextSubFeature();
160
Traversing
}
}
161
Traversing
Set feat = feat.GetNextFeature()
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
Sub TraverseSubFeatures(feature As feature)
Dim feat As feature
Set feat = feature.GetFirstSubFeature()
While Not feat Is Nothing
MsgBox feat.Name
TraverseSubFeatures feat
Set feat = feat.GetNextSubFeature()
Wend
End Sub
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
Component2
swConf;
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 arent working with a drawing:
C#
if (swModel.GetType() == (int)swDocumentTypes_e.swDocDRAWING)
{
MessageBox.Show("Active document cannot be a drawing");
return;
}
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.
Next we want to call the TraverseFeatures function we created in
the last section, and to call the TraverseComponent function too.
The addition in this instance is we are passing in a TreeNode variable
as the second parameter. We will use this TreeNode variable and
add nodes to it.
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.
Lets take a look at the modified TraverseComponent function
where we add each component to its parent tree node item:
C#
private void TraverseComponent(Component2 component, TreeNode
treeNode)
{
object[] children = (object[])component.GetChildren();
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#
private void TraverseFeatures(Feature firstfeature, TreeNode tn)
{
Feature feat = firstfeature;
while (feat != null)
{
if (string.Compare(feat.GetTypeName2(), "Reference", true) == 0)
return;
TreeNode subtn = tn.Nodes.Add(":: " + feat.Name);
TraverseSubFeatures(feat, subtn);
feat = (Feature)feat.GetNextFeature();
}
}
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#
private void TraverseSubFeatures(Feature feature, TreeNode node)
{
Feature subfeat = (Feature)feature.GetFirstSubFeature();
while (subfeat != null)
{
TreeNode subtn = node.Nodes.Add("::" + subfeat.Name);
TraverseSubFeatures(subfeat, subtn);
subfeat = (Feature)subfeat.GetNextSubFeature();
}
}
169
Traversing
170
Traversing
hashStore;
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.
Inside the TraverseComponent function, right below the line where
we add the TreeNode again, add the same line as before, but this
time adding the component:
C#
hashStore.Add(tn, comp);
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.
Once we have the desired object, we call its respective Select
function to select it. Run our program and click the button to fill the
list, then select some items from the list and watch in SolidWorks as
the object gets selected.
175
Traversing
Pretty good hu? Thats 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 its 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 leftclicking, 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
181
182
Then all that is left is to call the function to get the manager from
any ModelDoc2 object:
VBA
Set cpm = swModel.Extension. CustomPropertyManager("")
183
VBA
Dim modelconfigs As Variant
modelconfigs = swModel.GetConfigurationNames
184
swCustomInfoUnknown
swCustomInfoText
swCustomInfoDate
swCustomInfoNumber
swCustomInfoYesOrNo
swCustomInfoDouble
In the user interface for the custom properties the user can only
select 4 options (Text, Date, Number, Yes/No). The Double
185
VBA
Dim iRet As Long
iRet = cpm.Add2(Description, swCustomInfoText, My Description)
We then just check this iRet value for true (1) or false (0):
C#
if (iRet == 0)
// Failed
else
// Succeeded
186
Another quick note: the Add2 function will return a failure if the
property already exists in the first place.
187
VBA
Dim iRet As Long
iRet = cpm.Delete(Description)
188
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 lets take a look at how to check if one already exists.
189
VBA
Dim theval As String
Dim thevalres As String
Cpm.Get2 fieldName, theval, thevalres
190
192
193
194
195
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.
Start with a new project in C# or VB.Net, using the usual template.
Go to the Form Designer. You should already have the default
button there; we are going to add a few more controls.
From the toolbar to the left drag in 2 Label controls, 2 TextBox
Controls, and 1 TreeView control.
196
197
198
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
No explanation needed here. Now for the main loop; dont 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;
value = GetCustomProperty(cpm, configProp);
if (value.ToUpper().Contains(searchFor.ToUpper()))
AddConfig(cpm, s);
}
200
201
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#
string theval, theval2;
// Get all property names
List<string> propnames = new List<string>();
string[] getnames;
getnames = (string[])cpm.GetNames();
if (getnames != null)
foreach (string prop in getnames)
propnames.Add(prop.ToUpper());
// Check whether to overwrite if exists
if (propnames.Contains(fieldName.ToUpper()))
{
// FOUND
202
203
If the property we are looking for doesnt 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#
private void AddConfig(CustomPropertyManager cpm, string name)
{
TreeNode tn = tvList.Nodes.Add(name);
string[] getnames;
if (cpm != null)
{
204
206
207
208
Notice the filter list that we typed in and how it has limited the files
we can see to assemblies and parts.
Once the ShowDialog function has returned, the property FileName
of the OpenFileDialog will either be empty if the user clicked cancel,
or the full location of the file they selected, so we check this out.
209
210
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
Private Function FileExists(ByVal sPathName As String, Optional
Directory As Boolean) As Boolean
On Error Resume Next
If sPathName <> "" Then
If IsMissing(Directory) Or Directory = False Then
FileExists = (Len(Dir(sPathName)) <> 0)
Else
FileExists = (Len(Dir(sPathName, vbDirectory)) <> 0)
End If
End If
211
This function will return True if a file or directory exists, and False if
not, so now we do this:
VBA
If Len(filename) < 8 Then
MsgBox "Invalid filename"
Exit Sub
End If
Dim ext As String
ext = UCase(Right(filename, 7))
If Not FileExists(filename) Or (ext <> ".SLDASM" And ext <> ".SLDPRT")
Then
MsgBox "Invalid filename"
Exit Sub
End If
212
VBA
Dim drwTemplate As String
drwTemplate =
swApp.GetUserPreferenceStringValue(swDefaultTemplateDrawing)
213
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:
SldWorks.NewDocument ( templateName, paperSize,
width, height )
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:
retval = SldWorks.GetTemplateSizes ( filename )
VBA
Dim sizes As Variant
sizes = swApp.GetTemplateSizes(drwTemplate)
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
VBA
Dim swDrawing As DrawingDoc
Set swDrawing = swApp.NewDocument(drwTemplate, sizes(0), sizes(1),
sizes(2))
216
217
VBA
Dim bRet As Boolean
bRet = swDrawing.Create3rdAngleViews2(filename)
If bRet = False Then
MsgBox "Failed to add 3 common views."
Exit Sub
End If
218
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.
Starting with .Net as usual, in the Form Designer drag a new
TextBox control to your form; we are going to use this to display the
information. Set its Multiline property to True and its ScrollBars
property to Both.
219
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
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
222
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
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.
Set the MultiLine property of the TextBox to True, and the
ScrollBars property to Both.
224
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
226
227
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
229
PrintDirect function
This function of the ModelDoc2 object takes no parameters and
returns no value, so it couldnt 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
231
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
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.
This will store our printer list. Within the constructor function add
the following:
233
234
235
236
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
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
239
240
241
242
Within this Activate function add the following code to call the
function we created just and fill the ComboBox with the printer list:
VBA
Private Sub UserForm_Activate()
Dim StrPrinters As Variant
Dim x As Long
StrPrinters = ListPrinters()
243
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.
The IsBounded function checks if the variable passed in is actually an
array by checking that the upper bound of the variable is a numeric
value. If the variable is not an array it will not return a value:
VBA
Public Function IsBounded(vArray As Variant) As Boolean
On Error Resume Next
IsBounded = IsNumeric(UBound(vArray))
End Function
244
245
246
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
doesnt 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
248
Add-ins
The basics of an Add-in
Removing Add-in entries
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 lets get straight to it.
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.
Try not to take
too much of the
code in as it will
likely just go
right over your
head and just
confuse the
matter, just
accept the fact
that it does what
it does for now,
which is to create a SolidWorks add-in with a few menu items. I will
cover as much of the important stuff as I can in this brief section.
Start by taking a glance at the Local Variables section:
251
Add-ins
C#
ISldWorks iSwApp;
ICommandManager iCmdMgr;
int addinID;
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;
string keyname = "SOFTWARE\\SolidWorks\\Addins\\{" +
t.GUID.ToString() + "}";
hklm.DeleteSubKey(keyname);
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, thats it. An entry looks like this:
253
Add-ins
254
Add-ins
#region Setup the Command Manager
iCmdMgr = iSwApp.GetCommandManager(cookie);
AddCommandMgr();
#endregion
return true;
}
public bool DisconnectFromSW()
{
RemoveCommandMgr();
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.
Finally, take a look at the UI Methods section. This is the section
where our code has been placed to add the command menu:
C#
public void AddCommandMgr()
{
ICommandGroup cmdGroup;
BitmapHandler iBmp = new BitmapHandler();
Assembly thisAssembly;
thisAssembly =
System.Reflection.Assembly.GetAssembly(this.GetType());
cmdGroup = iCmdMgr.CreateCommandGroup(1, "MyFirstAddin",
"This is my add-in", "", -1);
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.AddCommandItem2("Start my external program", 0, "Click
me", "Click me", 0, "StartExternalProgram", "", 0,
(int)(SwConst.swCommandItemType_e.swMenuItem |
SwConst.swCommandItemType_e.swToolbarItem));
cmdGroup.Activate();
}
public void RemoveCommandMgr()
{
iCmdMgr.RemoveCommandGroup(1);
}
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.
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.
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.
Before we continue to add command items we want to add some
icons ready for use with our items. To do this we use the
BitmapHandler object and set the 4 icon properties of the
ICommandManager object using the following lines:
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
Adding a Command Item
Now we have a Command Group and some icons to use, we are
ready to add a Command Item. By adding this item it will
automatically add the menu item in the add-in. For this we call the
function AddCommandItem2:
*CmdIndex = CommandGroup.AddCommandItem2 ( Name,
Position, HintString, ToolTip, ImageListIndex,
CallbackFunction, EnableMethod, UserID,
MenuTBOption)
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 HintString is the hint that appears in the SolidWorks status
bar.
The ToolTip is the tip that appears in the yellow box when the
user hovers over the item.
The ImageListIndex is the position within the image list you
added in the previous step, starting from 0.
The CallbackFunction is the exact case-sensitive name of the
function within your add-in to actually run when the user clicks
this item. You will see this in use in a moment.
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 MenuTBOption is an enumerator value for selecting where
to add the item; to the menu, to the toolbar, or both.
The function returns the index position the menu item has been
placed in the group.
For this example here is our call to this function:
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();
263
Add-ins
wish to start. Because Notepad is within a system folder it is found
automatically without the need for its full location.
The second line runs a SolidWorks macro; the function is as follows:
retval = SldWorks.RunMacro ( filePathName,
moduleName, procedureName )
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:
Try clicking your button or menu
item and see how your function
StartExternalProgram will run,
and effectively open notepad
and run the macro located in the
location you entered.
That is it for our brief overview of add-ins; hopefully you have
learned enough to create some basic menu items, the rest of the
coding is not really to do with add-ins but doing what you actually
want your program to do.
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
268