Today, let's highlight two view related data access topics:
An interesting in-depth conversation in the Revit API discussion forum thread on how to get ViewSheet from View presents two different approaches to determine relationships between sheets and the views they host:
Question: Is there a way to get the ViewSheet
or ElementId
of the ViewSheet
from a View
that is placed on that sheet?
I can get the sheet name and sheet number from the provided parameters but do not see one for the actual ViewSheet. Am I missing something? The only thing I can think of to do without this info is to loop through all ViewSheets to match the one with the same sheet number (since sheet number is required to be unique).
Answer: Have you looked at the sample code provided in the description of the ViewSheet class?
ICollection<ElementId> views = viewSheet.GetAllPlacedViews(); message += "\nNumber of views in the sheet : " + views.Count;
If all else fails, you could use this relationship and invert it, just as you suggest.
The View.Title property also generally consists of the view name plus other modifiers, such as the view type, sheet number, area scheme, and/or assembly type, depending on the specifics of the view:
Response: You must have misunderstood my question. I don't have the ViewSheet in order to use the method to get its placed views. I have a view, that I know is on a sheet (because its sheet number parameter is not null), but I want to get a direct reference to that sheet that it is placed on. It seems to me that you are still suggesting that I must loop through all ViewSheets to get the View that I'm concerned with. Thanks.
Answer: Yes, indeed, that is exactly what I am suggesting.
Get all the view sheets, keep track of them and the views they host, and invert that relationship, as described in one of the very early discussions by The Building Coder back in 2008 on a relationship inverter.
That gives you a complete dictionary lookup both ways: you can look up the sheet hosting any view and you can look up all the views hosted by any sheet, instantaneously.
It takes a moment to set up the relationships; after that, all data is available and lookup is very fast.
Other answer: I maybe have a workaround for this question, something similar I assume.
Select some views in the Project Browser to know if the views are placed on sheet(s).
Then, run a macro or external command to execute something like code below.
If the view is placed on a sheet, it shows the sheet number, sheet name and whatever data you are interested in from the ViewSheet referenced by the views selected in the Project Browser:
public void GetViewSheetFromView( UIDocument uidoc ) { Document doc = uidoc.Document; string data = ""; ICollection<ElementId> selectedIds = uidoc.Selection.GetElementIds(); foreach( ElementId selectedid in selectedIds ) { View e = doc.GetElement( selectedid ) as View; foreach( View v in new FilteredElementCollector( doc ) .OfClass( typeof( View ) ) .Cast<View>() .Where( q => q.Id.Equals( e.Id ) ) ) { string thisSheet = ""; foreach( ViewSheet vs in new FilteredElementCollector( doc ) .OfClass( typeof( ViewSheet ) ) .Cast<ViewSheet>() ) { foreach( ElementId eid in vs.GetAllPlacedViews() ) { View ev = doc.GetElement( eid ) as View; if( ev.Id == v.Id ) { thisSheet += vs.SheetNumber + " - " + vs.Name + Environment.NewLine; break; } } } if( thisSheet != "" ) { data += v.ViewType + ": " + v.Name + " " + Environment.NewLine + thisSheet.TrimEnd( ' ', ',' ) + Environment.NewLine; } else { data += v.ViewType + ": " + v.Name + " " + Environment.NewLine + thisSheet.TrimEnd( ' ', ',' ); data += " NOT ON SHEET " + Environment.NewLine + "\n"; } } } TaskDialog.Show( "View Report", data ); }
Yet another answer: You can perhaps get VIEWPORT_SHEET_NUMBER from Parameters.
Yet another answer: As suggested above, using the BIP for viewport sheet number with an ElementParameterFilter is probably the best approach. Sheet numbers are unique in each Revit model, so it is safe to search by them and get the right result.
Public Function TObj43(ByVal commandData As ExternalCommandData, ByRef message As String, ByVal elements As ElementSet) As Result If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Cancelled Else Dim AcView As View = commandData.Application.ActiveUIDocument.ActiveGraphicalView Dim P_Ns As Parameter = AcView.Parameter(BuiltInParameter.VIEWPORT_SHEET_NUMBER) If P_Ns Is Nothing Then GoTo Monday End If Dim Txt As String = P_Ns.AsString If String.IsNullOrEmpty(Txt) Then GoTo Monday Else Dim SeFR As FilterRule = ParameterFilterRuleFactory.CreateEqualsRule( New ElementId(BuiltInParameter.SHEET_NUMBER), Txt, True) Dim PFilt As New ElementParameterFilter(SeFR, False) Dim FEC As New FilteredElementCollector(AcView.Document) Dim ECF As New ElementClassFilter(GetType(ViewSheet)) Dim LandF As New LogicalAndFilter(ECF, PFilt) Dim Els As List(Of Element) = FEC.WherePasses(LandF).ToElements If Els.Count <> 1 Then GoTo Monday Else Dim TD As New TaskDialog("Was this your sheet...") TD.MainInstruction = Txt & "-" & Els(0).Name TD.MainContent = "Note that view types that can appear on " _ + "multiple sheets (Legends, Images, Schedules etc.) will " _ + " not be returned by this method." TD.Show() GoTo Friday End If Monday: TaskDialog.Show("Something went amiss...", "We looked hard in many places but were unable to " _ + "find your sheet on this occasion.") Return Result.Failed Friday: Return Result.Succeeded End Function
Yet another answer: Here is a pretty quick way of checking for a specific sheet:
private ViewSheet CheckSheet( string _sheetNumber ) { ParameterValueProvider pvp = new ParameterValueProvider( new ElementId( BuiltInParameter.SHEET_NUMBER ) ); FilterStringRuleEvaluator fsr = new FilterStringEquals(); FilterRule fRule = new FilterStringRule( pvp, fsr, _sheetNumber, true ); ElementParameterFilter filter = new ElementParameterFilter( fRule ); if( new FilteredElementCollector( doc ) .OfCategory( BuiltInCategory.OST_Sheets ) .WherePasses( filter ) .FirstOrDefault() is ViewSheet vs ) { return vs; } else { return null; } }
Summary: I believe this last solution is unbeatable if you are interested in one single lookup.
It uses a parameter filter and the SHEET_NUMBER
built-in parameter.
Probably, the VB function TObj43
above is similarly efficient.
GetViewSheetFromView
demonstrates the lookup of the inverted relationship I described, but just for one single view.
That code could be used to store the entire relationships mapping sheet to hosted views and the inverted one mapping each view to hosting sheets for all views and sheets. That might possibly be more efficient if you frequently need to look up several different view to sheet relationships.
Thank you all for the very illuminating and helpful answers!
Question: I am searching for some sample code for use in a Design Automation API project.
Rather than reinvent to wheel, I thought you might have a snippet handy showing how to read Revit drawing title block attributes like these:
Answer: I discussed accessing the title block of a sheet myself back in 2009, but that information is rather antiquated now.
The question was also raised and answered in the Revit API discussion forum thread on TitleBlock, and that information is perfectly valid.
Ah, I now found a more recent and useful article on how to determine sheet size that should provide all you need.
The code is included in The Building Coder samples module CmdSheetSize.cs.
The title block instances are family instance elements. You can access the title block element using a filtered element collector, e.g.:
FilteredElementCollector title_block_instances = new FilteredElementCollector( doc ) .OfCategory( BuiltInCategory.OST_TitleBlocks ) .OfClass( typeof( FamilyInstance ) );
You can loop through these elements and retrieve the required data from their built-in parameters, such as SHEET_NAME, SHEET_NUMBER, SHEET_DRAWN_BY, SHEET_CHECKED_BY etc., like this:
foreach( FamilyInstance e in a ) { p = e.get_Parameter( BuiltInParameter.SHEET_NUMBER ); Debug.Assert( null != p, "expected valid sheet number" ); string sheet_number = p.AsString(); p = e.get_Parameter( BuiltInParameter.SHEET_WIDTH ); Debug.Assert( null != p, "expected valid sheet width" ); string swidth = p.AsValueString(); double width = p.AsDouble(); . . . }
Als always, you can use RevitLookup to explore this data interactively yourself in your own model to see which properties are available where and what other title block information may be of interest to your application.