Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

PickObject to select room in current model or linked models?

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
juqing27
2565 Views, 11 Replies

PickObject to select room in current model or linked models?

Hello Community,

 

This may be somehow old but I cannot find a good solution for my specific case here 🙂 I’m working on a Revit addin and would like to prompt user to select a room in model. The room can be from either the current model or the linked models. With the help from previous posts in the forum I’m able  to make some progress so far, but still none of the approaches is ideal. Here are what I’ve tried and the downside –

  1. PickObject(ObjectType.Element, roomSelectionFilter). This works great for rooms in current model, but won’t allow user to select rooms from linked models.
  2. PickObject(ObjectType.LinkedElement, roomSelectionFilter_linkedRoom). This is the opposite of 1.: works great for rooms in linked models, but won’t allow user to select rooms from current model.
  3. PickObject(ObjectType.PointOnElement). This one allows user to select elements in both current and linked models. However, it’s not limited to rooms  - or at least I haven’t come up with a proper ISelectionFilter to achieve so.

So, any ideas? I’m thinking if there’s a way to combine 1. and 2. in one pick, or maybe there can be a ISelectionFilter to filter out anything that is not a room for 3.? Appreciate your help!

11 REPLIES 11
Message 2 of 12
jeremytammik
in reply to: juqing27

Please share the code for the two filters that you mention, roomSelectionFilter and roomSelectionFilter_linkedRoom.

 

Selection filters are very flexible, easy to understand and implement.

 

Therefore, the two can very probably be combined into one that performs a Boolean OR between the two types of filtering required.

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 12
juqing27
in reply to: jeremytammik

Thanks Jeremy! Edited my post to include the two filters I've tried. Thank you! - 

 

  • PickObject(ObjectType.Element, new RoomSelectionFilter(_doc)). This works great for rooms in current model, but won’t allow user to select rooms from linked models. Code for the filter - 

 

public class RoomSelectionFilter : ISelectionFilter
    {
        Document doc = null;

        public RoomSelectionFilter(Document document)
        {
            doc = document;
        }

        public bool AllowElement(Element elem)
        {
            if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Rooms)
            {
                return true;
            }
            return false;
        }

        public bool AllowReference(Reference reference, XYZ position)
        {
            //return false;
            RevitLinkInstance revitlinkinstance = doc.GetElement(reference) as RevitLinkInstance;
            Autodesk.Revit.DB.Document docLink = revitlinkinstance.GetLinkDocument();
            Element eRoomLink = docLink.GetElement(reference.LinkedElementId);
            if (eRoomLink.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Rooms)
            {
                return true;
            }
            return false;
        }
    }

 

  • PickObject(ObjectType.LinkedElement, new RoomSelectionFilter_linkedRoom(_doc)). This is the opposite of 1.: works great for rooms in linked models, but won’t allow user to select rooms from current model. Code for the filter - 

 

public class RoomSelectionFilter_Linked : ISelectionFilter
    {
        Document doc = null;

        public RoomSelectionFilter_Linked(Document document)
        {
            doc = document;
        }

        public bool AllowElement(Element elem)
        {
            return true;
        }

        public bool AllowReference(Reference reference, XYZ position)
        {
            //return false;
            RevitLinkInstance revitlinkinstance = doc.GetElement(reference) as RevitLinkInstance;
            Autodesk.Revit.DB.Document docLink = revitlinkinstance.GetLinkDocument();
            Element eRoomLink = docLink.GetElement(reference.LinkedElementId);
            if (eRoomLink.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Rooms)
            {
                return true;
            }
            return false;
        }
    }

 

  • PickObject(ObjectType.PointOnElement). This one allows user to select elements in both current and linked models. However, it’s not limited to rooms  - or at least I haven’t come up with a proper ISelectionFilter to achieve so.
Message 4 of 12
RPTHOMAS108
in reply to: juqing27

There is no perfect solution for this. Ideally there would be a separate flags argument for deciding where the items come from and then can do away with ObjectType.LinkedElement i.e.:

OutsideLink = 1

WithinLink = 2

Both = 1 + 2

 

You could try a looping strategy such as below where you click once for a link or room and then if link click again for a room in the link. I would not advise using pickpoint as you would require an active work plane and depending on the depth of this within the view you may not be picking on an element you believe you are picking. You can achieve the same as below in C# with while loop.

 

 

 Public Class CatSelFilter
        Implements Selection.ISelectionFilter
        Public Property Categories As List(Of Integer)
        Public Function AllowElement(elem As Element) As Boolean Implements Selection.ISelectionFilter.AllowElement
            If elem.Category Is Nothing Then Return False Else
            Return Categories.Contains(elem.Category.Id.IntegerValue)
        End Function
        Public Function AllowReference(reference As Reference, position As XYZ) As Boolean Implements Selection.ISelectionFilter.AllowReference
            Return True
        End Function
    End Class


    Public Function TObj81(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result

        Dim doc As Document = commandData.Application.ActiveUIDocument.Document
        Dim uiDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim R As Reference = Nothing
        Dim SelType As Selection.ObjectType = Selection.ObjectType.Element
        Dim Cfilt As New CatSelFilter
        Cfilt.Categories = {CInt(BuiltInCategory.OST_Rooms), CInt(BuiltInCategory.OST_RvtLinks)}.ToList

back:
        Try
            R = uiDoc.Selection.PickObject(SelType, Cfilt)
        Catch ex As Exception
        End Try
        If R Is Nothing Then Return Result.Cancelled Else

        Dim RvtLnk As RevitLinkInstance = TryCast(doc.GetElement(R), RevitLinkInstance)
        If RvtLnk Is Nothing = False AndAlso SelType = Selection.ObjectType.Element Then
            SelType = Selection.ObjectType.LinkedElement
            GoTo back
        End If

        Dim El As Element = Nothing
        If SelType = Selection.ObjectType.LinkedElement Then
            El = RvtLnk.GetLinkDocument.GetElement(R.LinkedElementId)
        Else
            El = doc.GetElement(R)
        End If

        Return Result.Succeeded

    End Function

 

 

Message 5 of 12
juqing27
in reply to: RPTHOMAS108

Thanks @RPTHOMAS108 ! Pick (a room or link) - decide whether a room or a link is selected - prompt another pick if a link is selected in the first one seems to be a good workaround at this time.

 

Still hoping @jeremytammik  could advise on how (or if it's possible) to build a ISelectionFilter that allows room room both current model and linked models. 🙂

 

Best,

Qing

 

 

Message 6 of 12
jeremytammik
in reply to: juqing27

Dear Richard,

 

Thank you for your good advice and solution. It looks nice and clean to me, very convincing.

 

I was hoping that something like ObjectType.PointOnElement could be used and the selection filter expanded to accept a point on either a room element or on a room element within a linked file.

 

I'm still not convinced that it cannot be done.

 

Have you tried that out? 

 

Qing Ju, have you?

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 12
juqing27
in reply to: jeremytammik

@jeremytammik @RPTHOMAS108 I'd love to try the route @jeremytammik suggest (PickObject(ObjectType.PointOnElement) with a proper selection filter, the 3rd approach in my original post 🙂 but I'm pretty new to Revit API and don't seem to be able to make the filter work for rooms from both current model and linked model... Can you show me some general idea, or point me to any similar example?

 

Thank you both!

 

Qing

Message 8 of 12
RPTHOMAS108
in reply to: juqing27

@jeremytammik you are correct .PointOnElement does provide a better approach thanks for highlighting this to me.

 

@juqing27 I have included below an Implementation of ISelectionFilter using generics. I've included this in VB but should be easy to convert to C#.

 

 Public Class ElementInLinkSelectionFilter(Of T As Element)
        Implements Selection.ISelectionFilter
        Private IntDoc As Document
        Private IntLastCheckedWasFromLink As Boolean = False
        Private IntLinkDoc As Document = Nothing

        Public Sub New(Doc As Document)
            IntDoc = Doc
        End Sub

        Public ReadOnly Property LinkDocument As Document
            Get
                Return IntLinkDoc
            End Get
        End Property
        Public ReadOnly Property LastCheckedWasFromLink As Boolean
            Get
                Return IntLastCheckedWasFromLink
            End Get
        End Property

        Public Function AllowElement(elem As Element) As Boolean Implements Selection.ISelectionFilter.AllowElement
            Return True
        End Function
        Public Function AllowReference(reference As Reference, position As XYZ) As Boolean Implements Selection.ISelectionFilter.AllowReference

            Dim El As Element = IntDoc.GetElement(reference)
            Dim RvtLnkInst As RevitLinkInstance = TryCast(El, RevitLinkInstance)
            If RvtLnkInst Is Nothing = False Then
                IntLastCheckedWasFromLink = True

                Dim LnkDoc As Document = RvtLnkInst.GetLinkDocument
                Dim ClsInst As T = TryCast(LnkDoc.GetElement(reference.LinkedElementId), T)
                If ClsInst Is Nothing = False Then
                    IntLinkDoc = RvtLnkInst.GetLinkDocument
                    Return True
                End If
            Else
                IntLastCheckedWasFromLink = False
                IntLinkDoc = Nothing
                Dim ClsInst As T = TryCast(El, T)
                If ClsInst Is Nothing = False Then
                    Return True
                End If
            End If

            Return False
        End Function
    End Class

ISelectionFilter implementation.

 

Public Function TObj82(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result

        Dim doc As Document = commandData.Application.ActiveUIDocument.Document
        Dim uiDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim R As Reference = Nothing
        Dim SelType As Selection.ObjectType = Selection.ObjectType.PointOnElement
        Dim Cfilt As New ElementInLinkSelectionFilter(Of Room)(doc)

        Try
            R = uiDoc.Selection.PickObject(SelType, Cfilt)
        Catch ex As Exception
        End Try
        If R Is Nothing Then Return Result.Cancelled Else

        Dim El As Element = Nothing
        If Cfilt.LastCheckedWasFromLink Then
            El = Cfilt.LinkDocument.GetElement(R.LinkedElementId)
        Else
            El = doc.GetElement(R)
        End If
        TaskDialog.Show("Picked", El.Name)

        Return Result.Succeeded
    End Function

Usage

 

Message 9 of 12
RPTHOMAS108
in reply to: RPTHOMAS108

Above in C#

 

 

public class ElementInLinkSelectionFilter<T> : ISelectionFilter where T : Element
		{
			private Document IntDoc;
			private bool IntLastCheckedWasFromLink = false;

			private Document IntLinkDoc = null;
			public ElementInLinkSelectionFilter(Document Doc)
			{
				IntDoc = Doc;
			}

			public Document LinkDocument
			{
				get { return IntLinkDoc; }
			}
			public bool LastCheckedWasFromLink
			{
				get { return IntLastCheckedWasFromLink; }
			}

			public bool AllowElement(Element elem)
			{
				return true;
			}
			public bool AllowReference(Reference reference, XYZ position)
			{

				Element El = IntDoc.GetElement(reference);
				RevitLinkInstance RvtLnkInst = El as RevitLinkInstance;
				if (RvtLnkInst == null == false)
				{
					IntLastCheckedWasFromLink = true;

					Document LnkDoc = RvtLnkInst.GetLinkDocument();
					T ClsInst = LnkDoc.GetElement(reference.LinkedElementId) as T;
					if (ClsInst == null == false)
					{
						IntLinkDoc = RvtLnkInst.GetLinkDocument();
						return true;
					}
				}
				else
				{
					IntLastCheckedWasFromLink = false;
					IntLinkDoc = null;
					T ClsInst = El as T;
					if (ClsInst == null == false)
					{
						return true;
					}
				}

				return false;
			}
		}

 

 

Implementation of ISelectionFilter

 

 

public Result TObj82(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
		{

			Document doc = commandData.Application.ActiveUIDocument.Document;
			UIDocument uiDoc = commandData.Application.ActiveUIDocument;
			Reference R = null;
			ObjectType SelType = ObjectType.PointOnElement;
			ElementInLinkSelectionFilter<Room> Cfilt = new ElementInLinkSelectionFilter<Room>(doc);

			try
			{
				R = uiDoc.Selection.PickObject(SelType, Cfilt);
			}
			catch (Exception)
			{
			}
			if (R == null)
				return Result.Cancelled;

			Element El = null;
			if (Cfilt.LastCheckedWasFromLink)
			{
				El = Cfilt.LinkDocument.GetElement(R.LinkedElementId);
			}
			else
			{
				El = doc.GetElement(R);
			}
			TaskDialog.Show("Picked", El.Name);

			return Result.Succeeded;
		}

 

 

Usage

 

I've extracted the LinkedDocument and determined the nature of the reference within the ISelectionFilter. It may be preferable to determine again these things outside. I just don't like going through the same process twice but since the filter is called when you hover over an element and not when you select an element it may be wise to move getting the linked document and determining again if the reference is from a link outside of ISelectionFilter.

 

You can also simplify the null checking of the above converted by using '!=' etc.

 

Message 10 of 12
juqing27
in reply to: RPTHOMAS108

This is great! Thanks for combining the solution decently into one click! 

Message 11 of 12
jeremytammik
in reply to: RPTHOMAS108

Very nice indeed!

 

Thank you for putting that together.

 

This is really worth saving, I think, so I cleaned it up and added it to The Building Coder samples.

 

Here is the diff to the previous version:

 

https://github.com/jeremytammik/the_building_coder_samples/compare/2021.0.149.2.../2021.0.150.0

 

Here is my version of your selection filter:

 

public class ElementInLinkSelectionFilter<T> : ISelectionFilter where T : Element
{
  private Document _doc;

  public ElementInLinkSelectionFilter( Document doc )
  {
    _doc = doc;
  }

  public Document LinkedDocument { get; private set; } = null;

  public bool LastCheckedWasFromLink
  {
    get { return null != LinkedDocument; }
  }

  public bool AllowElement( Element e )
  {
    return true;
  }

  public bool AllowReference( Reference r, XYZ p )
  {
    LinkedDocument = null;

    Element e = _doc.GetElement( r );

    if( e is RevitLinkInstance )
    { 
      RevitLinkInstance li = e as RevitLinkInstance;

      LinkedDocument = li.GetLinkDocument();

      e = LinkedDocument.GetElement( r.LinkedElementId );
    }
    return e is T;
  }
}

 

Here is the resulting external command for testing it:

 

public Result Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  UIApplication uiapp = commandData.Application;
  UIDocument uidoc = uiapp.ActiveUIDocument;
  Document doc = uidoc.Document;
  Reference r;

  ElementInLinkSelectionFilter<Room> filter
    = new ElementInLinkSelectionFilter<Room>(
      doc );

  try
  {
    r = uidoc.Selection.PickObject( 
      ObjectType.PointOnElement, 
      filter,
      "Please pick a room in current project or linked model" );
  }
  catch( Autodesk.Revit.Exceptions.OperationCanceledException )
  {
    return Result.Cancelled;
  }

  Element e;

  if( filter.LastCheckedWasFromLink )
  {
    e = filter.LinkedDocument.GetElement( 
      r.LinkedElementId );
  }
  else
  {
    e = doc.GetElement( r );
  }

  TaskDialog.Show( "Picked", e.Name );

  return Result.Succeeded;
}

 

Please let us know whether this works for you as well.

 

Thank you again, and cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 12 of 12
RPTHOMAS108
in reply to: jeremytammik

Thanks Jeremy,

 

Yes that is much cleaner and still works from the test I've done.

 

Regards

Richard

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community