.NET

Reply
Active Member
pymote
Posts: 9
Registered: ‎03-22-2012
Message 1 of 9 (514 Views)
Accepted Solution

How to apply a selection filter on a ObjectIDCollection?

514 Views, 8 Replies
12-11-2012 10:56 PM

Hi there,

I wonder if its possible to apply a filter to a subset of entities (that could be the result of a previous filtering).

It was just an example, for xdata, layer, you'll need to Open the objects. This can be done mapping the ObjectIdCollection to Entities with the Select() method, then adapting the Where() argument function as you need.

 

another example to be used within a Transaction (tr):

ids.Cast<ObjectId>()
.Select(id => (Entity)tr.GetObject(id, OpenMode.ForRead))
.Where(ent => ent.Layer == layerName)

 

Initially I tried to work with SelectionFilter (TypedValues) only and select the entities via editor.selectAll(SelectionFilter), since its performance is great. But there are some limits, e.g. I can't filter for entities with certain XData.

So I switched to work with Predicates (Expression<Func<EntityBoolean>>) that also can be combined via PredicateBuilder. The mapping from ObjectIDCollection to Entities is as you suggested:

IEnumerable<Entity> ents = objIds.Select(id => tr.GetObject(id, OpenMode.ForRead)).Cast<Entity>();

 The predicate is applied afterwards:

IEnumerable<Entity> appliedEnts = ents.AsQueryable().Where(predicate);

This solution provides for me the most flexibility with only a little lack of performance, since I only need to open one Transaction.

Thanks!

*Expert Elite*
_gile
Posts: 2,092
Registered: ‎04-29-2006
Message 2 of 9 (505 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-11-2012 11:43 PM in reply to: pymote

Hi,

 

By my side, i think Linq provides many useful extension methods to deal with all types of collection, those which do not implement IEnumerable<T> as ObjectIdcollection may be converted to IEnumerable<ObjectId> with Cast<ObjectId>>().

Look at the Linq extension methods here:

http://msdn.microsoft.com/en-us/library/9eekhta0.aspx

 

To filter a collection the Where extension methode is quite easy to use passing it a function (delegate) returning true for the elements you want to keep in the collection.

 

Example filtering the Line entities in an ObjectIdCollection (called ids):

ids.cast<ObectId>()
    .Where(id => id.ObjectClass.DxfName == "LINE")

 

Gilles Chanteau
Active Member
pymote
Posts: 9
Registered: ‎03-22-2012
Message 3 of 9 (501 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-12-2012 12:09 AM in reply to: _gile

Hi Gilles,

 

I like your Linq approach, but the filtering criterias I need aren't provided by id.ObjectClass

I want to filter for XData, Layer and others.

*Expert Elite*
_gile
Posts: 2,092
Registered: ‎04-29-2006
Message 4 of 9 (488 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-12-2012 05:38 AM in reply to: pymote

It was just an example, for xdata, layer, you'll need to Open the objects. This can be done mapping the ObjectIdCollection to Entities with the Select() method, then adapting the Where() argument function as you need.

 

another example to be used within a Transaction (tr):

ids.Cast<ObjectId>()
    .Select(id => (Entity)tr.GetObject(id, OpenMode.ForRead))
    .Where(ent => ent.Layer == layerName)

 

Gilles Chanteau
Active Member
pymote
Posts: 9
Registered: ‎03-22-2012
Message 5 of 9 (458 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-13-2012 03:08 AM in reply to: _gile

Initially I tried to work with SelectionFilter (TypedValues) only and select the entities via editor.selectAll(SelectionFilter), since its performance is great. But there are some limits, e.g. I can't filter for entities with certain XData.

So I switched to work with Predicates (Expression<Func<EntityBoolean>>) that also can be combined via PredicateBuilder. The mapping from ObjectIDCollection to Entities is as you suggested:

IEnumerable<Entity> ents = objIds.Select(id => tr.GetObject(id, OpenMode.ForRead)).Cast<Entity>();

 The predicate is applied afterwards:

IEnumerable<Entity> appliedEnts = ents.AsQueryable().Where(predicate);

This solution provides for me the most flexibility with only a little lack of performance, since I only need to open one Transaction.

Thanks!

Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 6 of 9 (426 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-13-2012 09:23 PM in reply to: pymote

pymote wrote:

Initially I tried to work with SelectionFilter (TypedValues) only and select the entities via editor.selectAll(SelectionFilter), since its performance is great. But there are some limits, e.g. I can't filter for entities with certain XData.


You can filter on the XData application name to reduce the selection to only entities having one or more application names, and with that, reduce the number of entities that you must open to examine for more specific criteria, and that's probably a faster solution than having to open all objects selected. Remember that the user can type 'All' to select everything and that means in a large drawing, you will have to open every single object in the current space.

Active Member
pymote
Posts: 9
Registered: ‎03-22-2012
Message 7 of 9 (417 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-14-2012 12:59 AM in reply to: DiningPhilosopher

I benchmarked this with a large drawing ~ 20.000 entities. The performance loss is not really worth mention it.

~0.07 sec vs ~ 0.17 sec.

For that I can not only filter for XData Regappnames but for other XData formats too.

*Expert Elite*
_gile
Posts: 2,092
Registered: ‎04-29-2006
Message 8 of 9 (392 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-14-2012 11:50 AM in reply to: pymote

Hi,

 

Another way is to use the Editor.SelectionAdded event. You can call the Editor.SelectAll() method from a Transaction and combinate classical SelectionFilter with SelectionAdded event filtering.

 

Here's a little example (I 'm more comfortable giving example than trying to explain in English) where entities are classicaly filtered with layer and registered application and a SelectionAdded event handler is used to filter on the first xdata value: (1001, 'foo")

 

        [CommandMethod("Test", CommandFlags.Modal)]
        public void Test()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            try
            {
                SelectionFilter filter =
                    new SelectionFilter(new TypedValue[] { new TypedValue(8, "Layer1"), new TypedValue(1001, "RegApp") });
                ed.SelectionAdded += OnSelectionAdded;
                PromptSelectionResult psr = ed.SelectAll(filter);
                if (psr.Status != PromptStatus.OK)
                    return;
                ed.WriteMessage("\n{0} entities selected", psr.Value.Count);
            }
            finally
            {
                ed.SelectionAdded -= OnSelectionAdded;
            }
        }

        void OnSelectionAdded(object sender, SelectionAddedEventArgs e)
        {
            ObjectId[] ids = e.AddedObjects.GetObjectIds();
            for (int i = 0; i < ids.Length; i++)
            {
                using (Entity ent = (Entity)ids[i].Open(OpenMode.ForRead))
                {
                    ResultBuffer xdata = ent.GetXDataForApplication("RegApp");
                    TypedValue[] tvs = xdata.AsArray();
                    if (tvs[0].TypeCode != 1000 || tvs[0].Value != "foo")
                    {
                        e.Remove(i);
                    }
                }
            }
        }

 

Gilles Chanteau
Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 9 of 9 (390 Views)

Re : How to apply a selection filter on a ObjectIDCollection?

12-14-2012 11:57 AM in reply to: pymote

pymote wrote:

I benchmarked this with a large drawing ~ 20.000 entities. The performance loss is not really worth mention it.

~0.07 sec vs ~ 0.17 sec.

For that I can not only filter for XData Regappnames but for other XData formats too.


Using LINQ against the result of SelectionSet.GetObjectIds() is essentially the same process as using it against all objects in a BlockTableRecord, so I usually pre-filter with a selection set/selection filter first and then use LINQ against the resulting ObjectIds to filter for more specific criteria.

 

I've used that approach with drawings containing betwen 250,000 and 500,000 objects with great sucess and there  was a perceptible difference between that and directly filtering the BlockTableRecord.  Of course, if you don't deal with drawings that large, it may not matter.

Need installation help?

Start with some of our most frequented solutions or visit the Installation and Licensing Forum to get help installing your software.