iupdater doc is null hence doc.GetElement() returns nothing

iupdater doc is null hence doc.GetElement() returns nothing

sobon.konrad
Advocate Advocate
1,803 Views
9 Replies
Message 1 of 10

iupdater doc is null hence doc.GetElement() returns nothing

sobon.konrad
Advocate
Advocate

I am trying to modify Jeremy's great post about Preventing Deleting of elements from the model so that it can actually be saved in the model which elements are being "protected". I tried using a Schema to write an Entity to an Element and then later have the IUpdater retireve that schema to see if element is protected from deletion. However, I am getting null for a doc.GetElement() method in IUpdater and I can't proceed with the rest of the code. Any help would be appreciated. 

 

using System;
using System.Collections.Generic;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB.ExtensibleStorage;

namespace GrimshawRibbon
{
    // Create Elment Filter that passes all elements through
    public class ElementSelectionFilter : ISelectionFilter
    {
        public bool AllowElement(Element e)
        {
            return true;
        }
        public bool AllowReference(Reference r, XYZ p)
        {
            return false;
        }
    }

    [TransactionAttribute(TransactionMode.Manual)]
    public class PreventDeletion : IExternalCommand
    {
        Guid schemaGuid = new Guid("0DC954AE-ADEF-41c1-8D38-EB5B8465D255");
        public const string Caption = "PreventDeletion";

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

            try
            {
                IList<Reference> refs = null;
                ElementSelectionFilter selFilter = new ElementSelectionFilter();
                try
                {
                    // prompt user to add to selection or remove from it
                    Selection sel = uidoc.Selection;
                    refs = sel.PickObjects(ObjectType.Element, selFilter, "Please pick elements to prevent from deletion.");
                }
                // catch an exception if user hits ESC here
                catch (Autodesk.Revit.Exceptions.OperationCanceledException)
                {
                    return Result.Cancelled;
                }

                using (Transaction trans = new Transaction(doc, "AddElement-PreventDeletion"))
                {
                    trans.Start();
                    foreach (Reference r in refs)
                    {
                        Element e = doc.GetElement(r);
                        if (e != null)
                        {
                            SchemaBuilder schemaBuilder = new SchemaBuilder(schemaGuid);

                            // allow anyone to read the object
                            schemaBuilder.SetReadAccessLevel(AccessLevel.Public);

                            // restrict writing to this vendor only
                            schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);

                            // set schema name
                            schemaBuilder.SetSchemaName("PreventDeletionSchema");

                            // set documentation
                            schemaBuilder.SetDocumentation("A stored boolean value where true means that wall cannot be deleted.");

                            // create a field to store the bool value
                            FieldBuilder fieldBuilder = schemaBuilder.AddSimpleField("PreventDeletionField", typeof(String));

                            // register the schema
                            Schema schema = schemaBuilder.Finish();

                            // create an entity object (object) for this schema (class)
                            Entity entity = new Entity(schema);

                            // get the field from schema
                            Field fieldPreventDeletion = schema.GetField("PreventDeletionField");

                            // set the value for entity
                            entity.Set(fieldPreventDeletion, "Prevent");

                            // store the entity on the element
                            e.SetEntity(entity);
                        }
                    }
                    trans.Commit();
                }
                return Result.Succeeded;
            }
            catch (OperationCanceledException)
            {
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return Result.Failed;
            }
        }
    }
}
using System;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using System.Collections.Generic;

namespace GrimshawRibbon
{
    class PreventDeletionUpdater : IUpdater
    {
        static AddInId _appId;
        UpdaterId _updaterId;
        FailureDefinitionId _failureId = null;

        public PreventDeletionUpdater(AddInId addInId)
        {
            _appId = addInId;

            _updaterId = new UpdaterId(_appId, new Guid("6f453eba-4b9a-40df-b637-eb72a9ebf008"));

            _failureId = new FailureDefinitionId(new Guid("33ba8315-e031-493f-af92-4f417b6ccf70"));

            FailureDefinition failureDefinition = FailureDefinition.CreateFailureDefinition(_failureId, FailureSeverity.Error, "Prevent Deletion: Sorry, this element cannot be deleted. Please contact your BIM Manager to find out why.");
        }

        public void Execute(UpdaterData data)
        {
            Document doc = data.GetDocument();
            foreach (ElementId id in data.GetDeletedElementIds())
            {
                Element e = doc.GetElement(id) as Element;
                if (e != null)
                {
                    IList<Guid> schemaIds = e.GetEntitySchemaGuids();
                    if (schemaIds.Count != 0)
                    {
                        foreach (Guid schemaId in schemaIds)
                        {
                            Schema pdSchema = Schema.Lookup(schemaId);
                            if (pdSchema != null)
                            {
                                if (pdSchema.SchemaName == "PreventDeletionSchema")
                                {
                                    FailureMessage failureMessage = new FailureMessage(_failureId);
                                    failureMessage.SetFailingElement(id);
                                    doc.PostFailure(failureMessage);
                                }
                            }
                        }
                    }  
                }
            }
        }

        public string GetAdditionalInformation()
        {
            return "Prevent deletion of selected elements.";
        }

        public ChangePriority GetChangePriority()
        {
            return ChangePriority.FloorsRoofsStructuralWalls;
        }

        public UpdaterId GetUpdaterId()
        {
            return _updaterId;
        }

        public string GetUpdaterName()
        {
            return PreventDeletion.Caption;
        }
    }
}

my app is running these updater registers on startup: 

 

public Result OnStartup(UIControlledApplication application)
        {
            AddRibbonPanel(application);

            #region Prevent Deletion Tool
            // Failure Definition Registry
            // Implemented in Prevent Deletion Tool
            PreventDeletionUpdater deletionUpdater
              = new PreventDeletionUpdater(application.ActiveAddInId);

            UpdaterRegistry.RegisterUpdater(
              deletionUpdater);

            ElementClassFilter filter
              = new ElementClassFilter(
                typeof(Dimension), true);

            UpdaterRegistry.AddTrigger(
              deletionUpdater.GetUpdaterId(), filter,
              Element.GetChangeTypeElementDeletion());
            #endregion //Prevent Deletion Tool

            #region DeleteAllViewsSheets Tool

            m_MyForm = null;   // no dialog needed yet; the command will bring it
            thisApp = this;  // static access to this application instance

            #endregion //DeleteAllViewsSheets Tool

            return Result.Succeeded;
        }

Of course huge thanks to Jeremy for posting all of this code. I wouldnt even be able to get to this point without his blog. Thank you! 

0 Likes
Accepted solutions (2)
1,804 Views
9 Replies
Replies (9)
Message 2 of 10

Anonymous
Not applicable
I don't think you can retrieve the deleted element during the updater and hence will also not be able to read the extensible storage data. Best bet would be to maintain a secondary list of protected ElementIds (as integer values) to match against. Maybe in a singleton Datastorage Element of your own creation. There will ofcourse be issues related to worksharing with this method as there is currently no way to make use of UniqueId strings for deleted elements. These days should be sufficient to just pin the protected elements as they can not be deleted using the UI until they are unpinned. You could then modify your updater to keep the protected elements pinned instead with basically the same outcome.
0 Likes
Message 3 of 10

sobon.konrad
Advocate
Advocate

Scott,

 

Thank you for the comment. I wasn't sure if it was possible, but I am pretty sure that I saw a piece of code on Harry Mattison's blog that was using the Updater to retireve the Document and Instance Families to keep them coordinated. Of course I am not sure if you can get the deleted element the same way, but I should at least be able to get the Document. That's the main question here. What am I doing wrong that I am getting null instead of Document in the updater? 

 

Can someone from the Dev team confirm that this method is not valid? 

 

Ps. Scott, I am not really locking things down this way. Of course I use the Pin and that has usually worked well for me. I was interested in testing the Extensible Storage and Updater methods in Revit as they seem to be pretty powerful and might be useful for an array of other things that I am working on. This was just an example that I was able to get my hands on, thanks to Jeremy's blog. 

 

Thanks! 

0 Likes
Message 4 of 10

Anonymous
Not applicable
Sorry I just assumed that the null value you were getting was the null element from the getelement statement. Not sure about why you'd be getting a null document. you are doing it the same way I do (GetDocument) it should work. Try assigning it to a raw Object and checking the value and type in the debugger.
0 Likes
Message 5 of 10

Revitalizer
Advisor
Advisor

Hi Konrad,

 

you are using an inverted ElementClassFilter, that means you are monitoring a huge amount of Elements without need.

 

ElementClassFilter filter
              = new ElementClassFilter(
                typeof(Dimension), true);

In this list of Elements, there may be also exotic ones, existing just for internal purposes, thus never meant to be used in an IUpdater context.

Something like SharedParameterElements, SpatialFieldManager, AreaVolumeSettings and so on.

It's just a guess, but may it be that these invisible/abstract/virtual Elements corrupt your UpdaterData object ?

 

You could try to use a less common ElementFilter, without negation, for example an ElementMulticlassFilter.

 

 

There may be another problem in your PreventDeletion command class:

 

For each Element, you try to create a new Schema instead of looking for an already existing one (Schema.Lookup(schemaGuid)).

The RevitAPI.chm says that the SchemaBuilder.Finish() method will throw an exception if "A different Schema with a matching identity already exists".

Since it is encapsulated in a "using" block, this exception will be swallowed and not thrown to your catch blocks.

 

                using (Transaction trans = new Transaction(doc, "AddElement-PreventDeletion"))
                {
                    trans.Start();
                    foreach (Reference r in refs)
                    {
                        Element e = doc.GetElement(r);
                        if (e != null)
                        {
                            SchemaBuilder schemaBuilder = new SchemaBuilder(schemaGuid);

                            // allow anyone to read the object
                            schemaBuilder.SetReadAccessLevel(AccessLevel.Public);

                            // restrict writing to this vendor only
                            schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);

                            // set schema name
                            schemaBuilder.SetSchemaName("PreventDeletionSchema");

                            // set documentation
                            schemaBuilder.SetDocumentation("A stored boolean value where true means that wall cannot be deleted.");

                            // create a field to store the bool value
                            FieldBuilder fieldBuilder = schemaBuilder.AddSimpleField("PreventDeletionField", typeof(String));

                            // register the schema
                            Schema schema = schemaBuilder.Finish();

                            // create an entity object (object) for this schema (class)
                            Entity entity = new Entity(schema);

                            // get the field from schema
                            Field fieldPreventDeletion = schema.GetField("PreventDeletionField");

                            // set the value for entity
                            entity.Set(fieldPreventDeletion, "Prevent");

                            // store the entity on the element
                            e.SetEntity(entity);
                        }
                    }
                    trans.Commit();
                }

I think that it will work only if selecting exactly one Element and if this Schema hasn't been added before.

 

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





0 Likes
Message 6 of 10

Anonymous
Not applicable

Hi all,

 

Any idea to release the locked element id's through external command.

 

Regards

Richard Mass

0 Likes
Message 7 of 10

Anonymous
Not applicable
Are you sure GetDocument is returning null? What if any exceptions are you getting?
0 Likes
Message 8 of 10

arnostlobel
Alumni
Alumni
Accepted solution

I seriously doubt that the data.GetDocument() method yields a NULL, for if it were a NULL then there would be no executing of updaters for it. Mistakes can be made, of course, but I have not heard about such a case yet.

The call doc.GetElement(id), on the other hand, will always return NULL for elements that have been deleted because, well, the elements have been deleted and do not exist anymore.

Arnošt Löbel
0 Likes
Message 9 of 10

sobon.konrad
Advocate
Advocate

Scott,

 

It looks like you were right and GetElement() wont return the deleted element. I will go at it a little differently then. 

 

Arnost,

 

I will give this another run (its been a week), and report back with any new findings. It's most likely my mistake. 

 

Thanks! 

0 Likes
Message 10 of 10

sobon.konrad
Advocate
Advocate
Accepted solution

Guys,

 

So it ended up being my mistake as usual. Just like Arnost have mentioned the GetElement() method used for a deleted element in updater would not return anything since element was delete. Duh! I know, I am an idiot. I don't know why I thought it would, and also, I think i confused myself with thinking that it was an issue with the GetDocument() while it was the GetElement(). Thanks to all that commented for setting the record straight. 

 

So, I was able to use ExtensibleStorage and Updater to lock things away, instead of pinning them. I know that's probably a little extensive but like I mentioned I was more interested in seeing how ExtensibleStorage works in combination with Updater than to really lock things away. 

 

Again, huge thanks to others for contributing.

 

Massretailers,

 

Please see the code below, on how I used ExtensibleStorage to prevent things from being deleted, even when file is closed and re-opened again. Jeremy's original solution that was posted on his website was only able to lock things away for duration of Revit session and not when it was closed. 

 

Thanks! 

 

Ps. Code below is the Command only, since Updater and Application code remains the same as Jeremy's code posted on his blog. 

 

using System;
using System.Collections.Generic;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB.ExtensibleStorage;
using System.Linq;

namespace GrimshawRibbon
{
    // Create Elment Filter that passes all elements through
    public class ElementSelectionFilter : ISelectionFilter
    {
        public bool AllowElement(Element e)
        {
            return true;
        }
        public bool AllowReference(Reference r, XYZ p)
        {
            return false;
        }
    }

    [TransactionAttribute(TransactionMode.Manual)]
    public class PreventDeletion : IExternalCommand
    {
        static List<ElementId> protectedIds = new List<ElementId>();
        public const string Caption = "PreventDeletion";

        public static Schema GetSchema(string schemaName)
        {
            Schema schema = null;
            IList<Schema> schemas = Schema.ListSchemas();
            if (schemas != null && schemas.Count > 0)
            {
                // get schema
                foreach (Schema s in schemas)
                {
                    if (s.SchemaName == schemaName)
                    {
                        schema = s;
                        break;
                    }
                }
            }
            return schema;
        }

        public static bool SchemaExist(string schemaName)
        {
            bool result = false;
            if (GetSchema(schemaName) != null)
            {
                result = true;
            }
            return result;
        }

        public static void PopulateIds(Document doc, Schema schema)
        {
            IList<Element> allSchemaElements = new FilteredElementCollector(doc)
                .WhereElementIsNotElementType()
                .Where(x => x.GetEntity(schema) != null && x.GetEntity(schema).Schema != null)
                .ToList();

            if (allSchemaElements.Count > 0)
            {
                // write these elments ids to static list storage
                foreach (Element e in allSchemaElements)
                {
                    protectedIds.Add(e.Id);
                }
            }
        }

        public static bool IsProtected(ElementId id)
        {
            return protectedIds.Contains(id);
        }

        public static Schema CreateSchema()
        {
            Guid schemaGuid = new Guid("0DC954AE-ADEF-41c1-8D38-EB5B8465D255");

            SchemaBuilder schemaBuilder = new SchemaBuilder(schemaGuid);

            // set read access
            schemaBuilder.SetReadAccessLevel(AccessLevel.Public);

            // set write access
            schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);

            // set schema name
            schemaBuilder.SetSchemaName("PreventDeletionSchema");

            // set documentation
            schemaBuilder.SetDocumentation("A stored boolean value where true means that wall cannot be deleted.");

            // create a field to store the bool value
            FieldBuilder fieldBuilder = schemaBuilder.AddSimpleField("PreventDeletionField", typeof(String));

            // register the schema
            Schema schema = schemaBuilder.Finish();

            return schema;
        }

        public static void AddSchemaEntity(Schema schema, Element e)
        {
            // create an entity object (object) for this schema (class)
            Entity entity = new Entity(schema);

            // get the field from schema
            Field fieldPreventDeletion = schema.GetField("PreventDeletionField");

            // set the value for entity
            entity.Set(fieldPreventDeletion, "Prevent");

            // store the entity on the element
            e.SetEntity(entity);
        }

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

            Schema schema = null;

            try
            {
                IList<Reference> refs = null;
                ElementSelectionFilter selFilter = new ElementSelectionFilter();

                // get all elements with schema
                if (SchemaExist("PreventDeletionSchema"))
                {
                    schema = GetSchema("PreventDeletionSchema");
                    protectedIds.Clear();
                    PopulateIds(doc, schema);
                }

                // add elements with Schema to pre-selection list
                List<Reference> preSelectedElems = new List<Reference>();
                if (protectedIds.Count != 0)
                {
                    foreach (ElementId id in protectedIds)
                    {
                        Reference elemRef = new Reference(doc.GetElement(id));
                        preSelectedElems.Add(elemRef);
                    }
                }

                using (Transaction trans = new Transaction(doc, "PreventDeletion"))
                {
                    trans.Start();
                    try
                    {
                        // prompt user to add to selection or remove from it
                        Selection sel = uidoc.Selection;
                        refs = sel.PickObjects(ObjectType.Element, selFilter, "Please pick elements to prevent from deletion.", preSelectedElems);
                    }
                    catch (OperationCanceledException)
                    {
                        return Result.Cancelled;
                    }

                    // get set difference between selected elements and elements that were preselected
                    List<Reference> removeSchemaSet = preSelectedElems.Where(x => !refs.Any(l => x.ElementId == l.ElementId)).ToList();
                    if (removeSchemaSet.Count > 0)
                    {
                        foreach (Reference r in removeSchemaSet)
                        {
                            // remove schema
                            Element e = doc.GetElement(r.ElementId);
                            e.DeleteEntity(schema);
                        }
                    }
                    // get set difference between preselected elements and newly selected elements
                    List<Reference> addSchemaSet = refs.Where(x => !preSelectedElems.Any(l => x.ElementId == l.ElementId)).ToList();
                    if (addSchemaSet.Count > 0)
                    {
                        // if schema exists add it else create new one
                        if (schema == null)
                        {
                            schema = CreateSchema();
                        }
                        foreach (Reference r in addSchemaSet)
                        {
                            // add schema entity to element
                            Element e = doc.GetElement(r.ElementId);
                            AddSchemaEntity(schema, e);

                            // add element ids to protectedids
                            protectedIds.Add(r.ElementId);
                        }
                    }
                    trans.Commit();
                }

                // show message
                TaskDialog.Show(Caption, "There is total of " + refs.Count.ToString() + " elements protected from being deleted.");
                return Result.Succeeded;
            }
            catch (OperationCanceledException)
            {
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return Result.Failed;
            }

        } // execute
    } // prevent deletion class
} // namespace