From reading The Building Coder's post about Retrieving All Available Line Styles (https://thebuildingcoder.typepad.com/blog/2013/08/retrieving-all-available-line-styles.html) it is my understanding that detail line elements can be collected via FilteredElementCollector once a subcategory is selected? I have a line style subcategory and would like to collect all the detail lines of that style. How do I do that?
For context from the link above (bold added by me for emphasis):
"
While the Revit API does not provide a true 'Line style' element, the line styles are actually subcategories of the Lines category. Therefore, the FilteredElementCollector cannot easily be used for this in a single statement, like in your examples above.
It should be possible to retrieve the line styles without a line instance, though.
Here’s a macro that lists all subcategories of the Lines category:
public void GetListOfLinestyles( Document doc )
{
Category c = doc.Settings.Categories.get_Item(
BuiltInCategory.OST_Lines );
CategoryNameMap subcats = c.SubCategories;
foreach( Category lineStyle in subcats )
{
TaskDialog.Show( "Line style", string.Format(
"Linestyle {0} id {1}", lineStyle.Name,
lineStyle.Id.ToString() ) );
}
}
Note that some line styles like 'Room Boundary' cannot actually be assigned to arbitrary lines in the UI, but this should be good enough to find a usable one.
Once you have a collection of the line style subcategories of interest, you can create a filtered element collector retrieving all ElementType elements belonging to any one of them.
"
Solved! Go to Solution.
Solved by FAIR59. Go to Solution.
Solved by RPTHOMAS108. Go to Solution.
Solved by sragan. Go to Solution.
For example, changing OST_Lines to OST_LightFixtures will find all the line subcategories of the light fixtures.
(As you type in BuiltInCategory, you should get a list of all the subcatagories you can use.)
See the attached image. Using the OST_LightFixtures will return "Hidden Lines", "light Source", "test_lightfixturelines", and "test_lightfixturelines2".
Thanks for your reply. I don't quite understand your meaning.
I should clarify, I'm looking for the right way to use a Line Style subcategory in a FilteredElementCollector to grab all the detail lines in the project that are of that style. I don't actually use C#, that code is just from the The Building Coder's website, but I have gotten to the point in my code that I have the Line Style I want to filter with and now I would like to create a filtered element collector retrieving all ElementType elements belonging to that Line Style, using the Revit API.
I can translate from anything to anything. Maybe what I'm asking to do isn't possible, I'm not sure.
Actually, I may have found something. I looked up this older forum post: https://forums.autodesk.com/t5/revit-api-forum/filteredelementcollector-gt-get-all-instances-except-...
FilteredElementCollector collector = new FilteredElementCollector(doc); ElementCategoryFilter fi = new ElementCategoryFilter(BuiltInCategory.OST_TitleBlocks, true); ICollection<Element> collection
= collector.OfClass(typeof(FamilyInstance)).WherePasses(fi) .ToElements();
I used the ElementCategoryFilter but replaced the built-in category with my category (not built-in) and I removed the .OfClass filter.... although it is collecting 23,000+ lines which doesn't seem right either, so maybe I need to apply another filter 😕
I believe its a 3 step process. You find all lines, then narrow those down to the Detail Lines, and then narrow those down to get the line style you want.
Add a new line style called "MyNewLineStyle" (match the caps exactly) and try something like
FilteredElementCollector collector = new FilteredElementCollector(doc);
ElementCategoryFilter fi = new ElementCategoryFilter(BuiltInCategory.OST_GenericLines, true);
ICollection<Element> collection = collector.OfClass(typeof(CurveElement)).WherePasses(fi) .ToElements();
TaskDialog.Show("Number of curves", collection.Count.ToString ());
List<Element> detail_lines = new List<Element>();
foreach (Element e in collection)
{
if (e as DetailLine != null)
{
detail_lines.Add (e);
}
}
TaskDialog.Show("Number of Detail Lines", detail_lines.Count.ToString ());
List<Element> some_detail_lines = new List<Element>();
foreach (DetailLine dl in detail_lines)
{
if (dl.LineStyle.Name == "MyNewLineStyle")
{
some_detail_lines.Add(dl);
}
}
TaskDialog.Show("Number of Detail Lines of MyNewLineStyle", some_detail_lines.Count.ToString ());
this;
Usually easier to filter for objects of GraphicsStyle using ElementClassFilter rather than subcategories of OST_Lines.
GraphicsStyle has a property GraphicsStyle.GraphicsStyleCategory. When this is a subcategory of OST_Lines then it relates to either ModelCurves or DetailCurves (Note that GraphicsStyle.Category is null).
You can't use a class filter for DetailCurves and ModelCurves (which inherit from CurveElement). This base class has the property LineStyle which will be one of the GraphicsStyle elements found above.
When you have items of CurveElement you can distinguish between ModelCurves and DetailCurves as follows:
DetailCurves have OwnerViewId <> ElementId.InvalidElementId
ModelCurves have OwnerViewId = ElementId.InvalidElementId
So you see one potential route is to filter for GraphicsStyles
Filter again to find GraphicsStyle.GraphicsStyleCategory that is equal to your subcategory of lines
Then use this to find CurveElements that have such a CurveElement.LineStyle.
Finally use CurveElement.OwnerViewId to list either ModelCurves or DetailCurves
One simple way of getting valid GraphicsStyles for DetailCurves/ModelCurves is via CurveElement.GetLineStyleIds (there are many graphics styles that don't relate to lines). Otherwise check GraphicsStyle.GraphicsStyleCategory is a subcategory of OST_Lines.
Example extension methods for getting CurveElements of a given subcategory of lines or matching a GraphicsStyle:
<Extension()>
Public Function GetLinesOfCategory(Doc As Document, GraphicsStyle As GraphicsStyle, DetailLines As Boolean, Optional FromView As View = Nothing) As List(Of CurveElement)
Dim FEC As FilteredElementCollector = Nothing
If FromView Is Nothing Then
FEC = New FilteredElementCollector(Doc)
Else
FEC = New FilteredElementCollector(Doc, FromView)
End If
Dim ECF As New ElementCategoryFilter(BuiltInCategory.OST_Lines)
Dim Els As List(Of CurveElement) = FEC.WherePasses(ECF).WhereElementIsNotElementType.ToElements _
.Cast(Of CurveElement).Where(Function(x) x.LineStyle.Id = GraphicsStyle.Id _
AndAlso (x.OwnerViewId <> ElementId.InvalidElementId) = DetailLines).ToList
Return Els
End Function
<Extension()>
Public Function GetLinesOfCategory(Doc As Document, Category As Category, DetailLines As Boolean, Optional FromView As View = Nothing) As List(Of CurveElement)
Dim FECgs As New FilteredElementCollector(Doc)
Dim ECFgs As New ElementClassFilter(GetType(GraphicsStyle))
Dim Gs As List(Of GraphicsStyle) = FECgs.WherePasses(ECFgs).ToElements _
.Cast(Of GraphicsStyle).Where(Function(x) x.GraphicsStyleCategory.Id = Category.Id).ToList
If Gs.Count = 0 Then Return New List(Of CurveElement) Else
Return GetLinesOfCategory(Doc, Gs(0), DetailLines, FromView)
End Function
Should also say you could probably use:
FilteredElementCollector.WhereElementIsViewIndependent
In combination with .Excluding to find ModelCurves and Exclude them from your DetailCurves.
i.e. filtering this way first will be quicker since it happens at lower level prior to Linq but you don't have millions of these elements to sort through anyway.
there is a standard filter for detail/model lines: the CurveElementFilter
Category targetLineStyle ;
IEnumerable<GraphicsStyle> gstyles = new FilteredElementCollector(doc)
.OfClass(typeof(GraphicsStyle))
.Cast<GraphicsStyle>()
.Where(gs=> gs.GraphicsStyleCategory.Id.IntegerValue == targetLineStyle.Id.IntegerValue);
ElementId targetGraphicsStyleId = gstyles.FirstOrDefault().Id;
CurveElementFilter filter_detail = new CurveElementFilter(CurveElementType.DetailCurve);
FilterRule frule_typeId = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.BUILDING_CURVE_GSTYLE),targetGraphicsStyleId);
ElementParameterFilter filter_type = new ElementParameterFilter(new List<FilterRule>(){ frule_typeId});
IEnumerable<Element> lines = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.WhereElementIsCurveDriven()
.WherePasses(filter_detail)
.WherePasses(filter_type);
Sorry for the very basic question.
How can I run this code?
Thank you very much.
The way I extend Revit is through pyRevit. I'm sure there are other forums about getting starting with the Revit API, you should look for those. Here's the link to pyRevit's main page: https://www.notion.so/pyrevitlabs/pyRevit-bd907d6292ed4ce997c46e84b6ef67a0