Author Topic: Block Reference ?  (Read 7266 times)

0 Members and 1 Guest are viewing this topic.

jcoon

  • Newt
  • Posts: 157
Block Reference ?
« on: August 07, 2013, 08:04:27 AM »

Hi All,

Questions about block reference:
The test below is suppose to check for a block named "SURFACECUT" and if the block name is not found it is suppose to create the block. that works thanks to members here. second part of test is suppose to insert the block reference of the block named "SURFACECUT", which it does however after insertion I'm not able to select the block. that's the area I looking for some help on. block name if defined, then block is created and finally it is inserted. if I use the autocad insert command the block inserts correctly. why can't I list or select the block from this sample insertion.

Thanks
John

<CommandMethod("BlockNameCheckInsert")> _
    Public Sub BlockNameCheckInsert()
        Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim civilDoc As CivilDocument = CivilApplication.ActiveDocument
        Dim acaddoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument

        Dim blockName1 As String
        blockName1 = "SURFACECUT"
        Dim blockName2 As String
        blockName2 = "SURFACECUT-WITHINTENFEET"
        Dim ObjId As ObjectId = Nothing
        Dim mytrans As Transaction = db.TransactionManager.StartTransaction
        Try
            Dim id As ObjectId = Nothing
            ' Open the Block table for read
            Dim BlockTable As BlockTable
            BlockTable = mytrans.GetObject(db.BlockTableId, OpenMode.ForRead)
            'blockName1 = "SURFACECUT"
            If Not BlockTable.Has(blockName1) Then
                Using BlockTableRecord As New BlockTableRecord
                    BlockTableRecord.Name = blockName1
                    ' Set the insertion point for the block
                    BlockTableRecord.Origin = New Point3d(0, 0, 0)
                    BlockTableRecord.Units = UnitsValue.Feet
                   
                    Dim InsertionPt = New Point3d(0, 0, 0)
                    ' create point and lines to create block = "SURFACECUT"
                    Dim pt0(2) As Double
                    pt0(0) = 0.0# : pt0(1) = -0.0 : pt0(2) = 0.0#
                    Dim pt1(2) As Double
                    pt1(0) = 0.0# : pt1(1) = -0.04 : pt1(2) = 0.0#
                    Dim pt2(2) As Double
                    pt2(0) = -0.06 : pt2(1) = -0.04 : pt2(2) = 0.0#
                    Dim pt3(2) As Double
                    pt3(0) = 0.06 : pt3(1) = -0.04 : pt3(2) = 0.0#
                    Dim pt4(2) As Double
                    pt4(0) = -0.0 : pt4(1) = 0.08 : pt4(2) = 0.0#

                    Dim Line As Line = New Line(New Point3d(pt2), New Point3d(pt3))
                    Line.Layer = "0"
                    BlockTableRecord.AppendEntity(Line)
                    Line = New Line(New Point3d(pt3), New Point3d(pt4))
                    BlockTableRecord.AppendEntity(Line)
                    Line = New Line(New Point3d(pt4), New Point3d(pt2))
                    BlockTableRecord.AppendEntity(Line)
                    BlockTable.UpgradeOpen()
                    BlockTable.Add(BlockTableRecord)
                    mytrans.AddNewlyCreatedDBObject(BlockTableRecord, True)

                    Dim BlockTableModelSpace As BlockTableRecord = BlockTable(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForWrite)
                    Dim Blockref As New BlockReference(InsertionPt, BlockTableRecord.ObjectId)
                    BlockTableModelSpace.AppendEntity(Blockref)
                    ed.WriteMessage(vbCrLf + vbLf & "block created:" & blockName1)

                End Using
            End If
            ' Save the new object to the database
            mytrans.Commit()
            ' Dispose of the transaction
        Catch ex As Exception
        End Try
    End Sub
End Class

Micaletti

  • Guest
Re: Block Reference ?
« Reply #1 on: August 07, 2013, 09:16:05 AM »
All of your code is inside this If statement:

If Not BlockTable.Has(blockName1) Then
'Do Stuff
End If

You need to move the code that actually creates the reference below the if statement, because the second time it is ran, the if statement will be false. You also need to add mytrans.AddNewlyCreatedDBObject(Blockref, True).

Forgetting to add the AddNewlyCreatedDBObject is a common mistake. No problem, you're learning the API. Understanding If logic is a big deal however, and you're not yet. You have to learn to walk before you can run.
« Last Edit: August 07, 2013, 09:44:21 AM by Micaletti »

n.yuan

  • Bull Frog
  • Posts: 348
Re: Block Reference ?
« Reply #2 on: August 07, 2013, 09:57:02 AM »
Besides what the comment in the other reply, you also need to remove the Using at the point where new BlockTableRecord is created: the Using.. block ends before Transaction is committed, which will dispose the newly created BlockTablerecord. This could be the reason that your block reference is not selectable, because it is created based on a disposed block definition. In fact, after running your code, the database might already be corrupted.

You do not create a new DBObject in Using...block, if you intend to add it into database within a transaction.

Also, creating a variable with the name exactly the same as a class/type name (BlockTableRecord As New BlockTableRecord) is very bad practice, which makes the code very difficult to read and very confusing.

jcoon

  • Newt
  • Posts: 157
Re: Block Reference ?
« Reply #3 on: August 07, 2013, 10:47:21 AM »
Thanks, I'll make changes during lunch and see if that makes it clearer for me.

I thought the idea would be that if the block is not in the dwg then build the new block and if the block is already in the dwg then just go to the next block to check for.
under this test, if I ran it again it shouldn't create the newly built blocks. wouldn't that work flow work under this type of test? 

 If Not BlockTable.Has(blockName1) Then
'Do Stuff
End If

You need to move the code that actually creates the reference below the if statement, because the second time it is ran, the if statement will be false. You also need to add mytrans.AddNewlyCreatedDBObject(Blockref, True).

Micaletti

  • Guest
Re: Block Reference ?
« Reply #4 on: August 07, 2013, 11:14:10 AM »
It's not really clear what you are trying to do. If you are trying to write code that makes sure certain block definitions exist then you don't need to insert a block reference, you only need to create the definition. It will then be available for the user to insert.

Right now blockName2 is declared but not used, and the code is overly complex.

1) You don't need "ed", "civildoc" or "acaddoc".
2) I would avoid "Using" and "UpgradeOpen" until you start to wrap your head around transactions.

I am assuming your intention is to create two block definitions:

Code: [Select]
Public Sub CreateBlockDefinitions()
        Dim DB as Database = Application.DocumentManager.MdiActiveDocument.Database
        Dim T As Transaction = DB.TransactionManager.StartTransaction
        Try
            Dim BT As BlockTable = T.GetObject(DB.BlockTableId, OpenMode.ForWrite)
            Dim BlockDefName1 = "SomeBlock1"
            Dim BlockDefName2 = "SomeBlock2"
            If Not BT.Has(BlockDefName1) Then
                Dim BTR As New BlockTableRecord
                BTR.Name = BlockDefName1
                'Append entities to create the block def here.
                BT.Add(BTR)
                T.AddNewlyCreatedDBObject(BTR, True)
            End If
            If Not BT.Has(BlockDefName2) Then
                Dim BTR As New BlockTableRecord
                BTR.Name = BlockDefName2
                'Append entities to create the block def here.
                BT.Add(BTR)
                T.AddNewlyCreatedDBObject(BTR, True)
            End If
            T.Commit()
        Catch ex As System.Exception
            MsgBox(ex.ToString)
        Finally
            T.Dispose()
        End Try
    End Sub
« Last Edit: August 07, 2013, 12:04:26 PM by Micaletti »

jcoon

  • Newt
  • Posts: 157
Re: Block Reference ?
« Reply #5 on: August 07, 2013, 11:51:49 AM »
Your right it is confusing.  Yes, the definition is all I need to create the block but I have not tried the blockRef insert for a block yet so Included it so I could see how that works so that I could include it in a function later. I'm converting VBA application that I 've used for years. In VBA it was pretty simple but dot net is another animal with all the objectID and the like to wrap your head around. I got help from Jeff M a while back on the first part of this application which was selecting civil points and a surface.

n.yuan, I did change the variable names.  you were right it is a lot easier to read thanks. it appears to work correctly..........for now!

thanks for the help
John


  <CommandMethod("BlockNameCheckInsert")> _
    Public Sub BlockNameCheckInsert()
        Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor()
        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim civilDoc As CivilDocument = CivilApplication.ActiveDocument
        Dim acaddoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument

        Dim blockName1 As String
        blockName1 = "SURFACECUT"
        Dim ObjId As ObjectId = Nothing

        Dim mytrans As Transaction = db.TransactionManager.StartTransaction
        Try

            Dim id As ObjectId = Nothing
            ' Open the Block table for read
            Dim BT As BlockTable
            BT = mytrans.GetObject(db.BlockTableId, OpenMode.ForRead)
            'blockName1 = "SURFACECUT"
            If Not BT.Has(blockName1) Then
                Dim BTR As New BlockTableRecord
                BTR.Name = blockName1
                ' Set the insertion point for the block
                BTR.Origin = New Point3d(0, 0, 0)
                BTR.Units = UnitsValue.Feet
                ' create point and lines to create block = "SURFACECUT"
                Dim InsertionPt = New Point3d(0, 0, 0)

                Dim pt0(2) As Double
                pt0(0) = 0.0# : pt0(1) = -0.0 : pt0(2) = 0.0#
                Dim pt1(2) As Double
                pt1(0) = 0.0# : pt1(1) = -0.04 : pt1(2) = 0.0#
                Dim pt2(2) As Double
                pt2(0) = -0.06 : pt2(1) = -0.04 : pt2(2) = 0.0#
                Dim pt3(2) As Double
                pt3(0) = 0.06 : pt3(1) = -0.04 : pt3(2) = 0.0#
                Dim pt4(2) As Double
                pt4(0) = -0.0 : pt4(1) = 0.08 : pt4(2) = 0.0#

                Dim Line As Line = New Line(New Point3d(pt2), New Point3d(pt3))
                Line.Layer = "0"
                BTR.AppendEntity(Line)
                Line = New Line(New Point3d(pt3), New Point3d(pt4))
                BTR.AppendEntity(Line)
                Line = New Line(New Point3d(pt4), New Point3d(pt2))
                BTR.AppendEntity(Line)
                BT.UpgradeOpen()
                BT.Add(BTR)
                Dim BTMSpace As BlockTableRecord = BT(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForWrite)
                Dim Blockref As New BlockReference(InsertionPt, BTR.ObjectId)
                BTMSpace.AppendEntity(Blockref)
                ed.WriteMessage(vbCrLf + vbLf & "block created:" & blockName1)
                mytrans.AddNewlyCreatedDBObject(Blockref, True)

            End If

Micaletti

  • Guest
Re: Block Reference ?
« Reply #6 on: August 07, 2013, 11:55:53 AM »
I've made some edits to my above post. Paste that in, it should be much easier to follow.

jcoon

  • Newt
  • Posts: 157
Re: Block Reference ?
« Reply #7 on: August 07, 2013, 01:06:22 PM »
Micaletti,

I was successful switching out my sample with yours.
Thanks so much for your help, I'm sure I'll be back as I work on the other parts of this.

Thanks
John


MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Block Reference ?
« Reply #8 on: August 07, 2013, 01:30:47 PM »
2) I would avoid "Using" and "UpgradeOpen" until you start to wrap your head around transactions.


The Using statement and your Try/Catch/Finally block are identical in your example.  The difference would be if an exception occurred in this block of code but you'll never see it because any exception that would happen within your example would cause AutoCAD to fail and never return to your program. The Using statement has become the preferred method in .NET and AutoCAD and is the way you'll see in most AutoCAD examples from Autodesk and on The Swamp.
Revit 2019, AMEP 2019 64bit Win 10

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Block Reference ?
« Reply #9 on: August 07, 2013, 01:56:25 PM »
Sometimes it helps to break it down to task and responsibilities, and class or functions should focus on one thing and do that thing well.
 
You are
1) Checking if BlockTableRecord exists
2) Creating a BlockTableRecord if it does not
3) Inserting a BlockReference from BlockTableRecord
 
You can break this down to make it more reusable, easier to read, debug, etc..  and many different ways to approach it
 
To start off with first round of a simple refactor and this will NOT make it robust, re-usable, or close to deployable to many users but start breaking down in different parts and start in better direction.
 
Ask yourself
Should a function to insert a BlockReference need to worry about creating a BlockTableRecord?
Should it need to care if it was just created or been there for days, years, etc...
 
One hint would be the function name and to be more correct would be CheckBlockNameCreateIfNotInDrawingInsert. So maybe first step would be getting functions name to contain only one verb.
 
Its up to you to decide which responsibilities and contracts to implement like
1)Should the function to insert be responsible for checking if it exists and if not exist then call function to create it.
or
2)The Insertion function should Insert.
 
Can you see how first options now requires insertion function responsible for still creating BlockTableRecords and if you need more than one then it needs to know how to do that.
 
The insertion function signature could have a parameter for the ObjectId of the BlockTableRecord, which for a BlockTableRecord to have a ObjectId it must be in the drawing(up to you for checking if it is valid, is from correct database, etc...)
 
 
 
Depending on requirements there are many different approaches.
 
 
So just making up a quick example and better ways to do it but to get started in better direction
For a short easy example say you have folders containing drawings for blocks and folder is in SearchPaths
 
3 Functions
 
 
InsertBlockReference(Name)
 If not In drawing
  CreateBlock(Name)
 InsertBlock(Id)
 
CreateBlock(name)
 FindFile(Name)
 InsertBlock("Path from Filename")
 
InsertBlock(Id)
 ......
 ......
 
As far as inserting blocks and you are going to get user input and provide visual feedback then I would script insert command.
Unless you want to deal with scaling, scales uniformly, is it annotative, alignment parameters, attributes, attributes with fields, Ucs, etc...
 
Again just quick example to help maybe push in right direction
 
 

Micaletti

  • Guest
Re: Block Reference ?
« Reply #10 on: August 07, 2013, 03:44:32 PM »
The Using statement and your Try/Catch/Finally block are identical in your example.

He was using both try/catch and using, which is redundant and confusing.

The difference would be if an exception occurred in this block of code but you'll never see it because any exception that would happen within your example would cause AutoCAD to fail and never return to your program.

Huh?? Can you elaborate on this? It catches the exception, gives you a msgbox, and disposes the transaction. I've seen it happen. We've all seen that. What do you mean by "would cause AutoCAD to fail and never return to your program"? Are you talking about stuff like eNotOpenForWrite? You'll crash AutoCAD either way.
« Last Edit: August 07, 2013, 04:58:24 PM by Micaletti »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Block Reference ?
« Reply #11 on: August 07, 2013, 04:12:23 PM »
MSDN is very useful
 
Quote

The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler. The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):
http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Micaletti

  • Guest
Re: Block Reference ?
« Reply #12 on: August 07, 2013, 04:13:57 PM »
MSDN is very useful
 
Quote

The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler. The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):
http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Thanks Jeff but we've already established that. That wasn't what I was asking. In fact it says using is translated to try catch finally by the compiler...

Let me clarify. He said Try/Catch/Finally:

Quote
"would cause AutoCAD to fail and never return to your program"

Which isn't true at all, he's confused. If you crash ACAD with Try/Catch/Finally, you're going to crash it with Using too. I'm sorry if I appear to be the abrasive "new guy" but you can't be like "oh well we use using around here so that's what you should use", and then proceed to spout misinformation, further confusing a person I am trying to help.

Now then, if you guys would like to explain when and why it is better without any hearsay or bias, I'd like to hear it, however MSDN just told you they are basically the same thing!

The cool thing about Try/Catch/Finally is you can do a lot more than dispose one object. You can actually provide some feedback as to what went wrong, AND you can add code to handle the exception gracefully.
« Last Edit: August 07, 2013, 06:56:27 PM by Micaletti »

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: Block Reference ?
« Reply #13 on: August 07, 2013, 05:26:03 PM »
pssst jcoon: see how the others code examples are formatted?  Easier to read, isn't it?  Have a look at the "Insert code" button in the Post reply section.   :angel:
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Block Reference ?
« Reply #14 on: August 07, 2013, 06:51:23 PM »
Sorry to disappoint you Micaletti but you need try harder and put forth more effort if you want to be considered abrasive.
I am kidding.
 
Nothing to do with the validity of what anyone posted in this thread, but when it comes to anything I post if anyone thinks what I post is misleading or wrong then point it out and make it known. I want to know when I am wrong.
If someone has problem with being pointed out they are incorrect then their answers an advice are effected by motivation that they care what people think.
I guess that is easy for me to say since I am never wrong.
 
 
A Finally block is used to guarantee a block of code will execute.
One reason to use try/finally is to make sure you clean up resources 
"Using" uses try/finally and also cast the object to IDisposable in finally block, so in a way to me is a easy way to see that your using Try/Finally to clean-up resources(to be correct call Dispose on an object that implement IDisposable) .
 
So I prefer using because it makes it easier for me to se that I am dealing with an object that implements IDisposable.
And helps me make sure I correctly keep it scoped and not able to be used once disposed, and  just looks cleaner.
 
One thing to consider with example that makes it different from using is the addition of Catch block.
When catching exceptions in a way you are saying you expected this might happen, and you understand why it happened, and you know how to deal with it.
 
 
I can not think of a specific example for AutoCAD but with adding Catch block especially with System.Exception should re-throw it and even though it might still crash and burn the application could have a exception handling for fixing corrupted objects or creating recovery file that will never happened since the exception was swallowed up before it could make it up the call stack.