Showing posts with label API. Show all posts
Showing posts with label API. Show all posts

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.

Saturday, February 12, 2011

[Revit API] – How to draw a detail line on a view

I got asked a question similar to this so thought I’d post it up. To draw a detail line on a view, from one point to another, you first need to create a ‘Line’ and then a ‘DetailCurve’.
This may be a little confusing, but a ‘Line’ is not the actual end result, it’s a geometry object that represents where the line will go. To actually draw the line you need to create a DetailCurve, which is the actual Detail Line.
Here’s the code that I’m talking about:
//Create the Points
double x1 = 5.0;
double y1 = 5.0;
double z = 0.0;
double x2 = 117.0;
double y2 = 115.0;
XYZ point1 = app.Application.Create.NewXYZ(x1, y1, z);
XYZ point2 = app.Application.Create.NewXYZ(x2, y2, z);
 
//Create line
Line line = app.Application.Create.NewLineBound(point1, point2);
DetailCurve detailCurve = doc.Create.NewDetailCurve(doc.ActiveView, line);
 
TaskDialog.Show("Done", "Line Created");
First off, we are setting up the XYZ for each point that we are going to draw between. These are points in the model. Then we use the NewLineBound method to create a bound line in space between point1 and point2. If you just stopped here, you would see nothing in your model. To actually draw the Detail Line you use the NewDetailCurve method to specify which view you want to draw the line on, and the line that represents it’s geometry.
To run the code you’ll need to be in a transaction (or use Automatic transaction mode), and need to initialize the app and doc variables as you normally would. When you run the tool, the final result is this:
image
A simple line drawn on the page.

Monday, February 7, 2011

[Revit API]–Hide Warnings in Revit using the API

The general rule ever since the Revit API was released, is that whatever you can do in the API, you can also do through the normal user interface.

In recent versions I’ve come across a few different examples of where the API is starting to let you do some things you can’t do through the interface (as far as I know – there may be a way I am missing!). One such example is interacting with warnings.

Revit 2011 introduced the new Failures API. This API lets you do two things:

1. Create new warnings within Revit

2. Respond to warnings reported by Revit.

I’m going to discuss option 2 today through a quick example. Lets say that you have a burning desire to place two walls in exactly the same position. Normally when you do this in Revit, you’ll get a warning that looks like the following:

image

Which you can ignore. This is a bit of a silly example, but in many offices there may be warnings in Revit which you ALWAYS accept, and therefore having them in the warnings menu simple messes up the signal to noise ratio, and could mean that real warnings get lost in the crowd.

To stop this warning from displaying, we can make use of the new failures API. To do this, we use an event called the FailuresProcessing event, so as per usual with events we need to make a new ExternalApplication and subscribe to that event:

 

public Result OnStartup(UIControlledApplication application)
{
    application.ControlledApplication.FailuresProcessing += ControlledApplication_FailuresProcessing;
    return Result.Succeeded;
}
 
public Result OnShutdown(UIControlledApplication application)
{
    application.ControlledApplication.FailuresProcessing -= ControlledApplication_FailuresProcessing;
    return Result.Succeeded;
}

Whenever Revit does its failures checks, which is at a number of points in time, including after making our wall, or when opening the warnings menu, our ControlledApplication_FailuresProcessing method will get called.

void ControlledApplication_FailuresProcessing(object sender, Autodesk.Revit.DB.Events.FailuresProcessingEventArgs e)
   {
       FailuresAccessor accessor = e.GetFailuresAccessor();
       IList<FailureMessageAccessor> messages = accessor.GetFailureMessages();
       foreach (FailureMessageAccessor failure in messages)
       {
           FailureDefinitionId failId = failure.GetFailureDefinitionId();
           if (failId == BuiltInFailures.OverlapFailures.WallsOverlap)
           {
               TaskDialog.Show("Deleting Failure", failure.GetDescriptionText());
               accessor.DeleteWarning(failure);
           }
       }
   }

In the code above, you get an object of type ‘FailuresAccessor’ which, funnily enough, lets you access the failures.

Then you can get all of the failure messages and loop through them, then I use the BuiltInFailures listings to match the unique ID of each failure with the one that I’m looking for, that is BuiltInFailures.OverlapFailures.WallsOverlap. The WallsOverlap property simply returns a FailureDefintionId which is used when a WallsOverlap failure is added, so by comparing this with our ID we can find the failures we are after.

If they match, I’m showing a task dialog (you may like to remove this, it’s just there for demonstration purposes) and deletes the warning.

If you load Revit and try to create walls again you’ll see:

image

and you won’t get the Revit warning. Take out the call to task dialog and you’ll always have the warning deleted silently – meaning it’s never displayed to the user. Fairly quickly and easily you can remove a certain failure from the list, and as long as your ExternalApplication is running in the background it will never show up.

This can only be used for warnings, so errors can’t be simply deleted, however the Failures API has some neat options to do with automatically resolving errors, but that’s a topic for another day.

Friday, February 4, 2011

[Revit]–Free API addin–Revit Save Reminder, alerts you when your save to central is complete.

A fair while ago, during AU 2009 I believe, I saw on Twitter that someone suggested it would be a great if someone could make a dialog box that flashes to let you know that your save to central has completed, so that if you start doing other things whilst large projects are synchronizing, you will know when it’s done.
I thought this was a good idea, and made a mental note to make an add-in for it, and at long last, I’ve created it. You can download the add-in from HERE or HERE. If you are interested in the very basic source code, it’s on GitHub HERE.
To install it, simply extract the files into C:\Program Files\RevitSaveReminder\ and then move the RevitSaveReminder-Manifest.addin file to your addins directory which is C:\ProgramData\Autodesk\Revit\Addins\2011\ if you are on Vista/7 or C:\Documents and Settings\All Users\Application Data\Autodesk\Revit\Addins\2011\ if you are on XP.
After installing it, simply open your project, when your next synchronization with central is complete, you’ll see a window like this pop up:
image
And in your taskbar it’s icon will be flashing:
image
Additionally, you’ll be greeted with a generous round of applause playing from your speakers, to congratulate you on successfully completing some more work. Smile So even if you are doing some paper work on your desk, you’ll be alerted when it’s time to get back into Revit. I’ve made the sounds configurable, there’s a couple more sounds, one is a chime, and another is the cocking of a shotgun (as in, reloading and getting ready to kick Revit’s ass some more!), or if you wish, you can configure your own .wav file to play. To do this, simply edit the RevitSaveReminder.dll.config file in your program files directory. If you want no sound, edit it and make the WavFile value empty.
I hope someone gets some use out of this (perhaps the person that Tweeted it a couple of years ago and has no doubt been frantically searching the internet for it ever since)! Let me know your thoughts, bugs or suggestions.

Thursday, February 3, 2011

[Revit API]–Common Mistakes: DocumentSaved and DocumentSavedAs events

The Revit API has many useful events, including:

  • DocumentChanged
  • DocumentSaving
  • DocumentSavedAs
  • DocumentClosing
  • DocumentClosed
  • DocumentOpened
  • DocumentCreating
  • DocumentCreated
  • DocumentSynchronizingWithCentral
  • FileExported

And more. These let you run some code when certain events occur in Revit. You can make some powerful things with these events, so if you haven’t used them before, I recommend that you check them out.

A bit of a ‘duh’ moment that you may run into, is when you want to make a tool that runs whenever the user saves the document. Seems simple enough, you subscribe to the DocumentSaved event with code similar to this:

      public Result OnStartup(UIControlledApplication application)
        {
            application.ControlledApplication.DocumentSaved += ControlledApplication_DocumentSaved;
            return Result.Succeeded;
        }

However there’s a problem with this code – it won’t run the first time a user saves the document. This is because the DocumentSaved event is for when the user does a ‘Save’. If the document is new, when they click the save button, what Revit is actually doing in the background is a ‘Save As’, which prompts the user for a location and filename.

So, to get that event included as well you need to do something like this:

   public Result OnStartup(UIControlledApplication application)
        {
            application.ControlledApplication.DocumentSaved += ControlledApplication_DocumentSaved;
            application.ControlledApplication.DocumentSavedAs += ControlledApplication_DocumentSaved;

            return Result.Succeeded;
        }

This is registering the ControlledApplication_DocumentSaved method as a method to call when either of those events are called. To allow your save method to be the same method for each event, you need to set its parameters to be the following:

        void ControlledApplication_DocumentSaved(object sender, Autodesk.Revit.DB.Events.PostDocEventArgs e)
        {
          //my code goes here 
        }

Notice here that we are accepting event arguments of ‘PostDocEventArgs’ as opposed to ‘DocumentSavedAsEventArgs’ or ‘DocumentSavedEventArgs’, as PostDocEventArgs is a base class of these two.

Just something to keep in mind if you plan to use Revit’s powerful events API.

Thursday, November 18, 2010

[Revit API]–A couple more Revit API Blogs to follow

Just a quick note to let you know about a couple of blogs I’ve recently discovered that have a large amount of Revit API content.
First off, check out ‘Daren@Work’ which is the blog of Daren Thomas, creator of RevitPythonShell.

Secondly, Don Rudder has a blog ‘Revit .NET Development’.  Don wrote the Revit API chapter in Mastering Autodesk Revit Architecture 2011 which I recently purchased a copy of. He is also presenting at this years AU.

I’ve added these two sites to the index for RevitAPISearch.com, so their content will be easily searchable along with the other Revit API blogs and forums that I’ve got listed there.

If anyone else is writing a Revit API blog and would like to be included in the links on the right hand side of this page, and in the Revit API Search engine, please let me know.

Wednesday, September 1, 2010

[Revit API] Autodesk University 2010–Introduction to the Revit API

The Autodesk University class listings for both physical and virtual classes are now available on the AU website. Head over there and take a look, there are lots of great looking classes. It is also available in a handy spread sheet format.

This year they have tried to mix things up by changing the class selection a little bit, instead of having the same speakers present the same classes (even though they have been doing a great job of it) they have decided to try and get some fresh new content. Whilst new content is great, there are some classic ‘must have’ classes, and for those they have tried wherever they can to give a new speaker a chance to present on those to get a different perspective.

One such ‘must have’ class is the ‘Introduction to the Revit API’ class, which as a part of the changeup, I have the privilege of presenting. I have a high standard to live up to, as in previous years Danny Polkinhorn has done a stellar job of presenting this class, and helping many people to take their first steps in Revit API development (including me!).

The class description is this:

From Hello World and beyond, this class will take you through the steps of creating a Revit add-in from scratch and registering it to run inside Revit using the Revit application programming interface. You will learn how to get input from the user, access their currently selected elements, and read and write to and from the project database. Learn how the RevitLookup tool can help you to get familiar with the Revit file structure and debug your application, and learn how filters can help you speed up your database searches. With a little Microsoft® .NET programming knowledge, you can create great add-ins for Revit. This class shows you the basics that you can then build on. Some .NET programming (C# or Visual Basic®) experience is recommended.

It is a 60 minute lecture, on the first day of AU in November.

I would love to hear from anyone who is attending this class as to anything they would like to see included that isn’t listed in the description above, its not a huge timeslot so I won’t be going into great detail (there is a great range of Revit API classes this year that will cover more in depth topics), but I would like to hear from people who have maybe just started learning the API as to what certain things you found difficult or what gotchas you had so I can make sure I touch on those.

From my talks with people locally who have been trying to learn the API the first challenge is that of learning C# (or VB.NET), as a result I have been contemplating doing a sort of ‘C# for Revit Users’ webcast/blog post series in the lead up to AU. Stay tuned for information on that.

Sunday, July 4, 2010

[Revit API] Document.PrintManager returns a new instance every time

A gotcha that you may run into if you use the Print API in Revit is that the PrintManager property of a Document always returns a NEW PrintManager.
So if you make a change (say to the print range) of Document.PrintManager directly, it will be pointless.
Document.PrintManager.PrintRange = PrintRange.Select;
//then submit print
Document.PrintManager.SubmitPrint(view);

both will act on a NEW PrintManager each time, which isn’t what you want. What you need to do is maintain a reference to PrintManager yourself, only calling Document.PrintManager once.
PrintManager manager = Document.PrintManager;
manager.PrintRange = PrintRange.Select;
manager.SubmitPrint(view);

If you have a look using Red Gate’s .NET Reflector to disassemble that method in the RevitAPI.dll, you can see why:
public unsafe PrintManager get_PrintManager()
{
    PrintManager manager;
    REVIT_MAINTAIN_STATE revit_maintain_state;
    REVIT_MAINTAIN_STATE.{ctor}(&revit_maintain_state);
    try
    {
        manager = new PrintManager(this);
    }
    fault
    {
        ___CxxCallUnwindDtor(REVIT_MAINTAIN_STATE.{dtor}, (void*) &revit_maintain_state);
    }
    REVIT_MAINTAIN_STATE.{dtor}(&revit_maintain_state);
    return manager;
}

As convenient as only passing around the Document object is, it’s not going to work! Changes made to one instance are not likely to apply to any others.

Wednesday, June 2, 2010

[Revit API] Dynamic Model Update in the 2011 API

After completing the larger than normal task of converting my suite of Revit add-ins over to the new 2011 API, I’ve recently had the chance to sit down and start having a good play with the new features.

One feature which I have been particularly excited about is the new Element Level Events, also known as Dynamic Model Update. Basically, this lets you attach an event to a particular type of element, and when that element is changed, your code gets called, and can do various things. This is a feature which the team has clearly put a lot of thought into, and it opens Revit up to a whole new level of automation. Page 283 of the developers guide has a good introduction to this, and the example they use is changing all new walls to a certain type.

My first idea for using this tool is to solve a problem that some of the drafters at the company I work for have been complaining about, which is detail lines in Revit. When they want to insert a detail line they hit the keyboard shortcut, and select from a long list of different line styles, then draw the line. If they are doing lots of lines, they will scroll through that list many times – which is annoying. My idea was that they could tell my add-in what line style they want to insert, and then just draw detail lines without scrolling through the list each time.

The process would be this:

  • User runs the tool which registers an updater, and sets the line style.
  • When a new line is entered, that updater will be triggered
  • The updater will then convert the newly inserted line to the line style selected.

It was actually quite easy to get this working, which is a testament to how well this API is written.

First you need to create a class that implements IUpdater. Mine was called DetailLineUpdater. You then need to implement each of the members of that interface. These include:

  • Execute(UpdaterData data) – this is the actual method thats called when the updater is triggered, here is where I would change my line style.
  • GetAdditionalInformation() – just returns a description
  • GetChangePriority() – tells Revit the nature of what you will be doing in the Execute method. Revit uses this to determine in which order it should run each of the updaters.
  • GetUpdaterId() – returns an ID for the updater, which includes the AddInId and a GUID
  • GetUpdaterName() – a name for your updater. Make this something meaningful as it may get displayed to the user.

As you can see, these have some similarities to the kinds of things you need to tell Revit about your external commands.

Once you have your updater class, you need to do two things:

  1. Register it, using the UpdaterRegistry.RegisterUpdater(updater) method.
  2. Tell Revit when to trigger it.

For the second point, you need to use the UpdaterRegistry.AddTrigger method, which accepts three parameters.

  1. The id of the Updater
  2. A filter, which tells Revit which elements to attach to.
  3. A change type. This defines what actions on the filtered elements will trigger your updater. This could be insertion, deletion, parameter changes or geometry changes.

For my tool, I got the change type by using Element.GetChangeTypeElementAddition().

So now I have some code that will be run, whenever a certain type of element (in my case, elements with the category OST_Lines) is added. Here is a snapshot of the registration code:

DetailLineUpdater updater = new DetailLineUpdater(commandData.Application.ActiveAddInId);
                UpdaterRegistry.RegisterUpdater(updater);
                ElementCategoryFilter filter = new ElementCategoryFilter(BuiltInCategory.OST_Lines);
                UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), filter,Element.GetChangeTypeElementAddition());

Most of the IUpdater methods are trivial, but here is my Execute method:

        public void Execute(UpdaterData data)
        {
            foreach (ElementId addedElement in data.GetAddedElementIds())
            {
                DetailLine line = data.GetDocument().get_Element(addedElement) as DetailLine;

                foreach (Element style in line.LineStyles)
                {
                    if (style.Name.Equals("A_Certain_line_style"))
                        line.LineStyle = style;

                }
            }
            
        }

As you can see, its all fairly simple, and works well! However, there is a catch.

Once you use an updater on a model, it permanently stains it.

What do I mean by stain? It leaves a mark. Its not a huge deal, and doesn’t affect the model, but from a software developers point of view, its not ideal. If you run a tool, such as my line styling tool, and then someone else opens your model, or even links it into their project, they get a warning saying “the file … was modified by the third-party updater …… which is not currently installed”.

If you continue to edit the file, data maintained by …. will not be updated properly. This may create problems when … is later opened when …. is present.

wallwarning

The user can easily just click the second option ‘Do not warn about this updater again and continue working with the file’. But I believe every user will have to do this (have not confirmed this though, would love to hear if this is not the case), and I’d imagine, if you send your model to other firms to link in, they will all get it. This is not a good user experience, and would scare or confuse some users.

The intentions behind the warning are good, the Revit team obviously doesn’t want problems seen in AutoCAD where third party add-ins are so intertwined with projects that they cause a troubleshooting nightmare. But for simple time saving add-ins like my line styling tool, this is inconvenient. I’d love for there to be an option where developers can say that honestly if the updater is removed it will not cause any problems, and then that error will not show, hopefully next release!

All in all, this addition is great, and makes a range of great new add-ins to be made, but when your tool is not installed, users get this dialog, which could be confusing. If this doesn’t affect you, then I encourage you to get in there and have a play around, its easier than you’d think it would be, and I’m sure you’ll come up with some great ideas of how you could utilize it.

Monday, April 27, 2009

Revit: 2010 API Developer Guide

I stumbled across this document today whilst setting up my SDK Samples menus in Revit.

It’s located in the Revit 2010 SDK folder (there’s an extractable on the Revit disc to get this folder) and spotted the ‘Revit 2010 API Developer Guide’.

It’s a 315 page PDF with a whole heap of tutorials, code samples and tips for using the Revit API. It’s pretty much the user manual and seems to be exactly where a new Revit API user should start. This document is exactly what I dreamed of when I started developing with the Revit API a couple of years ago!

The section headings include:

  • Getting Started
  • Add-in Integration
  • Application and Document
  • Elements Essentials
  • Parameters
  • Collections
  • Editing Elements
  • Walls, Floors, Roofs and Openings
  • Family Instances
  • Family Creation
  • Datum and Information Elements
  • Annotation Elements
  • Sketching
  • Views
  • Material
  • Geometry
  • Place and Locations
  • Shared Parameters
  • Transactions
  • Events
  • Revit Architecture
  • Revit Structure
  • Revit MEP

If there isn’t anything there that interests you, you obviously aren’t interested in the Revit API. Props to Autodesk for putting this together!

Monday, March 23, 2009

Revit: How to print sheets in order.

I’ve had a bit of fun lately working with the Revit 2009/2010 API for printing views. There is a useful SDK sample, ‘ViewPrinter’ which shows you how to replicate the print menu in revit, but using the API.

I’ve created a customized version of this that links in with our companies document management systems, and does things like prints, renames, and copies PDFs to the correct directory all with the press of a button.

This has been working well for our drafters, and has sped things up, however there have been a few issues that I’ve run into whilst following the methods set out in ViewPrinter and in the SDK documentation. Most of them relate to selecting certain views (in this case, sheets) and printing them in the correct order, and with the correct amount of copies.

The way the SDK sample does this, is it gathers a list of the sheet names the user selected, compares their names with those in the PrintMan.ViewSheetSetting.AvailableViews list and selects ones that match. It then adds these into a ViewSet, and supplies that to PrintMan and calls SubmitPrint().

   1: //create Viewset store those you need to print
   2: ViewSet viewsToPrint = new ViewSet();
   3:  
   4: //Compare your list of sheets, to those in AvailableViews
   5: foreach (string name in selectedSheetNames) 
   6: {
   7:     foreach (View view in _printMan.ViewSheetSetting.AvailableViews)
   8:      {
   9:         if (view.ViewType == ViewType.DrawingSheet)
  10:         {
  11:             //cast to viewsheet
  12:             ViewSheet vs = view as ViewSheet;
  13:  
  14:             if (vs != null && name.Contains(vs.SheetNumber + " - " + vs.Name))
  15:             {
  16:                 viewsToPrint.Insert(vs);
  17:             }
  18:         }
  19:      }
  20: }
  21:  
  22: ViewSheetSet viewSheetSet = m_viewSheetSetting.CurrentViewSheetSet;
  23: viewSheetSet.Views = selectedViews;
  24: m_viewSheetSetting.Save();
  25:  
  26: //set copies
  27: _printMan.CopyNumber = numCopies;
  28:  
  29: //submit print
  30: _printMan.SubmitPrint(v); 
  31:  
  32:  

This works great ok normally, however there is a significant problem. The problem lies in how the views are stored. The views are placed into a ViewSet, which is an implementation of the Set collection type, ie, an unordered set of items. Unfortunately, this means that us coders have no say in how the prints get ordered.

My experimentation shows that revit seems to order them by ElementID, or, in the order they were created. So lets say you create Sheet 1, Sheet 2, Sheet 4, Sheet 3, in that order. That is exactly how they will come out of the printer, even though the user would assume otherwise.

My workaround for this, after much trial and error, is this:

   1: //create a list of view tp store those you need to print
   2: List<View> viewsToPrint = new List<View>();
   3:  
   4: //Compare your list of sheets, to those in AvailableViews
   5: foreach (string name in selectedSheetNames) 
   6: {
   7:     foreach (View view in _printMan.ViewSheetSetting.AvailableViews)
   8:      {
   9:         if (view.ViewType == ViewType.DrawingSheet)
  10:         {
  11:             //cast to viewsheet
  12:             ViewSheet vs = view as ViewSheet;
  13:  
  14:             if (vs != null && name.Contains(vs.SheetNumber + " - " + vs.Name))
  15:             {
  16:                 viewsToPrint.Add(vs);
  17:             }
  18:         }
  19:      }
  20: }
  21: //loop through copies 
  22: for (int i = 1; i <= numCopies; i++) 
  23: { 
  24:  //loop through each view 
  25:  foreach (View v in viewsToPrint) 
  26:  { 
  27:   //submit print of just 1 view 
  28:    _printMan.SubmitPrint(v); 
  29:   } 
  30: }

There are a number of steps behind the scenes (such as setting up _printMan aka my PrintManager to have the correct settings) but instead of supplying PrintManager with a ViewSet of views I want to print, and changing the CopyNumber to how many times I wish to print them, I’m printing one view at a time using the overload for SubmitPrint. This allows me to order my list of selectedSheetNames in the way I wish it to come out, including sorting and reversing it. I’ve also done a for loop to replicate the CopyNumber property.

Now, at long last, my prints are coming out in the correct order!

Note: This code was created for Revit 2010, not fully tested in 2009, though most of it should work the same.