If you are looking for the ultimatively painless introduction to the Forge Design Automation API, you are in luck: the Design Automation webinars are coming up soon.
If you are more interested in the desktop Revit API, the solution for creating two stacked ribbon items instead of three might be more to your taste.
In either case, the tip on reinitialising a filtered element collector before reusing it is importantissimo in both contexts:
CreateReferenceInLink
to select a face in a linked fileA new series of webinars on the Forge Design Automation APIs for AutoCAD, Inventor, Revit and 3DS Max is coming up.
You can register now to participate and also to gain access to the recordings that will be posted after the live events.
There is still time to get hands on with the new Design Automation APIs. Four webinars are scheduled in December that will cover each of the Design Automation APIs in depth. The recordings will be posted after so be sure to register to take advantage.
All webinars begin at 8:00 AM Pacific Standard Time.
Jameson Nyp, BIM Manager and IS Director at Telios Engineering in Dallas, Texas, shares a nice solution for stacking two ribbon items in his Revit API discussion forum thread on 24x24 StackedItems:
Question: This may be an easy one, but so far I am struggling to find anything specific about it.
How do you make a StackedItem
where the icons are 24x24 when there are only 2 in the stack?
It seems like it should be possible, as it is used multiple times in the modify tab:
I have been able to set the ShowText
property to false to get the 3 stacked icons, but when I use the same methodology with the 2-icon stack, it remains 16x16, regardless of the icon resolution.
I tried to obtain and change the button's height and width, minWidth and minHeight through the Autodesk.Window.RibbonItem object to no avail.
Has anyone had any success in creating these icons?
Answer: I found a solution.
In order to display the button at the 24x24 size, the Autodesk.Windows.RibbonItem.Size needs to be manually set to Autodesk.Windows.RibbonItemSize.Large enum and a 24x24 icon needs to be set to the button's LargeImage
property.
Here is a code sample:
using Autodesk.Revit.UI; using Autodesk.Windows; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Windows.Media.Imaging; using YourCustomUtilityLibrary; namespace ReallyCoolAddin { public class StackedButton { public IList<Autodesk.Revit.RibbonItem> Create( RibbonPanel ribbonPanel ) { // Get Assembly Assembly assembly = Assembly.GetExecutingAssembly(); string assemblyLocation = assembly.Location; // Get DLL Location string executableLocation = Path.GetDirectoryName( assemblyLocation ); string dllLocationTest = Path.Combine( executableLocation, "TestDLLName.dll" ); // Set Image BitmapSource pb1Image = UTILImage.GetEmbeddedImage( assembly, "Resources.16x16_Button1.ico" ); BitmapSource pb2Image = UTILImage.GetEmbeddedImage( assembly, "Resources.16x16_Button2.ico" ); BitmapSource pb1LargeImage = UTILImage.GetEmbeddedImage( assembly, "Resources.24x24_Button1.ico" ); BitmapSource pb2LargeImage = UTILImage.GetEmbeddedImage( assembly, "Resources.24x24_Button2.ico" ); // Set Button Name string buttonName1 = "ButtonTest1"; string buttonName2 = "ButtonTest2"; // Create push buttons PushButtonData buttondata1 = new PushButtonData( buttonName1, buttonTextTest, dllLocationTest, "Command1" ); buttondata1.Image = pb1Image; buttondata1.LargeImage = pb1LargeImage; PushButtonData buttondata2 = new PushButtonData( buttonName2, buttonTextTest, dllLocationTest, "Command2" ); buttondata2.Image = pb2Image; buttondata2.LargeImage = pb2LargeImage; // Create StackedItem IList<Autodesk.Revit.RibbonItem> ribbonItem = ribbonPanel.AddStackedItems( buttondata1, buttondata2 ); // Find Autodes.Windows.RibbonItems UTILRibbonItem utilRibbon = new UTILRibbonItem(); var btnTest1 = utilRibbon.getButton( "Tab", "Panel", buttonName1 ); var btnTest2 = utilRibbon.getButton( "Tab", "Panel", buttonName2 ); // Set Size and Text Visibility btnTest1.Size = RibbonItemSize.Large; btnTest1.ShowText = false; btnTest2.Size = RibbonItemSize.Large; btnTest2.ShowText = false; // Return StackedItem return ribbonItem; } } }
Many thanks to Jameson for raising and solving this issue.
Yet another important hint
from Frank @Fair59 Aarssen
on reinitialising the filtered element collector
for collection of elements created using ElementWorksetFilter
giving incorrect count:
You need to reinitialise a filtered element collector before reusing it. All the filters that you add to it are accumulated. If they are mutually exclusive, you will get zero results.
Question: I'm trying to retrieve empty worksets, but the count method of the collection of elements in a particular workset is not giving correct results. Here is my code:
FilteredElementCollector fec = new FilteredElementCollector( doc ); FilteredWorksetCollector fwc = new FilteredWorksetCollector( doc ); fwc.OfKind( WorksetKind.UserWorkset ); try { string msg = ""; int count = 0; Transaction t = new Transaction( doc ); t.Start( "Check Empty Worksets" ); foreach( Workset w in fwc ) { ElementWorksetFilter ewf = new ElementWorksetFilter( w.Id, false ); ICollection<ElementId> elemIds = fec.WherePasses( ewf ).ToElementIds(); int foundElems = elemIds.Count; TaskDialog.Show( "Elements:", w.Name + ": " + foundElems.ToString() ); if( foundElems == 0 ) { count++; msg += count.ToString() + ". " + w.Name + "\n"; } } if( count == 0 ) msg = "None"; TaskDialog.Show( "Empty Worksets: ", msg ); t.Commit(); t.Dispose(); } catch( Exception e ) { TaskDialog.Show( "Error", e.ToString() ); }
Answer: A FilteredElementCollector
isn't a static variable, but a dynamic collection.
Every time you apply a filter, the elements that don't pass the filter are removed from the collection.
So, after the first pass of the foreach
loop, the collector only contains the elements belonging to the first workset.
All those elements aren't part of the second workset (2nd pass) and therefore the collector is empty after the second pass.
Solution: reinitialize the collector in every pass:
foreach( Workset w in fwc ) { ElementWorksetFilter ewf = new ElementWorksetFilter( w.Id, false ); ICollection<ElementId> elemIds = new FilteredElementCollector( doc ) .WherePasses( ewf ) .ToElementIds(); int foundElems = elemIds.Count; count++; msg += foundElems.ToString() + ". " + w.Name + "\n"; }
Many thanks to Fair59 for yet another invaluable hint.
By the way, you might also want
to simplify your transaction handling by wrapping it in a using
statement.
However, I also wonder whether you need any transaction at all for this read-only operation.
This misunderstanding caused a similar initial problem in another recent case involving a material assets collector for appearance, structural (physical) and thermal.
Back in 2012, we discussed a pretty convoluted solution for selecting a face in a linked file.
Joshua Lumley added a comment to that old post, pointing out that:
The
CreateReferenceInLink
was added after that discussion, in Revit 2014.To select any face anywhere, all you need is this:
public static Face SelectFace( UIApplication uiapp ) { Document doc = uiapp.ActiveUIDocument.Document; IEnumerable<Document> doc2 = GetLinkedDocuments( doc ); Autodesk.Revit.UI.Selection.Selection sel = uiapp.ActiveUIDocument.Selection; Reference pickedRef = sel.PickObject( Autodesk.Revit.UI.Selection.ObjectType.PointOnElement, "Please select a Face" ); Element elem = doc.GetElement( pickedRef.ElementId ); Type et = elem.GetType(); if( typeof( RevitLinkType ) == et || typeof( RevitLinkInstance ) == et || typeof( Instance ) == et ) { foreach( Document d in doc2 ) { if( elem.Name.Contains( d.Title ) ) { Reference pickedRefInLink = pickedRef .CreateReferenceInLink(); Element myElement = d.GetElement( pickedRefInLink.ElementId ); Face myGeometryObject = myElement .GetGeometryObjectFromReference( pickedRefInLink ) as Face; return myGeometryObject; } } } else { Element myElement = doc.GetElement( pickedRef.ElementId ); Face myGeometryObject = myElement .GetGeometryObjectFromReference( pickedRef ) as Face; return myGeometryObject; } return null; }
Many thanks to Joshua for this important update.