While working with the ‘File Upgrader for Revit’ app as a candidate for the ‘Plugin of the Month’ series which is part of the Autodesk Labs (with the same utility now part of Revit Exchange Apps Store), I had a detailed and in-depth conversation with the Revit API Development team on ways to set the preview of a view for a Revit document using the API. This blog post cover the inferences from the discussions in details and I hope this will be useful for many developers having a similar requirement:
There are two ways of setting a preview image on a Revit document using the API.
The first approach uses DocumentPreviewSettings to assign a permanent view to the document to always be used for preview settings. This is done by serializing this preview id into the document. So using this approach, the preview view will be permanently set for the document.
In the user interface, when we save a file, typically a permanent view for the preview generation is not assigned. It is only assigned if the users click on Options button the SaveAs dialog and select a view other than “Active view/sheet” in the Preview Source of the dialog. It is this permanent view that can be extracted and set using the DocumentPreviewSettings API. If there is no permanent preview view assigned here, Revit will use the default active view/sheet to generate preview.
The other approach is a temporary way to set the preview view id which will be used during the single save operation and not serialized in the document. This can be done using the Document.Save(SaveOptions) or Document.SaveAs(SaveAsOptions). Both these options classes have a setting for the preview view id.
In a typical use case of OpenDocumentFile(), performing certain changes to the document and then saving the file either using a Save or SaveAs results in Revit having no way to know how to save a preview and so previews are lost. This is where the Save(SaveOptions) or SaveAs(SaveAsOptions) help. However, setting the temporary preview view settings using the save options can become challenging since API users would still not know which view to be set as preview – the Document.ActiveView does not work for an un-displayed document. Here, there are two ways of approaching this –
The following lines of code show a typical use of the various APIs which help in setting the preview. As you would notice, it covers all the various approaches mentioned in this post.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.ApplicationServices;
namespace Revit.SDK.Samples.HelloRevit.CS
{
[Transaction(TransactionMode.Manual)]
public class Command : IExternalCommand
{
Autodesk.Revit.ApplicationServices.Application app = null;
public Result Execute(ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
// Open Revit file
Document doc
= commandData.Application.Application.OpenDocumentFile(
file.FullName);
// Create transaction and start
Transaction trans = new Transaction(doc, "T1");
trans.Start();
doc.Regenerate();
// Create thumbnails for preview
DocumentPreviewSettings previewSettings =
doc.GetDocumentPreviewSettings();
SaveAsOptions saveOpts = new SaveAsOptions();
// Check for permanent preview view
if (doc.GetDocumentPreviewSettings().PreviewViewId
.Equals(ElementId.InvalidElementId))
{
// Access the intial view
StartingViewSettings startingViewSettings =
StartingViewSettings.GetStartingViewSettings(doc);
if (!startingViewSettings.ViewId.Equals(ElementId.InvalidElementId))
{
// If valid, then set the viewId
//previewSettings.PreviewViewId = startingViewSettings.ViewId;
saveOpts.PreviewViewId = startingViewSettings.ViewId;
}
else
{
// Algorithmic approach - look for 3D views
FilteredElementCollector collector =
new FilteredElementCollector(doc);
collector.OfClass(typeof(Autodesk.Revit.DB.View));
IEnumerable views
= from Autodesk.Revit.DB.View f
in collector
where (f.ViewType == ViewType.ThreeD
&& !f.IsTemplate)
select f;
Boolean bNotFound = true;
foreach (Autodesk.Revit.DB.View vw in views)
{
if (!vw.IsTemplate)
{
//If we were to set the preview view permanently,
// we would use the following two commented lines
//previewSettings.PreviewViewId = vw.Id;
//previewSettings.ForceViewUpdate( true );
// Set the temporary view
saveOpts.PreviewViewId = vw.Id;
bNotFound = false;
break;
}
}
// If no 3D view is obtained, look for other valid views
if (bNotFound)
{
IEnumerable<Autodesk.Revit.DB.View> viewsNon3D
= from Autodesk.Revit.DB.View fNon3D
in collector
where (fNon3D.ViewType == ViewType.FloorPlan
|| fNon3D.ViewType == ViewType.EngineeringPlan
|| fNon3D.ViewType == ViewType.Elevation
|| fNon3D.ViewType == ViewType.Section
&& !fNon3D.IsTemplate)
select fNon3D;
foreach (Autodesk.Revit.DB.View vw in viewsNon3D)
{
if (!vw.IsTemplate)
{
// If we were to set the preview view permanently,
// we would use the following two commented lines
//previewSettings.PreviewViewId = vw.Id;
//previewSettings.ForceViewUpdate( true );
saveOpts.PreviewViewId = vw.Id;
break;
}
}
}
}
}
// Commit transaction
trans.Commit();
// Save Revit file to target destination
doc.SaveAs(destPath + "\\" + file.Name, saveOpts);
// Close document
doc.Close();
return Result.Succeeded;
}
}
}