Issue Retrieving Element Using Prompt And DocumentChangedEvent

Issue Retrieving Element Using Prompt And DocumentChangedEvent

rvtquestions
Advocate Advocate
550 Views
2 Replies
Message 1 of 3

Issue Retrieving Element Using Prompt And DocumentChangedEvent

rvtquestions
Advocate
Advocate

I recently had a need to revisit the topic of retrieving a newly created element using the PromptForFamilyInstancePlacement command but to some quirky results. I've read through a lot of the discussions which have led back to the decade old post found here:
 https://thebuildingcoder.typepad.com/blog/2010/06/place-family-instance.html

 

Problem:

  • The OnDocumentChanged Event fires right before the PromptForFamilyInstancePlacement is called or in other words fires before I even get to place a family instance.
  • After placing an instance or two, when exiting the command by pressing the ESC key, it seems as if the transaction rolls back or fails and all the placed instances are removed.

 

What I know:

  • I am aware the prompt has its own internal transaction method and I am NOT running this inside another transaction.
  • I am running this on Revit 2022
  • I am sure I can go the idling or IUpdater route, but I would like to reserve those for last case scenarios.
  • The family instance I am placing are of the Generic Model Category. I do not have any other updaters that would conflict with the newly created elements especially in this category.

 

To make troubleshooting simple, I am testing this with an extremely simply example, even the family is just a generic faced based model with nothing in it except a circle. Anyone else experience similar issues or have insight on why the following is not working properly? Thank you.

 

    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class Place : IExternalCommand
    {
        List<ElementId> ids = new List<ElementId>();
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            Document doc = commandData.Application.ActiveUIDocument.Document;
            UIDocument uiDoc = new UIDocument(doc);
            UIApplication uiApp = commandData.Application;
            Autodesk.Revit.ApplicationServices.Application app = uiApp.Application;
            ids.Clear();
            FamilySymbol symbol = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel).OfClass(typeof(FamilySymbol)).Cast<FamilySymbol>().FirstOrDefault(s => s.FamilyName.Equals("testfamily"));

            if (symbol != null)
            {
                app.DocumentChanged += new EventHandler<DocumentChangedEventArgs>(OnDocumentChanged);
                uiDoc.PromptForFamilyInstancePlacement(symbol); // Nothing below this line seems to fire, including the TaskDialog. It also seems to not even unsubscribe. 
                app.DocumentChanged -= new EventHandler<DocumentChangedEventArgs>(OnDocumentChanged);
            }

            TaskDialog.Show("Count", ids.Count.ToString()); // Does not fire
            return Result.Succeeded;
        }
        void OnDocumentChanged(object sender, DocumentChangedEventArgs e)
        {
            // I know this fires before the prompt because if I add a message box here, the message box appears right before the command.
            ids.AddRange(e.GetAddedElementIds());
        }
    }

 

0 Likes
Accepted solutions (1)
551 Views
2 Replies
Replies (2)
Message 2 of 3

Moustafa_K
Collaborator
Collaborator
Accepted solution

Prompt for family placement will be in a loop that will keep seeking users placement point to create and place a family. thus when the user hit an Escape button it means the operation for seeking placement point is aborted, and not what has been placed is gone. To swallow such thing you need to wrap this line in a try catch

 

uiDoc.PromptForFamilyInstancePlacement(symbol); // Nothing below this line seems to fire, including the TaskDialog. It also seems to not even unsubscribe. 

 

And to distinguish between user aborted exception against any other exception you can be specific of what exceptions to ignore and what to consider and handle them differently.

 

Another point I usually don't use document changed event for this usage. I would be more inclined to get all the ids before and after placing families. then get the new ones. you can achieve that by using Family Instance Filter.

 

I have summarized the above in the below example code... see if this helps and delivers you what you need. 

 

FamilyInstanceFilter familyInsFilter = new FamilyInstanceFilter(symbol.Document, symbol.Id);

var oldIds = new FilteredElementCollector(symbol.Document, symbol.Document.ActiveView.Id)
	.WherePasses(familyInsFilter)
	.ToElementIds();
try
{
	uiDoc.PromptForFamilyInstancePlacement(symbol);
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException ex)
{
	// user has exited the operation
}
catch (Exception ex)
{
	// something bad happened abort
	return;
}

var newFamilyInstanceIds = new FilteredElementCollector(symbol.Document, symbol.Document.ActiveView.Id)
	.WherePasses(familyInsFilter)
	.ToElementIds()
	.Except(oldIds);

 

 

 

 

 

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
Message 3 of 3

rvtquestions
Advocate
Advocate

@Moustafa_K 
Thanks for the reply. I did at some point have a try and catch surrounding the prompt, however, it did not change anything so I removed it for now. I am aware of your approach and do like the idea of comparing lists of a before and after so to speak but ultimately I would have loved to know why the Document Changed Event is behaving the way it is when it had so much success prior to very similar examples. Regardless, the list compare method should work as you've posted. Thanks again.

0 Likes