The Revision API and a Form on the Fly

Poetical, ain’t it?

One of the

major Revit 2015 API additions
is
access to

revisions
.

All prior versions provided very limited access to revision data in a project.
Here are some things people achieved in spite of the limitations:

Let’s now take a look at an elegant example of accessing and displaying the complete revision data in a project,

GetRevisionData
,
prompted by the following query by Dan Tartaglia of

design technology@NBBJ
:

Question: Selecting the View tab in Revit and then Revisions in the Sheet Composition pane displays the ‘Sheet Issues/Revisions’ dialogue:

Sheet Issues Revisions dialogue

I am trying to access the information displayed programmatically, in particular the information for these revisions found in the ‘Show’ column with the three possible choices ‘None’, ‘Tag’ and ‘Cloud and Tag’.

Is that possible?

I am currently using the GetAllProjectRevisionIds method, but that does not return all the required information.

Answer: What you ask for is now possible in Revit 2015 using the new

revision classes
.

I am not aware of any way to access it programmatically in Revit 2014, though.
Developers have been asking for it for a long time, and that access was one of the major Revit 2015 API enhancements.

Examining your

Revit 2014 GetRevisionData attempt
in
a little more detail, I have some comments on that before getting to the Revit 2015 solution.

It includes a nice utility method GetParameterInformation to convert a parameter value to a string representation.
That is used by the ParamsFromGetAllRevElements method to retrieve all project revisions via their ids and list all their parameter values.

However, as said, it does not return the information we are after.

I fixed the

architecture mismatch warnings
in
the initial 2014 project using my recursive project parser and fixer

DisableMismatchWarning.exe
.

I like the parameter to value to string converter, so let’s list it here for posterity to enjoy:


  /// <summary>
  /// Extract the parameter information.
  /// By Dan Tartaglia.
  /// </summary>
  public string GetParameterInformation(
    Parameter para,
    Document doc )
  {
    string defName = "";
 
    // Use different method to get parameter 
    // data according to the storage type
 
    switch( para.StorageType )
    {
      // Determine the parameter type 
 
      case StorageType.Double:
 
        // Convert the number into Metric
 
        defName = para.AsValueString();
        break;
 
      case StorageType.ElementId:
 
        // Find out the name of the element
 
        Autodesk.Revit.DB.ElementId id
          = para.AsElementId();
 
        defName = ( id.IntegerValue >= 0 )
          ? doc.GetElement( id ).Name
          : id.IntegerValue.ToString();
 
        break;
 
      case StorageType.Integer:
        if( ParameterType.YesNo
          == para.Definition.ParameterType )
        {
          if( para.AsInteger() == 0 )
          {
            defName = "False";
          }
          else
          {
            defName = "True";
          }
        }
        else
        {
          defName = para.AsInteger().ToString();
        }
        break;
 
      case StorageType.String:
        defName = para.AsString();
        break;
 
      default:
        defName = "Unexposed parameter";
        break;
    }
    return defName;
  }

One little suggestion I have is to encapsulate the text writer instance in a using block, e.g. like this:


using( TextWriter tw = new StreamWriter(
"C:/tmp/RevisionTest.txt" ) )
{
tw.WriteLine( ". . ." );
tw.Close();
}

For comparison with the Revit 2015 results, let’s list the limited data accessible via the Revit 2014 API here, printed out to a text file C:/tmp/RevisionTest.txt:


C:tmp>cat RevisionTest.txt
Hidden = False
Element Name: Revisions
oParamRevEnum = 0
oParamRevDate = Date 1
oParamRevDescrip = Revision 1
oParamRevIssued = False
oParamRevIssuedBy =
oParamRevIssuedTo =
oParamRevNumber = 1
oParamSeqNumber = 1
==============================
Hidden = False
Element Name: Revisions
oParamRevEnum = 0
oParamRevDate = Date 2
oParamRevDescrip = Revision 2
oParamRevIssued = False
oParamRevIssuedBy =
oParamRevIssuedTo =
oParamRevNumber = 2
oParamSeqNumber = 2
==============================
Hidden = False
Element Name: Revisions
oParamRevEnum = 0
oParamRevDate = Date 5
oParamRevDescrip = Revision 5
oParamRevIssued = False
oParamRevIssuedBy =
oParamRevIssuedTo =
oParamRevNumber = 5
oParamSeqNumber = 5
==============================

I published the Revit 2014 version in the

GetRevisionData GitHub repository
as

version 2014.0.0.0
.

Response: Yes, I verified that I can get what I need with the Revit 2015 API like this:


  IList<ElementId> oElemIDs = oViewSheet.GetAllRevisionIds();
 
  if( oElemIDs.Count == 0 )
    return;
 
  foreach( ElementId elemID in oElemIDs )
  {
    Element oEl = doc.GetElement( elemID );
 
    Revision oRev = oEl as Revision;
 
    // Add text line to text file
    tw.WriteLine( "Rev Category Name: " + oRev.Category.Name );
 
    // Add text line to text file
    tw.WriteLine( "Rev Description: " + oRev.Description );
 
    // Add text line to text file
    tw.WriteLine( "Rev Issued: " + oRev.Issued.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Issued By: " + oRev.IssuedBy.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Issued To: " + oRev.IssuedTo.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Number Type: " + oRev.NumberType.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Date: " + oRev.RevisionDate );
 
    // Add text line to text file
    tw.WriteLine( "Rev Visibility: " + oRev.Visibility.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Sequence Number: " + oRev.SequenceNumber.ToString() );
 
    // Add text line to text file
    tw.WriteLine( "Rev Number: " + oRev.RevisionNumber );
  }

Answer: Congratulations on solving it!

I am glad that the Revit 2015 API provides all you need.

I implemented a sample command grabbing all the revision data displayed in the Revit ‘Sheet Issues/Revisions’ form and displaying that in a Windows form generated on the fly.

It avoids the text writer and file output completely by implementing a revision data holder class and a container dictionary, using a Windows forms data grid view container and its DataSource property to access and display the data:

Revision data

It demonstrates several other nice features, in addition to the revision functionality:

  • Accessing and displaying all the revision information without ever actually touching or formatting any individual data members.
  • Generating a Windows form programmatically on the fly.
  • Populating a DataGridView via its DataSource property.

The Revit 2015 version is published in the

GetRevisionData GitHub repository
as

version 2015.0.0.0
.

The hardest challenge was actually not implementing the DataGridView population, but the automatic resizing.
That took quite a while, testing and debugging numerous combinations of the automatic resizing properties until finally finding one that worked.

The complete external command implementation defines just three simple little items:

  • RevisionData – a container for the revision data displayed in the Revit ‘Sheet Issues/Revisions’ dialogue.
  • DisplayRevisionData – generate a Windows modeless form on the fly and display the revision data in it in a DataGridView.
  • Execute – external command mainline.

Each one on its own is pretty trivial.

Together, they form a rally neat and elegant solution, I think:


#region Namespaces
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Windows;
using TaskDialog = Autodesk.Revit.UI.TaskDialog;
#endregion
 
namespace GetRevisionData
{
  /// <summary>
  /// External command demonstrating how to use the 
  /// Revit 2015 Revision API to retrieve and display
  /// all infoormation shown in the Revit 
  /// 'Sheet Issues/Revisions' dialogue.
  /// </summary>
  [Transaction( TransactionMode.ReadOnly )]
  public class Command : IExternalCommand
  {
    /// <summary>
    /// A container for the revision data displayed in
    /// the Revit 'Sheet Issues/Revisions' dialogue.
    /// </summary>
    class RevisionData
    {
      public int Sequence { get; set; }
      public RevisionNumberType Numbering { get; set; }
      public string Date { get; set; }
      public string Description { get; set; }
      public bool Issued { get; set; }
      public string IssuedTo { get; set; }
      public string IssuedBy { get; set; }
      public RevisionVisibility Show { get; set; }
 
      public RevisionData( Revision r )
      {
        Sequence = r.SequenceNumber;
        Numbering = r.NumberType;
        Date = r.RevisionDate;
        Description = r.Description;
        Issued = r.Issued;
        IssuedTo = r.IssuedTo;
        IssuedBy = r.IssuedBy;
        Show = r.Visibility;
      }
    }
 
    /// <summary>
    /// Generate a Windows modeless form on the fly 
    /// and display the revision data in it in a 
    /// DataGridView.
    /// </summary>
    void DisplayRevisionData(
      List<RevisionData> revision_data,
      IWin32Window owner )
    {
      System.Windows.Forms.Form form
        = new System.Windows.Forms.Form();
 
      form.Size = new Size( 680, 180 );
      form.Text = "Revision Data";
 
      DataGridView dg = new DataGridView();
      dg.DataSource = revision_data;
      dg.AllowUserToAddRows = false;
      dg.AllowUserToDeleteRows = false;
      dg.AllowUserToOrderColumns = true;
      dg.Dock = System.Windows.Forms.DockStyle.Fill;
      dg.Location = new System.Drawing.Point( 0, 0 );
      dg.ReadOnly = true;
      dg.TabIndex = 0;
      dg.Parent = form;
      dg.AutoSize = true;
      dg.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
      dg.AutoResizeColumns( DataGridViewAutoSizeColumnsMode.AllCells );
      dg.AutoResizeColumns();
 
      form.ShowDialog( owner );
    }
 
    public Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
    {
      IWin32Window revit_window
        = new JtWindowHandle(
          ComponentManager.ApplicationWindow );
 
      UIApplication uiapp = commandData.Application;
      UIDocument uidoc = uiapp.ActiveUIDocument;
      Document doc = uidoc.Document;
 
      if( doc.IsFamilyDocument )
      {
        TaskDialog.Show( "Not a Revit RVT Project",
          "This command requires an active Revit RVT file." );
 
        return Result.Failed;
      }
 
      if( !( doc.ActiveView is ViewSheet ) )
      {
        TaskDialog.Show( "Current View is not a Sheet",
          "This command requires an active sheet view." );
        return Result.Failed;
      }
 
      IList<ElementId> ids
        = Revision.GetAllRevisionIds( doc );
 
      int n = ids.Count;
 
      List<RevisionData> revision_data
        = new List<RevisionData>( n );
 
      foreach( ElementId id in ids )
      {
        Revision r = doc.GetElement( id ) as Revision;
 
        revision_data.Add( new RevisionData( r ) );
      }
 
      DisplayRevisionData( revision_data,
        revit_window );
 
      return Result.Succeeded;
    }
  }
}

Enjoy!

Next time I write will be from Toronto,
  إن شاء الله
  (insha’Allah, God willing).


Comments

6 responses to “The Revision API and a Form on the Fly”

  1. Matt Taylor Avatar
    Matt Taylor

    Hi Jeremy,
    I had the issue with auto-sizing gridview columns also.
    I did a little research, and found that it’s easier to set it per column (hope this formats okay!):
    Public Sub SetDataGridColumnWidthMode(ByVal column As Integer, ByVal mode As DataGridViewAutoSizeColumnMode)
    dgData.Columns(column).AutoSizeMode = mode
    End Sub
    mainForm.SetDataGridColumnWidthMode(2, DataGridViewAutoSizeColumnMode.DisplayedCells)
    mainForm.SetDataGridColumnWidthMode(3, DataGridViewAutoSizeColumnMode.Fill)

  2. Dear Matt,
    Thank you for the suggestion!
    Did you try it out in this case?
    I did, and it appears that due to my usage of the DataSource property, I have no columns in that sense.
    The Count property of Columns is zero, so attempting to make calls like this throws an exception:
    dg.Columns[2].AutoSizeMode
    = DataGridViewAutoSizeColumnMode.DisplayedCells;
    dg.Columns[3].AutoSizeMode
    = DataGridViewAutoSizeColumnMode.Fill;
    Cheers, Jeremy.

  3. Matt Taylor Avatar
    Matt Taylor

    Hi Jeremy,
    Ah, sorry that didn’t help. (I didn’t test it.)
    The thing I used it for was printer devices, their status, and queue length etc (read-only), so a DataTable was a perfect Datasource for that.
    From what I read about the column count on the datasource (list) issue, it’s to do with late-binding of data – so I guess your solution works best!!
    I hope you’re having fun in Toronto!
    Kind regards,
    Matt

  4. Dear Matt,
    Thank you for the suggestion, anyway, and for your good wishes!
    Cheers, Jeremy.

  5. Vipul Surana Avatar
    Vipul Surana

    Hello Jeremy,
    I didn’t understand the above example completely.
    I have questions like: which file are you talking about ? there is no namespace and the filename + extension.
    I am a very layman person, just started working 2 days before on Revit 2016.
    Need to ask you how to get started with creating a solution (class library / usercontrol library / wpf application) and connect with Revit, there are no proper guidelines for this.
    Can you make a layman blog post for me and for the starters.
    I am very desperate to learn this tech.

  6. Dear Vipul,
    You will be very happy to hear that we have already created a full set of getting started material:
    http://thebuildingcoder.typepad.com/blog/about-the-author.html#2
    I guarantee that it covers all your needs :-)
    Cheers, Jeremy.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading