Dim filter As New SelectionFilter(New TypedValue() {New TypedValue(DxfCode.Start, "TEXT")})
Dim result As PromptSelectionResult = ed.GetSelection(filter)
Dim optEnt As New PromptEntityOptions(
vbLf & "Select an AEC object to attach a property set")
optEnt.SetRejectMessage(
vbLf & "Selected entity is NOT an AEC object, try again...")
' "Geo" is the base class for AEC object.
' Use this if you want to apply to all the AEC objects.
optEnt.AddAllowedClass(GetType(Geo), False)
' If you are interested in only Door object, use this instead.
'optEnt.AddAllowedClass(GetType(Door), False)
Dim resEnt As PromptEntityResult = ed.GetEntity(optEnt)
If resEnt.Status <> PromptStatus.OK Then
ed.WriteMessage("Selection error - aborting")
Exit Sub
End If
' <summary>
' Tests the PickSet Flag on duct and duct fittings
' </summary>
<Autodesk.AutoCAD.Runtime.CommandMethod("PropertySetTools", "TestPickfirst", CommandFlags.Modal + CommandFlags.UsePickSet)> _
Public Sub Command_TestPickFirst()
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim ed As Editor = doc.Editor
Dim db As Database = doc.Database
'Create an array of DXF Names to use in a filter
Dim AECB_TypedValues() As TypedValue = {New TypedValue(DxfCode.Operator, "<OR"), _
New TypedValue(DxfCode.Start, "AECB_DUCTFITTING"), _
New TypedValue(DxfCode.Start, "AECB_DUCT"), _
New TypedValue(DxfCode.Operator, "OR>")}
'Create the filter and set it equal to the DXF Names created previously
Dim filter As New SelectionFilter(AECB_TypedValues)
'Create a Prompt Selection Option
Dim pso As New PromptSelectionOptions
'Create a message to display if no fittings are picked before the command is ran
pso.MessageForAdding = vbLf + "Select fittings: "
'Get the fittings from the pickfirst selection or from the message
Dim result As PromptSelectionResult = ed.GetSelection(pso, filter)
'Display the number of objects selected
If result.Status = PromptStatus.OK Then
ed.WriteMessage(String.Format("{0} objects selected", result.Value.Count.ToString))
End If
End Sub
Thank you Jeff! That worked perfectly.You're welcome! Note that you can combine same DXF values into one, like so:
Dim AECB_TypedValues() As TypedValue = {New TypedValue(DxfCode.Start, "AECB_DUCT,AECB_DUCTFITTING")}
Dim AECB_TypedValues() As TypedValue = {New TypedValue(DxfCode.Start, "AECB_DUCT*")}
Oh, and as for the filtering by the classification, I don't think that's directly possible with the base filter object. I think you will need to get the SS then loop through it to test whether to keep it in the SS or not.
Take the 'meat' out of your CommandMethod and make it a separate Method... Then, presumably, either the CommandMethod, or Event Handler could call the newly separated Method, supplying a valid selection set as a parameter, etc..
Here is the final code that I came up with to get a selection set of mvparts of a certain classification. I went with the method of filtering by DXF Name, and then iterating over the selection set and removing items that were not of the classification that I wanted.Code: [Select]<Autodesk.AutoCAD.Runtime.CommandMethod("PropertySetTools", "MvPartPickFirst", CommandFlags.Modal + CommandFlags.UsePickSet)> _
Public Sub Command_MvPartPickFirst()
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim ed As Editor = doc.Editor
Dim db As Database = doc.Database
Dim ClassificationDefId As ObjectId
Using Tr As Transaction = db.TransactionManager.StartTransaction
Dim ClassDefDictionary As New DictionaryClassificationDefinition(db)
Dim ObjectIds As ObjectIdCollection = ClassDefDictionary.Records
For Each ObjId As ObjectId In ObjectIds
Dim classdef As ClassificationDefinition = Tr.GetObject(ObjId, OpenMode.ForRead)
If classdef.Name = "MvPart Type" Then
ClassificationDefId = classdef.ObjectId
End If
Next
Dim filter As New SelectionFilter(New TypedValue() {New TypedValue(DxfCode.Start, "AECB_MvPart")})
'Create a Prompt Selection Option
Dim pso As New PromptSelectionOptions
'Create a message to display if no fittings are picked before the command is ran
pso.MessageForAdding = vbLf + "Select VAV Boxes: "
'Get the fittings from the pickfirst selection or from the message
Dim result As PromptSelectionResult = ed.GetSelection(pso, filter)
Dim ObjIdColl As ObjectIdCollection = New ObjectIdCollection()
'filter the items by classification
If result.Status = PromptStatus.OK Then
ObjIdColl = New ObjectIdCollection(result.Value.GetObjectIds())
For i As Integer = 0 To result.Value.Count - 1
Dim VavBox As MultiViewPart = Tr.GetObject(result.Value(i).ObjectId, OpenMode.ForRead)
Dim cls As Classification = Tr.GetObject(ClassificationDefinition.GetClassification(VavBox, ClassificationDefId), OpenMode.ForRead)
If Not (cls.Name.ToString = "VAV_Box") Then
ObjIdColl.RemoveAt(i)
End If
Next
ed.WriteMessage(String.Format(vbCrLf + "{0} MvParts selected.", ObjIdColl.Count.ToString()))
End If
End Using
End Sub
Next step is to impliment Gile's extension method so the objects that I remove do not actually get selected. Thank you to everyone that helped out with suggestions and code. And if you see anything that is blatantly wrong and needs corrected or just something to improve the code, please let me know.
A few comments:
1. Your first For Each loop looks like it continues to run even after finding the element you're looking for.
2. Calling RemoveAt() on any dynamic container or collection object iteratively in a loop is wasteful.
ObjectIdCollection is actually an array (in native code) that can dynamically grow in size without much pain. However, the same cannot be said for dynamically shrinking in size. When you remove an element, all the elements beyond the one removed must be shifted (make sense?). It would be far more efficient to just build a new collection containing the qualifying items.