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

0 Members and 1 Guest are viewing this topic.

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.         }