Author Topic: Accessing objects on a layout  (Read 4594 times)

0 Members and 1 Guest are viewing this topic.

GILESP

  • Newt
  • Posts: 42
Accessing objects on a layout
« on: June 12, 2013, 11:50:13 AM »
(This is probably really simple for most, but I'm still pretty new to the way Autocad is organised in .NET)

I'm trying to find/access a table entity (to be precise) on a paperspace layout -there are several in the dwg file.

I've managed to get a list of layouts using the LayoutDictionaryId :

Code: [Select]
               
                Dim Layoutlist As DBDictionary = ScanTrans.GetObject(Dbase.LayoutDictionaryId, OpenMode.ForRead)
                For Each LayoutID As DBDictionaryEntry In Layoutlist
                    layoutName = LayoutID.Key

..with a bit of conditional code following to determine if it's the right layer, but I'm stuck as to where to go from here.

Eventually I'll want to delete and replace said table if it exists, so I guess the OpenMode above should be ForWrite

How can I get the contents of the named layout in such a way I can search and identify the table? I've found lots of code fragments that will find blocks in paperspace, but that uses block records which I can't see applying to a table object.

Any thought, hints or code examples would be greatly appreciated..

G
..END OF LINE..

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Accessing objects on a layout
« Reply #1 on: June 12, 2013, 12:40:35 PM »
ModelSpace, PaperSpace, & "Block definitions" are all BlockTableRecords which can contain entites.
 
I've found lots of code fragments that will find blocks in paperspace, but that uses block records which I can't see applying to a table object.

If you mean BlockReferences in Paperspace a Table is really a BlockReference.
 
I would only run this on drawings test drawings or ones you plan to delete,
but after you run it you can open BlockEditor and will see blocks named
T0, T1 - for tables
D0,D1 - for dimensions
U1, U2 - for dynamic blocks with different settings
etc....
 
Edit...
Updated not open all Blocks
Code - C#: [Select]
  1.         /////Does not check if a block already exists with trimmed anonymous name
  2.         [CommandMethod("AnonToNamed")]
  3.         public void AnonToUnAnon()
  4.         {
  5.             Document doc = Application.DocumentManager.MdiActiveDocument;
  6.             Database db = doc.Database;
  7.             char[] trimChars = new char[] { '*' };
  8.             using (Transaction trx = Db.TransactionManager.StartTransaction())
  9.             {
  10.                 BlockTable bt = (BlockTable)trx.GetObject(db.BlockTableId, OpenMode.ForRead, false, false);
  11.                 foreach (ObjectId id in bt)
  12.                 {
  13.                     BlockTableRecord btr = (BlockTableRecord)trx.GetObject(id, OpenMode.ForRead, false, false);
  14.                     if (btr.IsAnonymous)
  15.                     {
  16.                         btr.UpgradeOpen();
  17.                         btr.Name = btr.Name.TrimStart(trimChars);
  18.                     }
  19.                    
  20.                 }
  21.                 trx.Commit();
  22.             }
  23.         }

You can filter through the Objectids in Paperspaces checking if the ObjectId.ObjectClass is for a table, and then use whatever filtering logic needed to determine if is a table you after.
 
If your using a version prior to ObjectClass being added or I am not being clear I can try to explain better or one the many smarter guys here would have better input.
 
« Last Edit: June 12, 2013, 12:52:31 PM by Jeff H »

fixo

  • Guest
Re: Accessing objects on a layout
« Reply #2 on: June 12, 2013, 01:25:35 PM »
Another way, tested on A2010
 
Code: [Select]
      <CommandMethod("seltb")> _
        Public Shared Sub testSelectTable()

            'Dim countblocks As New Dictionary(Of KeyValuePair(Of String, Object), Long)
            'Dim blkname As String
            Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument()
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor
            Try
                Using tr As Transaction = db.TransactionManager.StartTransaction

                    Dim res As PromptSelectionResult
                    ' change values to your suit
                    Dim tvs(2) As TypedValue
                    tvs(0) = New TypedValue(0, "acad_table")
                    tvs(1) = New TypedValue(1, "*title_text*")
                    tvs(2) = New TypedValue(410, "Layout2")
                    '' you can also add a filter to select table with known rows count, see DXF codes
                    Dim filt As SelectionFilter = New SelectionFilter(tvs)
                    ' select all tables
                    res = ed.SelectAll(filt)

                    If res.Status <> PromptStatus.OK Then
                        Return
                    End If
                    For Each sobj As SelectedObject In res.Value
                        Dim obj As DBObject = DirectCast(tr.GetObject(sobj.ObjectId, OpenMode.ForRead, False), DBObject)
                        Dim tb As Table = TryCast(obj, Table)
                        If tb Is Nothing Then Return
                        Dim tbowner As BlockTableRecord = DirectCast(tr.GetObject(tb.OwnerId, OpenMode.ForRead, False), BlockTableRecord)

                        'if this is found the open to edit
                        tb.UpgradeOpen()
                        'display the result just for debug
                        Dim sb As New StringBuilder
                        sb.AppendLine(String.Format("---------------------------------------"))
                        sb.AppendLine(String.Format("Drawing name: {0}", doc.Name))

                        sb.AppendLine(String.Format("Tab: {0}", tbowner.Name))

                        Dim numrows As Integer = tb.Rows.Count
                        Dim numcols As Integer = tb.Columns.Count
                        For i As Integer = 0 To numrows - 1
                            Dim rowline As New List(Of String)
                            Dim line As String = String.Empty
                            For j = 0 To numcols - 1
                                Dim cel As Cell = tb.Cells(i, j)
                                rowline.Add(cel.TextString)
                                line = line + cel.TextString + vbTab
                            Next

                            sb.AppendLine(line.TrimEnd(vbTab))

                        Next
               
                        Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(sb.ToString())
                        ''_____________________________________________________________________________''
                        '' rest your code is here
                        ''_____________________________________________________________________________''

                    Next
     
                    tr.Commit()
                End Using
            Catch ex As Autodesk.AutoCAD.Runtime.Exception

                Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog((ex.ToString() & vbLf) + ex.Message)
            Finally
                ed.WriteMessage(vbLf + "Pokey")
            End Try

        End Sub

GILESP

  • Newt
  • Posts: 42
Re: Accessing objects on a layout
« Reply #3 on: June 13, 2013, 04:01:37 AM »
Jeff - Fixo

Thanks for the responses. I'm still very new to some of the concepts and structures in VB.net so it's taking me a while to figure out the details of your code examples -particularly where you've used concepts I've not encountered before. 

I forgot that Autocad lists tables as anonymous blocks, though I think I like the idea of filtering by objectclass.

I need to spend a bit of time picking apart the code samples above, but if anyone else has anything to throw into the pot,  the more ways of doing something I see, the better chance I have of understanding it..

thanks!
..END OF LINE..

GILESP

  • Newt
  • Posts: 42
Re: Accessing objects on a layout
« Reply #4 on: June 13, 2013, 12:59:42 PM »
Been prodding this code a bit more inbetween other projects (not a good way to learn, I know).

Come up with the following (a munge of Jeff H's code above and something I found online from Kean Walmsley):

Code: [Select]
                Dim BlTable As BlockTable = CType(ScanTrans.GetObject(Dbase.BlockTableId, OpenMode.ForRead, False, False), BlockTable)
                For Each BlkID As ObjectId In BlTable
                    Dim blkEnt As Entity = CType(ScanTrans.GetObject(BlkID, OpenMode.ForRead), Entity)

which compiles fine, but on execution VS comes up with some error:

Quote
"InvalidCastException was unhandled by user code"
unable to cast object of type
'Autodesk.Autocad.DatabaseServices.BlockTableRecord' to type
'Autodesk.Autocad.DatabaseServices.Entity'

blah blah

I guess it doesn't like me trying to turn a block into an entity.

Any idea how I can either: extract the block/table from the database as an entity. Or, turn the blocktablerecord into an entity???

I figure I'd have better luck trying to manipulate the entity than the blocktablerecord as it'll allow access to useful properties such as insert point and layer.. 

Am I on the right path here?
..END OF LINE..

TheMaster

  • Guest
Re: Accessing objects on a layout
« Reply #5 on: June 13, 2013, 01:26:36 PM »
Been prodding this code a bit more inbetween other projects (not a good way to learn, I know).

Come up with the following (a munge of Jeff H's code above and something I found online from Kean Walmsley):

Code: [Select]
                Dim BlTable As BlockTable = CType(ScanTrans.GetObject(Dbase.BlockTableId, OpenMode.ForRead, False, False), BlockTable)
                For Each BlkID As ObjectId In BlTable
                    Dim blkEnt As Entity = CType(ScanTrans.GetObject(BlkID, OpenMode.ForRead), Entity)

which compiles fine, but on execution VS comes up with some error:

Quote
"InvalidCastException was unhandled by user code"
unable to cast object of type
'Autodesk.Autocad.DatabaseServices.BlockTableRecord' to type
'Autodesk.Autocad.DatabaseServices.Entity'

blah blah

I guess it doesn't like me trying to turn a block into an entity.

Any idea how I can either: extract the block/table from the database as an entity. Or, turn the blocktablerecord into an entity???

I figure I'd have better luck trying to manipulate the entity than the blocktablerecord as it'll allow access to useful properties such as insert point and layer.. 

Am I on the right path here?

A BlockTableRecord is the managed object that represents a block definition (e.g., the entities comprising the block).

A BlockReference is the managed object that represents an insertion of a block.

Have you gone through all of the learning materials available for AutoCAD managed development?  The mistake your making here (confusing a BlockReference and a BlockTableRecord) would suggest you've not done that.

Nothing wrong with that, except that you'll save yourself a lot of time and having to ask questions, if you were to study all of the learning materials available.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Accessing objects on a layout
« Reply #6 on: June 13, 2013, 03:08:16 PM »
You're going to struggle very hard unless you take a step back from your problem and try to understand what it is you need to do to solve your problem.  Stop thinking about the table you want on your layout for a minute and consider that it's buried inside a database.  Unless you understand what you need to do to navigate this database.

Have you downloaded the ObjectARX SDK? If not stop everything and go do that. Once you get that installed look at the class map and it will help you understand the following:

The key database objects are dictionaries and symbol tables. The core functionality is held in the symbol tables (older style database objects) and the more recent add on functionality uses dictionaries. 

There are several tables in the database which derive from symbol table, the one of interest to you now is the BlockTable.  The block table is just a container for BlockTableRecord objects, which are a container for Entities.  The first BTR in the table is model space, then paper space, then all the block definitions.

Looking at the classmap you will see that BlockTableRecord is not an Entity, an Entity is one of the visual objects you can see on your screen, it is a container which can hold entities.

Back to your problem... You're after a Table object which is derived from BlockReference on a layout

You've got the BlockTable, you need to access the BlockTableRecord for the layout you're interested in.  If you know the name you can access it's ObjectId using BlTable["My Layout"] and then open it for read, there are other ways to do this.

If there is only 1 table on the layout the next part is pretty easy, go through each ObjectId in that BTR opening each object for read As Table, if the result is null the object was not a table so keep looking.  Once you find the table of interest erase it and replace it. Done

Without knowing where you are in the database you're seeing all these tables and getting excited thinking they're your table... They're not... Get a map and go deeper

GILESP

  • Newt
  • Posts: 42
Re: Accessing objects on a layout
« Reply #7 on: June 14, 2013, 04:24:23 AM »
TT - Will

Thanks for the guidance - I think you're both right, in the fact I need to step back and look at the bigger picture a bit. My mistake, I think, is thinking I could pick up VB.NET like I did VBA. There appears to be very little in comparison (apart from the first two words of the acronym), and the structure within it are wildly different, or perhaps more complex in the case of VB.NET.

I think I will go back to the published literature and the SDK (which has been downloaded), and read it again, though this doesn't really suit my style of learning I'm more of a fiddle-and-find-out learner rather than a buy-the-book (good thing I'm not into nuclear physics)

Thanks all, I'll be back in a bit  :wink:
« Last Edit: June 14, 2013, 04:30:06 AM by GILESP »
..END OF LINE..