Sunday, February 13, 2011

[Revit API]–Introduction to VSTA as a rapid prototyping tool

At Autodesk University in December some of the Autodesk Revit API team were mentioning that they thought VSTA was a little underappreciated by Revit API developers, and that it is a very useful too, particularly for working things out before turning them into fully fledged applications.

I must say that I’ve always steered clear of VSTA, for a number of reasons, firstly it used to lack a few features, and secondly that I’ve always preferred the full Visual Studio professional as I can use plugins like the wonderful Resharper plugin. However, there are disadvantages to this, namely that you generally have to reboot Revit after each code change. I’ve purchased a good quality Solid State Drive in order to make Revit boot quickly, which has made this disadvantage bearable, and there are some techniques to try and avoid restarting Revit all the time but it’s still not ideal.

Enter VSTA. I recently decided to give it a try to quickly answer a question someone asked me via email. To get started using it, you need to install it from the Revit disk. After this, you should be able to open a document and go to the Manage ribbon tab and go to ‘Macro Manager’. This is somewhat confusing, as all non VSTA addins go in the ‘addins’ tab.
image

You will then see the Macro Manager, which lets you create either an Application level macro, or a Document level (ie the code is included in the actual Revit file you have open). To do some rapid prototyping we’ll create a document level macro by clicking on the tab that is named the same as your Revit file.
image
Here you’ll see a couple of tools I’ve made, to create a new one click ‘Create Module’ which opens a dialog box asking you to name your new Module.
image

This create a new module in the list.
image

Click on it, then Click Create ‘Macro’.
image

This will create a new instance of VSTA, which is a cut down version of Visual Studio 2008.
image

You can see here it has created a HelloWorld method which represents your macro, in addition to that there’s the usual Attributes you’d see on an External Command, and also the AddInID GUID is created – no need for you to set this up. There’s also Module_Startup and Shutdown methods which let you run some code which happens before or after every macro in the module. For now, we’ll just concentrate on the HelloWorld method.
To show a simple dialog box, first we need to import the Autodesk.Revit.UI namespace at the top, and then we can add in our Task Dialog code:

public void HelloWorld()
{
    TaskDialog.Show("Hello World","Hello World");
}

Now, to run this code first we need to build it, go to Build > Build Example. Then swap back to the Macro Manager and you’ll see the Example module can be expanded out to see all the macros underneath it.
image

We can select that macro and click ‘Run’ to run it, or Step Into if we want to debug it.
image

As you can see it works fine. Now lets create one that does something a little different. Say we have a detail line on our document, like one from my previous post that creates a detail line:
image

And we want to know how to access the length of it in millimetres through the API. Here’s a workflow we can use to do that.
First, create a new macro under the Example module called GetLength

image

You should see it creates another public void for that macro. (You can create macros just by creating the methods yourself if you like)

image

Now, we want to figure out how to access the length. If you have two monitors (if you don’t, you should!) put the VSTA code on one window and on the other, select your detail line. To work out how to access this value, we can use RevitLookup, if you haven’t installed it, grab it from the SDK and install it – it’s essential for Revit API development.
Go to the add-ins tab, go to Revit Lookup and snoop the current selection. You’ll see the API’s view of this element

image

Click on the ParameterSet and you’ll see there is a parameter called Length.
image

This is what we want to access. Swap to the other window, and lets access it via code. First off, we’ll  have to reference the Autodesk.Revit.DB namespace. Then, we need to get at both the Document and UIApplication objects. To do this in a VSTA macro, you can use this.Document and this.Application. However rather than writing this each time in the code, I like to extract these to a variable. That way if the code I’m prototyping here ends up as a full API tool I can easily move it to a Visual Studio solution.

public void GetLength()
{
    Document doc = this.Document;
    UIApplication app = this.Application;
}

We’ll use the currently selected element as a way of getting to the detail line, then we’ll read through its parameters looking for the length, and display it in a TaskDialog.

public void GetLength()
        {
            Document doc = this.Document;
            UIApplication app = this.Application;
 
            foreach (Element selectedElement in app.ActiveUIDocument.Selection.Elements)
            {
                if (selectedElement is DetailLine)
                {
                    //the selected element is a detail line, so cast it to one.
                    DetailLine line = (DetailLine)selectedElement;
 
                    //loop through all the parameters
                    foreach (Parameter parameter in line.Parameters)
                    {
                        if (parameter.Definition.Name.ToLower().Equals("length"))
                        {
                            //found the length parameter
                            TaskDialog.Show("Length", parameter.AsValueString());
                        }
                    }
 
                }
            }
        }

To run this, go to Build > Build Example and then swap back to Revit, head into the Macro Manager and run your macro. As long as you have the line selected you should see the result flash up
image

Now you know how to access the length, you’ve experimented in VSTA, and you can convert this code over to your full blown Revit add-in that you have been working on, or planning to work on.

This has saved us a fair bit of time, instead of creating a new Visual Studio project, setting up our entry class, referencing the API, adding the transaction and regeneration attributes (and probably forgetting to do that once, starting Revit, having an error and then having to exit again), setting the references to not copy, setting up an addin manifest file, creating an addinID GUID etc we’ve just opened VSTA, and with the document still live in front of us we’ve written our code and tested it.
While it may not be so convenient when creating a comprehensive Revit addin, I believe VSTA is certainly very handy when just doing some trial and error coding to work out how to access things through the API.  For more examples on how to use VSTA, see the VSTA samples folder in the Revit SDK.

No comments: