Author Topic: do I use ownerID to step through nesting levels?  (Read 9853 times)

0 Members and 1 Guest are viewing this topic.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
do I use ownerID to step through nesting levels?
« on: February 02, 2007, 01:18:32 AM »
If I use GetNestedEntity to get an object, do I use the OwnerID property to get the level above the entity.
The level above would be a block (speaking in common terms, not .net object types...).
It might be a block, an xref, or the madelspace or paperspace block.
I tried the ownerID property to get the entity, but I think I am assuming too much.
The following fuction catches when I feed it the ownerID of an entity:

 Public Function getEntNet(ByVal objID As AcDb.ObjectId) As AcDb.Entity
        Dim db As Database = AcDb.HostApplicationServices.WorkingDatabase()
        Dim tm As AcDb.TransactionManager = db.TransactionManager
        Dim myT As AcDb.Transaction = tm.StartTransaction()
        Dim ent As AcDb.Entity = Nothing
        If Not objID.IsNull Then
            Try
                ent = CType(tm.GetObject(objID, OpenMode.ForRead, True), AcDb.Entity)
                myT.Commit()
            Catch ex As Exception
                ent = Nothing
            Finally
                myT.Dispose()
            End Try
        End If
        Return ent
    End Function

Do I need to be more specific in my casting?
I typically have no problem finding them lunkers in Lake Sabrina, obviosly my fishing is better than my netting - and my analogies are better than my programming, I'm on a roll....
James Maeding

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #1 on: February 02, 2007, 01:26:52 AM »
Let's say you select a line in plain open ModelSpace...what do you expect it's owner to be?
« Last Edit: February 02, 2007, 06:37:25 AM by Glenn R »

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #2 on: February 02, 2007, 01:39:06 AM »
I'll give you a hint...an AcDbEntity is graphical, but.................

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: do I use ownerID to step through nesting levels?
« Reply #3 on: February 02, 2007, 01:54:25 AM »
Ohhh time for a class ..


[Settles down with popcorn]

« Last Edit: February 02, 2007, 05:51:34 AM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #4 on: February 02, 2007, 03:22:44 AM »
And another...ALL GRAPHICAL entities WILL have the same TYPE of owner...........

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: do I use ownerID to step through nesting levels?
« Reply #5 on: February 02, 2007, 04:15:02 AM »
We should leave this one for James , yes ?

James, do you have a copy of DBVIEW, which Glenn recently reminded me about.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #6 on: February 02, 2007, 04:26:22 AM »
Yes we should and anybody else who wants to anty up and post a reply.

DBVIEW is excellent, but a trip to the ARX docs will sort this one out as well Kerry.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #7 on: February 02, 2007, 04:48:45 PM »
I think you guys are wondering why I would ask for an objects owner, when something "plain" in modelspace is so simple.
I am doing a tool to list properties of an object, then allow you to step up or down in an objects nesting level and see those properties.
In the case of a plain line, it is essentially not nested.
My prog would not let you step up to a higher nesting level, as there is none in that case.
What about an object in a block though?
If I grab a line using the GetNestedEntity method, how do I get the entity object for the block insert it came from?
I need to get the object so I can list its layer properties.
In lisp, the nentsel returned a "trail" of data allowing you to step back via the entity name.
If you did this with VBA, I think you used the parent property or something.
I'm wondering how to do this in .net.

Also keep in mind, I'm not just interested in listing the entity's info, my prog lets you change the layer properties of any of the levels.
I swear, I tried looking at the ARX docs, I read all I could and it seemed like the ownerID was the right thing to look at.
I thought it would be an easy topic to figure out, but its not been so far...
thanks for the replies.
James Maeding

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #8 on: February 02, 2007, 04:52:46 PM »
Wait, I think I see the hint.  I will try casting it to a block table record and see what happens.
I'm only half as dense as I look, so there is an upper limit :)
James Maeding

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #9 on: February 02, 2007, 05:53:01 PM »
:) Any luck?

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #10 on: February 02, 2007, 06:04:18 PM »
A AcDbBlockTableRecord will be the TYPE of OWNER of all things graphical.

Now for a little lesson in CLASSICAL INHERITANCE. I'm sure you're familiar with inheritance, so I won't go into the nitty gritty, however, when designing class heirarchies, it's often useful to SAY your inheritance out loud to see if it makes sense.

For example, let's say we have 3 classes - 1 base class, called CAR, and 2 derived classes called SPORTSCAR and FAMILYCAR. SportsCar and FamilyCar both derive/inherit from Car. In classical inheritance, you can now say,  a SportsCar is a type of Car, also, a FamilyCar is a type of Car. However, you could not say a Car is a SportsCar nor is a Car a FamilyCar...it doesn't make sense. Does that make sense?

Right, back to our little problem. I said above an AcDbEntity is graphical, but......an AcDbObject is not! Now seeing as everything graphical must have an owner of type AcDbBlockTableRecord, which in turn derives from AcDbSymbolTableRecord, which in turns derives from............AcDbObject!!!

Now, is an AcDbBlockTableRecord a graphical entity? No it certainly isn't. So, in your function, you could specify the return type as AcDbBlockTableRecord or, more generically, as AcDbObject and cast appropriately.

Apologies for the hasty reply, but I've just gotten up, am hungry and going for breakfast.

Cheers,
Glenn.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #11 on: February 03, 2007, 12:56:47 AM »
wow, thanks for the tip.  I am soaking in this inheritance and casting stuff.  It makes perfect sense, it just takes me a while to get used to.  I'll report back.
James Maeding

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #12 on: February 03, 2007, 01:52:18 AM »
wait, what about a block insert, isn't that graphical?
So if I picked a line, within a block insert, within an xref, I would expect the ownerID of the line to be the ID of an insert.
But that did not work, of course.  So I'm not sure how to get the insert entity from the line properties still.
thx
James Maeding

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #13 on: February 03, 2007, 02:10:05 AM »
wait, what about a block insert, isn't that graphical?

Correct.

So if I picked a line, within a block insert, within an xref, I would expect the ownerID of the line to be the ID of an insert.

Incorrect :)

The owner of the line in the Block insert is the AcDbBlockTableRecord representing the insert that contains the line.
The owner of the block insert is the AcDbBlockTableRecord representing the xref.
The owner of the inserted xref, in this case, would be the AcDbBlockTableRecord representing modelspace.
Finally, the owner of modelspace is the block table itself.

Confused yet? ;)

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #14 on: February 03, 2007, 02:21:20 AM »
If you're using GetNestedEntity, in it's PromptNestedEntityResult you can get the ACTUAL graphical containers using GetContainers, of the entity thusly picked.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #15 on: February 03, 2007, 12:41:10 PM »
ahhh, grasshoppahhh! now we're talking, I had no idea.
You know, I looked through the arx help file a lot, and could not find info on this topic.
I can find definitions, once I know the property or method to look up, but nothing explaining how to iterate through levels.

I'd much rather know how to teach myself from the help, than nag you guys.  Did I miss the section in the help, or would you say my experience is typical? How does everyone learn the arx object model, the help seems non-tutorial like?
I have ADN access, it takes time to get someone though, sometimes a couple days.
James Maeding

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #16 on: February 04, 2007, 02:00:51 AM »
In the case of a plain line, it is essentially not nested.

Technically, that's not correct - it's nested in the current space, which is itself an AcDbBlockTableRecord...this is fun isn't it? ;)

As far as learning, the best advice I can give you is read - and read a LOT. The ARX docs are divided into Guide and Reference. Although not tutorial based, the Guide DOES show examples of doing certain things, but definately not all.

In this case, you were on the right track using GetNestedEntity. You could then have examined the return result and that would have showed you the PrompNestedEntityResult and it's methods/properties, of which, you would have investigated further and found GetContainers.

If you've used lisp in the past, nentsel is the equivalent and it also returns a list of graphical containers.

I must admit, it does help coming from an ARX background, but it's not a definite pre-requisite. It ultimately comes down to experience and understanding, so keep at it.

Cheers,
Glenn.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #17 on: February 04, 2007, 11:28:24 AM »
You're right about nested in current space, I just meant thats where my prog stops.
I lets you change the layer props at any level, because many times you want to freeze or change color of the layer a block is on, asopposed to the entity within the block, which is commonly on layer 0.

I think I have a pattern that will work, this is fun indeed!  Here is what I will do:
1) save the array of containier id's from the GetNestedEntity return
2) obtain properties for a given "level", when needed to fill in dialog box, by first getting an object based like this:

Dim obj As Object = CType(tm.GetObject(globalIDArray(level), OpenMode.ForRead), Object)

then finding type with this:

Dim aType As Type = obj.GetType()
Dim ent As Entity = Nothing
Dim blkRef As AcDb.BlockReference = Nothing
Dim btr As AcDb.BlockTableRecord = Nothing
' Time to see if we can process this entity type
Select Case aType.FullName
      'Autodesk.AutoCAD.DatabaseServices.BlockTableRecord
      Case "Autodesk.AutoCAD.DatabaseServices.BlockReference"
            blkRef = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.BlockReference)
      Case "Autodesk.AutoCAD.DatabaseServices.BlockTableRecord"
            btr = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.BlockTableRecord)
      Case Else
            ent = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.Entity)
End Select

3) then get props from the objects. (the case else is likely assuming too much, but its not done yet)

So this teaches me a lesson, if you just start with an objectID, you can run down any object in the database, then cast it to the specific type you want.  That is the equivalent of the entget in lisp.
I figured out the type detection from an example in the arx sdk, but its obvious once I see the approach (hindsight...20/20...)

Only thing I did not like is that I cannot encapsulate the whole process, and get the specific object back.
I would perfer to do a function like:
public function EntGetNet(byval objID as objectID) as object
but have it return a specific object already cast to the right type.
Is there a way to do that?  The return type above is too general I think, but here is where inheritance rules come into play, and the thinking is not yet automatic.
Could I make it so it returns a collection of different object types?  I thought there was a way to do that with .net.
That "variant" collection would serve the same purpose as a lisp list, which is powerful in that it stores different data types.

The other thing I'd like to know is how to track back up the nesting tree, if I only start with the bottom level objectID.  The data has to be there :) so I wonder if the net API gives it to us.
thanks again.


James Maeding

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: do I use ownerID to step through nesting levels?
« Reply #18 on: February 04, 2007, 06:57:08 PM »
James, These are worth a look .. it's C#, but you may be able to follow them

Changing the colour of nested AutoCAD entities through .NET
http://through-the-interface.typepad.com/through_the_interface/2007/02/changing_the_co.html

Highlighting an AutoCAD entity in a nested block using .NET
http://through-the-interface.typepad.com/through_the_interface/2006/12/highlighting_an.html

Highlighting a nested AutoCAD block using .NET
http://through-the-interface.typepad.com/through_the_interface/2006/12/highlighting_a_.html

kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #19 on: February 04, 2007, 07:09:26 PM »
Dim obj As Object = CType(tm.GetObject(globalIDArray(level), OpenMode.ForRead), Object)

That's more generic than it needs to be. You're actually using System.Object which you can think of as a Variant in VB - unnecessary overhead. You could change it to DBObject as this is the lowest Acad type...even better would be Entity as this is the type of containers returned.

' Time to see if we can process this entity type
Select Case aType.FullName
      'Autodesk.AutoCAD.DatabaseServices.BlockTableRecord
      Case "Autodesk.AutoCAD.DatabaseServices.BlockReference"
            blkRef = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.BlockReference)
      Case "Autodesk.AutoCAD.DatabaseServices.BlockTableRecord"
            btr = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.BlockTableRecord)
      Case Else
            ent = CType(tm.GetObject(resCont(0), OpenMode.ForRead, True), AcDb.Entity)
End Select

I really don't see why VB'ers continue to use STRING comparisons in case statements to check for autocad type - it's relatively slow and IF Adesk changes the string representation it will choke all over the place.
See my example below for another way.

Only thing I did not like is that I cannot encapsulate the whole process, and get the specific object back.
I would perfer to do a function like:
public function EntGetNet(byval objID as objectID) as object
but have it return a specific object already cast to the right type. Is there a way to do that?

Yes - see the example.

Code: [Select]
#region Using declarations

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;

#endregion

[assembly: CommandClass(typeof(ClassLibrary.tcgsClass))]

namespace ClassLibrary
{
/// <summary>
/// Summary description for tcgsClass.
/// </summary>
public class tcgsClass
{
public tcgsClass( ) { }

// Define Command "LNE"
[CommandMethod("LNE")]
static public void ListNestedEntityCommand( )
{

Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Set up the options...
PromptNestedEntityOptions pneOpts = new PromptNestedEntityOptions("\nSelect entity: ");
pneOpts.AllowNone = false;
// Ask the question...
PromptNestedEntityResult pneRes = ed.GetNestedEntity(pneOpts);

// Were we successful...?
if (pneRes.Status != PromptStatus.OK)
return;

// Cast to an 'Entity' as this is the lowest common object type
// in the inheritance tree for graphical entities...
Entity selectedEntity = tr.GetObject(pneRes.ObjectId, OpenMode.ForRead, false) as Entity;

// List some info about it...
DumpEntityInfo(selectedEntity);

// Get the list of 'graphical' containers for the entity picked...
ObjectId[] containerIds = pneRes.GetContainers();
// This should never happen, but check anyway...
if (containerIds == null || containerIds.Length == 0)
return;

// Loop over the array of containers and dump some info...
foreach (ObjectId containerId in containerIds)
{
Entity containerEnt = tr.GetObject(containerId, OpenMode.ForRead, false) as Entity;
DumpEntityInfo(containerEnt);
}

// Commit the transaction...
tr.Commit();
}
          
}

static private void DumpEntityInfo(Entity ent)
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

ed.WriteMessage("\nType of selected entity: {0}", ent.GetType());
ed.WriteMessage("\nDXF name: {0}", ent.GetRXClass().DxfName);
ed.WriteMessage("\nARX class name: {0}", ent.GetRXClass().Name);

using (Transaction tr = db.TransactionManager.StartTransaction())
{
// See if the type of ent is an AcDbBlockReference...
// Shows one way of using 'RTTI' to discover the type
// of an object at runtime...not really necessary in this
// case, as the 'container' will always be ultimately of
// type AcDbBlockReference.
if (ent.GetType() == typeof(BlockReference))
{
// Cast the entity to a block reference...
BlockReference blkRef = ent as BlockReference;
// Did the cast succeed?
if (blkRef == null)
return;

// Get the objectid of the AcDbBlockTableRecord representing/defining
// this insert and open it up...
BlockTableRecord btr = tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead, false) as BlockTableRecord;
// Print out it's name...
ed.WriteMessage("\nBlock name: {0}", btr.Name);
if (btr.IsFromExternalReference || btr.IsFromOverlayReference)
ed.WriteMessage("\nXref: true");
}

tr.Commit();
}

ed.WriteMessage("\n\n");
}

}
}

Cheers,
Glenn.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: do I use ownerID to step through nesting levels?
« Reply #20 on: February 04, 2007, 08:08:30 PM »
all right! more stuff to chew on.  Thanks Glenn and Kerry.
Hey, does this symbol  :police: remind you of the "Hot Cops" from Arrested Development?
My wife saw it when I was typing a response, watch out for that one....
James Maeding

Glenn R

  • Guest
Re: do I use ownerID to step through nesting levels?
« Reply #21 on: February 21, 2007, 07:52:32 AM »
That "variant" collection would serve the same purpose as a lisp list, which is powerful in that it stores different data types.

Actually, NON generic collections in .NET will store any TYPE of object. This is one of the reasons for generics, as this type of collection removes the vaunted, much touted and highly desired TYPE SAFETY.

Think about it. EVERYTHING in the .NET universe ultimately derives from System.Object. As the NON generic collections all take an object of type System.Object, you can stuff ANYTHING into these little babies (different types in one collection)...the trick would be determining what type of object you have at any given index in the collection.
« Last Edit: February 21, 2007, 07:55:43 AM by Glenn R »

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: do I use ownerID to step through nesting levels?
« Reply #22 on: August 27, 2017, 08:27:26 PM »
Better late than never ... Glenn's routine doesn't work for Attribute References in a BlockReference so I broke it...

Code - C#: [Select]
  1. // Define Command "LNE"
  2.        [CommandMethod("LNE")]
  3.         static public void ListNestedEntityCommand( )
  4.         {
  5.             Document doc = acadApp.DocumentManager.MdiActiveDocument;
  6.             Database  db = doc.Database;
  7.             Editor    ed = doc.Editor;
  8.  
  9.             using (Transaction tr = db.TransactionManager.StartTransaction())
  10.             {
  11.                 // Set up the options...
  12.                 PromptNestedEntityOptions pneOpts = new PromptNestedEntityOptions("\nSelect entity: ");
  13.                 pneOpts.AllowNone = false;
  14.                 // Ask the question...
  15.                 PromptNestedEntityResult pneRes = ed.GetNestedEntity(pneOpts);
  16.  
  17.                 // Were we successful...?
  18.                 if (pneRes.Status != PromptStatus.OK)
  19.                     return;
  20.  
  21.                 // Cast to an //Entity// as this is the lowest common object type
  22.                 // in the inheritance tree for graphical entities...
  23.                 Entity selectedEntity = tr.GetObject(pneRes.ObjectId, OpenMode.ForRead, false) as Entity;
  24.  
  25.                 // List some info about it...
  26.                 DumpEntityInfo(selectedEntity);
  27.  
  28.                 if (selectedEntity != null)
  29.                 {
  30.                     BlockReference blockReference = tr.GetObject(selectedEntity.OwnerId, OpenMode.ForRead) as BlockReference;
  31.                     if (blockReference != null)
  32.                     {
  33.                         ed.WriteMessage($"\nownerID: {selectedEntity.OwnerId}");
  34.                         DumpEntityInfo(blockReference);
  35.                     }
  36.                 }
  37.  
  38.                 // Get the list of //graphical// containers for the entity picked...
  39.                 ObjectId[] containerIds = pneRes.GetContainers();
  40.                 // This should never happen, but check anyway...
  41.                 if (containerIds == null || containerIds.Length == 0)
  42.                     return;
  43.  
  44.                 // Loop over the array of containers and dump some info...
  45.                 foreach (ObjectId containerId in containerIds)
  46.                 {
  47.                     Entity containerEnt = tr.GetObject(containerId, OpenMode.ForRead, false) as Entity;
  48.                     ed.WriteMessage($"\nContainer ID: {containerId}");
  49.                     DumpEntityInfo(containerEnt);
  50.                 }
  51.  
  52.                 // Commit the transaction...
  53.                 tr.Commit();
  54.             }
  55.  
  56.             ed.Regen();
  57.         }