Here is another issue of general interest and that arose during the DevLab in Waltham last week:
Question: How can I export some Revit model data to an XML file, for instance certain sheet properties?
Answer: That is actually very easy. All you need to do is:
- Collect the Revit elements of interest, for instance all sheets.
- Extract and save the data of interest from them, for instance the sheet number.
- Iterate over the data collection and export it to XML.
You could of course also skip saving the data in an intermediate container and export it to an external file directly. Adding the intermediate step might be useful, for instance to sort the data items or add other processing.
Exporting to XML is easy, because the .NET framework includes lots of XML formatting functionality. It would also be easy to generate your own XML output file by hand, though.
Here is a super simple little ViewSheet data container to use for the intermediate storage:
class SheetData { public bool IsPlaceholder { get; set; } public string Name { get; set; } public string SheetNumber { get; set; } public SheetData( ViewSheet v ) { IsPlaceholder = v.IsPlaceholder; Name = v.Name; SheetNumber = v.SheetNumber; } }
Obviously this data holder is rather overly simplistic, but you can easily add other items of interest to you to it.
I implemented a new Building Coder sample command which performs the steps outlined above making use of this data container class:
[Transaction( TransactionMode.ReadOnly )] [Regeneration( RegenerationOption.Manual )] class CmdSheetData : IExternalCommand { public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { UIApplication app = commandData.Application; Document doc = app.ActiveUIDocument.Document; // retrieve all sheets FilteredElementCollector a = new FilteredElementCollector( doc ); a.OfCategory( BuiltInCategory.OST_Sheets ); a.OfClass( typeof( ViewSheet ) ); // create a collection of all relevant data List<SheetData> data = new List<SheetData>(); foreach( ViewSheet v in a ) { // create some data for each sheet and add // to some serializable collection called Data SheetData item = new SheetData( v ); data.Add( item ); } // write out data collection to xml XmlTextWriter w = new XmlTextWriter( "C:/SheetData.xml", null ); w.Formatting = Formatting.Indented; w.WriteStartDocument(); w.WriteComment( string.Format( " SheetData from {0} on {1} by Jeremy ", doc.PathName, DateTime.Now ) ); w.WriteStartElement( "ViewSheets" ); foreach( SheetData item in data ) { w.WriteStartElement( "ViewSheet" ); w.WriteElementString( "IsPlaceholder", item.IsPlaceholder.ToString() ); w.WriteElementString( "Name", item.Name ); w.WriteElementString( "SheetNumber", item.SheetNumber ); w.WriteEndElement(); } w.WriteEndElement(); w.WriteEndDocument(); w.Close(); return Result.Succeeded; } }
Here is the XML file resulting from running this command in a simple Revit model:
<?xml version="1.0"?> <!-- SheetData from C:\tmp\sheets_and_views.rvt on 2010-06-12 18:16:48 by Jeremy --> <ViewSheets> <ViewSheet> <IsPlaceholder>False</IsPlaceholder> <Name>Unnamed</Name> <SheetNumber>A101</SheetNumber> </ViewSheet> <ViewSheet> <IsPlaceholder>False</IsPlaceholder> <Name>Unnamed</Name> <SheetNumber>A102</SheetNumber> </ViewSheet> <ViewSheet> <IsPlaceholder>False</IsPlaceholder> <Name>Unnamed</Name> <SheetNumber>A103</SheetNumber> </ViewSheet> <ViewSheet> <IsPlaceholder>False</IsPlaceholder> <Name>Unnamed</Name> <SheetNumber>A104</SheetNumber> </ViewSheet> </ViewSheets>
I initially created this as a stand-alone application, and here is the add-in manifest file, source code, and Visual Studio solution for that, compressed in the archive file SheetData.zip
As said, since it seems useful to keep track of this simple XML exporting functionality for future use as well, I also added the command to The Building Coder samples. Here is version 2011.0.72.0 of the complete source code and Visual Studio solution including the new command. Since I use a RvtSamples include file BcSamples.txt to load The Building Coder samples, included in the archive file, I provide no separate add-in manifest files for these commands.
Hi Jeremy,
I am trying to set viewsheet parameters from a text file, and have a mysterious exception upon commit. I have an error handler that does not capture an error, so am at a loss and have begun breaking things apart.
I read the values from a text file, so my outer loop is a streamreader.ReadLine() call
Then I loop through my viewSheets collector to find the right sheet. It strikes me that I may be better off using LINQ to find the particular sheet, but cannot find a suitable example. I am assuming that Revit won't allow a sheet to have a Sheet Number that is not unique (untested assumption for now).
So here is what I'm doing, but the compiler doesn't like it. Can you help?
string[] linearray;
ViewSheet vQuery;
FilteredElementCollector Sheets = new
FilteredElementCollector(doc).OfClass(typeof(ViewSheet));
var query = from element in Sheets
where
element.get_Parameter(KeyParameter.Text).AsString() == linearray[0]
select element; //linearray is a split (\t) on the ReadLine
if (query.Count.AsValue() > 0) //doesn't like this
vQuery = (ViewSheet) query.ElementAt(0); // I know this is wrong . . . how do I get the actual sheet?
Thanks for your help!
Posted by: Paul Gibson | September 10, 2014 at 11:15
Dear Paul,
First you talk about an exception. That is a runtime error.
Then you say the compiler is complaining. That is a compile time error, and not an exception.
Anyway, I would suggest that you search for some examples of using the .NET generic templated collection extension method FirstOrDefault in conjunction with a FilteredElementCollector.
http://lmgtfy.com/?q=FirstOrDefault+FilteredElementCollector
I use that myself on a regular basis, and I think that does exactly what you need.
LINQ will not be any better than anything else.
The only thing that makes a difference performance-wise is whether you use the Revit filtered element collector functionality, which is very fast, or something based on .NET that post processes the results of the former. That is significantly slower.
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 10, 2014 at 15:57
Hi Jeremy,
I am looking to read xml data into a revit addin. Are there any Revit specific tutorials I could possibly read up on?
Cheers,
Frank
Posted by: Frank Halliday | October 20, 2014 at 07:42
Dear Frank,
There are about umpteen zillion .NET XML libraries you can use, plus this functionality is provided by the standard .NET framework:
http://lmgtfy.com/?q=.NET+XML+library
Cheers, Jeremy.
Posted by: Jeremy Tammik | October 20, 2014 at 07:50
thanx jeremy, thats C# power I love it
Posted by: Frank Halliday | October 22, 2014 at 08:09
:-)
Posted by: Jeremy Tammik | October 22, 2014 at 09:11
Hi Jeremy,
Why do we need to set the transaction property to read-only? How can I edit the family doc to acquire it's path then famdoc.close() it in readonly mode as this causes an exception.
cheers
fabs
Posted by: frank halliday | November 08, 2014 at 03:08