ImportExport Update

I mentioned some features of the ImportExport SDK sample in previous posts, e.g. on porting it from C# to VB, setting the DWG export filename, modifying the DWF export filename, and exporting a 3D view to 2D DWF.

Now Kurt Schrempp ran into a little problem with this sample. He says: I solved my problem. Here is the issue: When you try to export as DWG, it requires and gives you options to define the layer mapping. If you don't open up the layer mapping options dialogue, no default settings are set, and a null argument exception is thrown.

Kurt provided a slightly updated version which fixes that and some other minor problems of the standard implementation. The main fix is to automatically open the dialogue so that the default options are instantiated and set. If this step is not performed, and the associated property is left set to its default value of null, the sample fails in Revit 2012.

In Kurt's words: There are actually two errors due to similar things. One is based on the layer options, the other on the selected views when selecting more than just the current view.

To reproduce the layer bug, launch ImportExport > Select Export > Select DWG format > Click OK > Select Directory > Click Save.

Now, because the options button is optional, and it is skipped by default, the error should come up.

To reproduce the view bug, launch ImportExport > Select Export > Select DWG format > Click OK > Select Directory > Select the "Selected views/sheets" Radio Button > Click Options > Click Ok > Click Save.

In this one, because the "..." button to select views is optional, no default views are set, similarly to the layer options issue, so a different error comes up.

Here is an image that illustrates a solution for this:

ImportExport fix

Here is the options button click handler implementation:

private void buttonOptions_Click(
  object sender,
  EventArgs e )
{
  // Export dwg
  if( m_exportData.ExportFormat 
    == ExportFormat.DWG )
  {
    bool contain3DView = false;
 
    if( radioButtonCurrentView.Checked )
    {
      if( m_exportData.Is3DView )
      {
        contain3DView = true;
      }
    }
    else
    {
      if( m_exportData.SelectViewsData
        .Contain3DView )
      {
        contain3DView = true;
      }
    }
 
    ExportDWGData exportDWGData 
      = m_exportData as ExportDWGData;
 
    using( ExportDWGOptionsForm exportOptionsForm 
      = new ExportDWGOptionsForm( 
        exportDWGData.ExportOptionsData, 
        contain3DView ) )
    {
      exportOptionsForm.ShowDialog();
    }
  }
  // . . .
}

It sets up the DWGExportOptions parameter:

  DWGExportOptions dwgExportOptions = new DWGExportOptions();
  dwgExportOptions.ExportingAreas = m_exportOptionsData.ExportAreas;
  dwgExportOptions.ExportOfSolids = m_exportOptionsData.ExportSolid;
  dwgExportOptions.FileVersion = m_exportFileVersion;
  dwgExportOptions.LayerMapping = m_exportOptionsData.ExportLayerMapping;
  dwgExportOptions.LineScaling = m_exportOptionsData.ExportLineScaling;
  dwgExportOptions.MergedViews = m_exportOptionsData.ExportMergeFiles;
  dwgExportOptions.PropOverrides = m_exportOptionsData.ExportLayersAndProperties;
  dwgExportOptions.SharedCoords = m_exportOptionsData.ExportCoorSystem;
  dwgExportOptions.TargetUnit = m_exportOptionsData.ExportUnit;

These properties aren't set until this window initializes. So unless you click the "Options" button on the previous form, the values are null, thus throwing the exception.

All I did was make it so the options form displays as soon as the export form comes up, setting the defaults immediately.

Here is the code of my modified version of the ImportExport SDK sample which opens up the two windows automatically, so there are no null defaults.

It implements a couple of other small changes as well. Here is a list of the differing files:

Many thanks to Kurt for these insights and sharing his update!

Avoid Casting Twice

Here is an additional little note on casting efficiency.

This code accesses the directory containing an external referenced file:

  Type T = es.Current.GetType();
  if( T == typeof( ImportInstance ) )
  {
    ImportInstance imp = es.Current 
      as ImportInstance;
 
    if( imp.IsLinked )
    {
      ExternalFileReference el = ExternalFileUtils
        .GetExternalFileReference( 
          actdoc, imp.GetTypeId() );
 
      if( el != null )
      {
        directory = ModelPathUtils
          .ConvertModelPathToUserVisiblePath(
            el.GetAbsolutePath() );
      }
    }
    else
      error = "The import instance you selected"
        + " is not linked into the project.";
  }

It can be simplified by using the 'is' statement, replacing the first two lines by the following more readable statement:

  if( es.Current is ImportInstance )

We can go one step further, though, because the next line is casting to an ImportInstance. The sequence of statements saying 'if x is Y then: Z z = x as Y' is basically performing the cast twice over. You can avoid that unneccessary cost by casting once only and testing the result agains null. In this case, we can combine the first if statement with the cast and the second if statement, i.e. combine the line above with the one folowing it line and just say:

  ImportInstance imp = es.Current 
    as ImportInstance;
 
  if( null != imp && imp.IsLinked )

This eliminates the first two lines altogether, leaving just this:

  ImportInstance imp = es.Current 
    as ImportInstance;
 
  if( null != imp && imp.IsLinked )
  {
    ExternalFileReference el = ExternalFileUtils
      .GetExternalFileReference(
        actdoc, imp.GetTypeId() );
 
    if( el != null )
    {
      directory = ModelPathUtils
        .ConvertModelPathToUserVisiblePath(
          el.GetAbsolutePath() );
    }
  }
  else
  {
    error = "The import instance you selected"
      + " is not linked into the project.";
  }

For more details, you can refer to sections 8.2.2 'The is Operator', 8.2.3 'The as Operator', and especially 8.2.4 'The is Operator Versus the as Operator' in this C# tutorial.