Sei sulla pagina 1di 14

Hands-on Introduction to C# Add-ins for AutoCAD—Part 2

Jerry Winters – VB CAD, Inc.

SD12697-L In this second part of the hands-on introduction to Microsoft C# we will continue where
the first part left off. Attendees will begin by creating graphical user interfaces and exploring the ins
and outs of Modal and Modeless dialogs. We will finish with batch-processing files and see what
happens when modern processors are pushed to their full potentials. After both parts of this lab, at-
tendees will be ready to create their own AutoCAD software add-ins with C#.

Learning Objectives
 Learn how to create a graphical user interface in Microsoft Visual Studio
 Discuss displaying forms Modal or Modeless and deciding which is best
 Learn how to open, process, and closing .DWG™ files with C#
 Learn how to process .DWG files without opening them in AutoCAD

About the Speaker


Jerry Winters has taught thousands at Autodesk University, mixing real-world practical examples with engaging humor. His “I’m
just a drafter” approach to API topics make his classes as enjoyable as they are applicable.
jerryw@vbcad.com
Learn how to create a graphical user interface in Microsoft
Visual Studio
Let’s begin by creating a new C# Class Library Project in Visual Studio.

Next, let’s add the ‘3 essential references’ and make the necessary changes to run AutoCAD when we Debug.
Next, let’s create a new Command in our Add-In that displays the Form.

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015K")]
public static void AU2015K()
{
Form1 myForm = new Form1();
Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myForm);
}

SAVE THE NEW PROJECT BEFORE DEBUGGING.


Now, let’s begin Debugging, netload our new application, and run the “AU2015K” command.

Now that’s a beautiful visual interface.

Since we know we can display the Form, let’s close it and return to
Visual Studio.

Our code worked exactly as designed. It opened and it closed. Let’s add a couple of controls to our Form:
Add a Button control to the
Form and change the Text
property to be “OK”.

Please note that this does not


change the Name of the Button,
only the Text that is displayed.

The Name of this Button is


“button1”.

Now, change the “DialogResult” property to


“OK”. Then let’s change the Anchor property
to “Bottom, Left”.
Now, we are going to add another Button. This will have a Text property of “Cancel”, a DialogResult property of “Cancel”, and will
be anchored to the “Bottom, Right” corner of the Form.

Now, let’s modify our Command that shows this Form.

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015K")]
public static void AU2015K()
{
Form1 myForm = new Form1();
switch(Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myForm))
{
case System.Windows.Forms.DialogResult.OK:
System.Windows.Forms.MessageBox.Show("OK");
break;
case System.Windows.Forms.DialogResult.Cancel:
System.Windows.Forms.MessageBox.Show("Cancelled");
break;
}
}

With our Buttons having OK and Cancel Dialog Result properties, we can determine how the Form was closed and execute code
based on that. Obviously, we want to do more than show MessgeBoxes but that is enough for now.

The next thing we want to do is add a ComboBox to this Form and change it’s
Anchor to “Top, Left, Right”.

Changing the Anchor to include Left and Right means the ComboBox will change
in Width as the Form’s Width changes.

Changing the Modifiers Property from Private to Public will allow us to ‘control’
this control from outside the Form1 Class.
Adding a couple of “Using” statements a the top of the Class1.cs file will simplify our code as we move forward. Note we added
the Autodesk.AutoCAD.DatabaseService and Autodesk.AutoCAD.Geometry namespaces.

Now, change the Code in our Command as follows:

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015K")]
public static void AU2015K()
{
Form1 myForm = new Form1();
using (Transaction myTrans =
HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)HostApplicationServices.WorkingDatabase.LayerTableId.GetObject
(OpenMode.ForWrite);
foreach(Autodesk.AutoCAD.DatabaseServices.ObjectId myLTRid in myLT)
{
LayerTableRecord myLTR = (LayerTableRecord)myLTRid.GetObject(OpenMode.ForRead);
myForm.comboBox1.Items.Add(myLTR.Name);
}
}
switch (Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myForm))
{
case System.Windows.Forms.DialogResult.OK:
System.Windows.Forms.MessageBox.Show("OK");
break;
case System.Windows.Forms.DialogResult.Cancel:
System.Windows.Forms.MessageBox.Show("Cancelled");
break;
}
}

Now we are adding the layer names of all Layers in AutoCAD into the ComboBox. What can we do with that?

Let’s modify our code a little more.


[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015K")]
public static void AU2015K()
{
Form1 myForm = new Form1();
using (Transaction myTrans =
HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)HostApplicationServices.WorkingDatabase.LayerTableId.GetObject
(OpenMode.ForWrite);
foreach(Autodesk.AutoCAD.DatabaseServices.ObjectId myLTRid in myLT)
{
LayerTableRecord myLTR = (LayerTableRecord)myLTRid.GetObject(OpenMode.ForRead);
myForm.comboBox1.Items.Add(myLTR.Name);
}
}
switch (Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myForm))
{
case System.Windows.Forms.DialogResult.OK:
Autodesk.AutoCAD.ApplicationServices.Document myDoc =
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Autodesk.AutoCAD.EditorInput.Editor myEditor = myDoc.Editor;
Autodesk.AutoCAD.EditorInput.PromptEntityResult myPPR = myEditor.GetEntity("Pick:");
if(myPPR.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK)
{
Autodesk.AutoCAD.DatabaseServices.ObjectId selEntID = myPPR.ObjectId;
using (Transaction myTrans =
HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction())
{
Entity myEnt = (Entity)selEntID.GetObject(OpenMode.ForWrite);
LayerTable myLT =
(LayerTable)
HostApplicationServices.WorkingDatabase.LayerTableId.GetObject(OpenMode.ForWrite);
if (myLT.Has(myForm.comboBox1.Text))
{
myEnt.Layer = myForm.comboBox1.Text;
}
myTrans.Commit();
}
}
break;
case System.Windows.Forms.DialogResult.Cancel:
System.Windows.Forms.MessageBox.Show("Cancelled");
break;
}
}

If the we select a Layer from the ComboBox and hit the OK button, we will now be presented with the opportunity to select an
entity that will then have its layer changed to the one selected in the ComboBox.
Visual Studio contains a large number of Controls that can be utilized in Forms added to our projects. Creating a useful and user-
friendly, intuitive user interface can be one of the most challenging aspects of creating AutoCAD Add-Ins.

During the course of this live Class, we will review a few of the Controls shown above. These include:
CheckBox
RadioButton
FolderBrowserDialog
ListView
TextBox
TreeView
Discuss displaying forms Modal or Modeless and deciding which
is best
[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015L")]
public static void AU2015L()
{
Form1 myForm = new Form1();
switch (Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myForm))
{
case System.Windows.Forms.DialogResult.Abort:
break;
case System.Windows.Forms.DialogResult.Cancel:
break;
case System.Windows.Forms.DialogResult.Ignore:
break;
case System.Windows.Forms.DialogResult.No:
break;
case System.Windows.Forms.DialogResult.OK:
break;
case System.Windows.Forms.DialogResult.Retry:
break;
case System.Windows.Forms.DialogResult.Yes:
break;
}
}

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015M")]
public static void AU2015M()
{
Form1 myForm = new Form1();
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(myForm);
}

When a Form is displayed “Modeless”, it is on the screen and we can continue to use AutoCAD and issue AutoCAD Commands
while the form is still on the screen. When a Form is displayed “Modal”, it maintains Focus until it is closed.

There are a variety of reasons why Modeless may be best in some situations and other reasons why Modal may be best in other
situations. Modal Dialogs are Linear in nature because once they have Focus, they maintain it until they are closed.

Both Modal and Modeless Forms can display other Modal and Modeless Forms.
Learn how to open, process, and closing .DWG™
files with C#
[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015N", Autodesk.AutoCAD.Runtime.CommandFlags.Session)]
public static void AU2015N()
{
System.Windows.Forms.OpenFileDialog myOFD = new System.Windows.Forms.OpenFileDialog();
if (myOFD.ShowDialog() == System.Windows.Forms.DialogResult.OK )
{
Autodesk.AutoCAD.ApplicationServices.Document myDoc =
Autodesk.AutoCAD.ApplicationServices.DocumentCollectionExtension.Open(
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager, myOFD.FileName);
using (Autodesk.AutoCAD.ApplicationServices.DocumentLock myLock = myDoc.LockDocument())
{
//Do something cool here!!!
}
Autodesk.AutoCAD.ApplicationServices.DocumentExtension.CloseAndDiscard(myDoc);
}
}

As we review the code shown above, we can see that a variable is declared as a Document and it is assigned to the result of the Open
method of the DocumentCollectionExtension Class.

The Document Lock is used to make sure we have access to this document. Also please note the Session flag is used in the
CommandMethod. This is necessary when we want to use multiple Documents from one Command. Please note, that was “multiple
Documents”, not multiple databases.

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015P", Autodesk.AutoCAD.Runtime.CommandFlags.Session)]
public static void AU2015P()
{
System.Windows.Forms.OpenFileDialog myOFD = new System.Windows.Forms.OpenFileDialog();
if (myOFD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Autodesk.AutoCAD.ApplicationServices.Document myDoc =
Autodesk.AutoCAD.ApplicationServices.DocumentCollectionExtension.Open(
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager, myOFD.FileName);
using (Autodesk.AutoCAD.ApplicationServices.DocumentLock myLock = myDoc.LockDocument())
{
//Something cool here!!!
using (Transaction myTrans = myDoc.Database.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)HostApplicationServices.WorkingDatabase.LayerTableId.GetObject
(OpenMode.ForWrite);
LayerTableRecord newLayer = new LayerTableRecord();
newLayer.Name = "AU2015P";
myLT.Add(newLayer);
myTrans.AddNewlyCreatedDBObject(newLayer, true);
myTrans.Commit();
}
myDoc.Database.SaveAs(myOFD.FileName, DwgVersion.Current);
}
Autodesk.AutoCAD.ApplicationServices.DocumentExtension.CloseAndDiscard(myDoc);
}
}
“AU2015P” is a great command. Not only does it open the selected file but it adds a new Layer named “AU2015P” to the file. Then
it saves THE DATABASE and closes the file.

As exciting as seeing the screen flashing violently as files are opened and closed can be, there is a performance hit because the
graphical elements on the screen have to be drawn in the Editor and that takes processing power and time. Wouldn’t it be great if
we could speak to AutoCAD .dwg files without opening them in the AutoCAD Editor?

Learn how to process .DWG files without opening them


in AutoCAD
[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015Q")]
public static void AU2015Q()
{
System.Windows.Forms.OpenFileDialog myOFD = new System.Windows.Forms.OpenFileDialog();
if (myOFD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Autodesk.AutoCAD.DatabaseServices.Database myDB =
new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
myDB.ReadDwgFile(myOFD.FileName, FileOpenMode.OpenForReadAndAllShare, true, "");
using (Transaction myTrans = myDB.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)myDB.LayerTableId.GetObject(OpenMode.ForWrite);
LayerTableRecord newLayer = new LayerTableRecord();
newLayer.Name = "AU2015Q";
myLT.Add(newLayer);
myTrans.AddNewlyCreatedDBObject(newLayer, true);
myTrans.Commit();
myDB.SaveAs(myOFD.FileName, DwgVersion.Current);
myDB.Dispose();
}
}
}

“AU2015P” and “AU2015Q” are very similar. There is one primary difference. In “AU2015P”, we are opening the Document and
then modifying the Document’s Database. In “AU2015Q” we are manipulating the Database directly by creating a completely
blank DWG file and then using “ReadDwgFile” to read the .dwg file into the blank database. After changes are made, the Database
is saved and Disposed of.
Manipulating and changing one file at a time using an Add-In is powerful and fast. Manipulating and changing many files is even
more powerful and faster.

[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015R")]
public static void AU2015R()
{
System.Windows.Forms.FolderBrowserDialog myFBD = new System.Windows.Forms.FolderBrowserDialog();
if (myFBD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
System.IO.DirectoryInfo myDIO = new System.IO.DirectoryInfo(myFBD.SelectedPath);
foreach(System.IO.FileInfo myFIO in myDIO.GetFiles("*.dwg"))
{
Autodesk.AutoCAD.DatabaseServices.Database myDB =
new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
myDB.ReadDwgFile(myFIO.FullName, FileOpenMode.OpenForReadAndAllShare, true, "");
using (Transaction myTrans = myDB.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)myDB.LayerTableId.GetObject(OpenMode.ForWrite);
LayerTableRecord newLayer = new LayerTableRecord();
newLayer.Name = "AU2015R";
myLT.Add(newLayer);
myTrans.AddNewlyCreatedDBObject(newLayer, true);
myTrans.Commit();
myDB.SaveAs(myFIO.FullName, DwgVersion.Current);
myDB.Dispose();
}
}
}
}

Most of the code here is the same as in our previous example. The primary difference is that we are now using a
FolderBrowserDialog to ask the user to select a Folder. Then we get each .DWG file in the selected folder, open it IN MEMORY
ONLY, add a new Layer named “AU2015R”, save it, and close it.
[Autodesk.AutoCAD.Runtime.CommandMethod("AU2015S")]
public static void AU2015S()
{
System.Windows.Forms.FolderBrowserDialog myFBD = new System.Windows.Forms.FolderBrowserDialog();
if (myFBD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
System.IO.DirectoryInfo myDIO = new System.IO.DirectoryInfo(myFBD.SelectedPath);
foreach (System.IO.FileInfo myFIO in myDIO.GetFiles("*.dwg",
System.IO.SearchOption.AllDirectories))
{
Autodesk.AutoCAD.DatabaseServices.Database myDB =
new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
myDB.ReadDwgFile(myFIO.FullName, FileOpenMode.OpenForReadAndAllShare, true, "");
using (Transaction myTrans = myDB.TransactionManager.StartTransaction())
{
LayerTable myLT =
(LayerTable)myDB.LayerTableId.GetObject(OpenMode.ForWrite);
LayerTableRecord newLayer = new LayerTableRecord();
newLayer.Name = "AU2015S";
myLT.Add(newLayer);
myTrans.AddNewlyCreatedDBObject(newLayer, true);
myTrans.Commit();
myDB.SaveAs(myFIO.FullName, DwgVersion.Current);
myDB.Dispose();
}
}
}
}

There is one SIGNIFICANT but small difference between “AU2015R” and “AU2015S”. Can you spot it?

System.IO.SearchOption.AllDirectories

This one additional parameter to GetFiles results in all files in the selected folder plus all files in all of the sub folders of the selected
folders to be returned. In our example, we are filtering the filename with “*.dwg” so we get all DWG files in the selected directory
plus the sub folders.

BE VERY CAREFUL WITH THIS. IF THE WRONG FOLDER IS SELECTED, WINDOWS CAN BE BROUGHT TO ITS KNEES VERY QUICKLY.
REVIEW
Creating Graphical User Interfaces that are enjoyable to work with and intuitive can be one of the most challenging aspects of
application development. And as we have seen in this Part 2 class, modifying or reading from multiple files is not only powerful but
easy to do. The only question is, what are you going to do with all of your spare time now?

I hope this class has been helpful and that you will continue your learning by attending Autodesk University classes in the future.
Thank you for your time.

Jerry Winters (jerryw@vbcad.com)

Potrebbero piacerti anche