Block with Multiple Attribute References

Block with Multiple Attribute References

youssefGC
Advocate Advocate
893 Views
12 Replies
Message 1 of 13

Block with Multiple Attribute References

youssefGC
Advocate
Advocate

Hello,

I tried to create a block composed of a polyline and multiple attributes.

Here is the part of the program. Please note that "ScalledPline" is the polyline on which I will create the block, and "_nameofbl" is the name of the block. The variables "insertPoint (point3d), _lstofposition (List<Pointe3d>), _lstoftextstring (List<string>), _lstofrotation (List<double>)" are used to store data related to the attributes (texts, positions, rotations).

 

// ScalledPline (polyline), insertPoint(point3d), _lstOfPosition (List<Point3d>), _lstOfTextString (List<string>)
            //_nameOfBl (string), _lstOfRotation (List<double>),  insertPoint : given


            // Get the current database and start a transaction
            Database db = Application.DocumentManager.MdiActiveDocument.Database;

            using (Transaction acTrans = db.TransactionManager.StartTransaction())
            {
                // Open the Block table for read
                BlockTable bt;
                bt = acTrans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

                ObjectId blkRecId = ObjectId.Null;

                if (!bt.Has(_nameOfBl))
                {
                    using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
                    {
                        acBlkTblRec.Name = _nameOfBl;

                        // Set the insertion point for the block
                        acBlkTblRec.Origin = insertPoint;

                        // Add pline with attribute onto block
                        using (ScalledPline)
                        {
                            acBlkTblRec.AppendEntity(ScalledPline);

                            // Add an attribute definition to the block
                            for (int i = 0; i < _lstOfDBText.Count; i++)
                            {
                                using (AttributeDefinition acAttDef = new AttributeDefinition())
                                {
                                    acAttDef.Position = _lstOfPosition[i];
                                    acAttDef.Prompt = "Txt N°" + (i + 1).ToString();
                                    acAttDef.Tag = "Txt " + (i + 1).ToString();
                                    acAttDef.TextString = _lstOfTextString[i];
                                    acAttDef.Rotation = _lstOfRotation[i];

                                    acAttDef.Height = 1;
                                    acAttDef.Justify = AttachmentPoint.MiddleCenter;
                                    acBlkTblRec.AppendEntity(acAttDef);
                                }
                            }

                            bt.UpgradeOpen();
                            bt.Add(acBlkTblRec); // error in this line
                            acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);

                        }

                        blkRecId = acBlkTblRec.Id;
                    }
                }
                else
                {
                    blkRecId = bt[_nameOfBl];
                }

                //========== 3 - Insert the block into the current space
                if (blkRecId != ObjectId.Null)
                {
                    BlockTableRecord acBlkTblRec;
                    acBlkTblRec = acTrans.GetObject(blkRecId, OpenMode.ForRead) as BlockTableRecord;

                    // Create and insert the new block reference
                    using (BlockReference acBlkRef = new BlockReference(insertPoint, blkRecId))
                    {
                        BlockTableRecord acCurSpaceBlkTblRec;
                        acCurSpaceBlkTblRec = acTrans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                        acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                        acTrans.AddNewlyCreatedDBObject(acBlkRef, true);

                        // Verify block table record has attribute definitions associated with it
                        if (acBlkTblRec.HasAttributeDefinitions)
                        {
                            // Add attributes from the block table record
                            foreach (ObjectId objID in acBlkTblRec)
                            {
                                DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                                if (dbObj is AttributeDefinition)
                                {
                                    AttributeDefinition acAtt = dbObj as AttributeDefinition;

                                    if (!acAtt.Constant)
                                    {
                                        using (AttributeReference acAttRef = new AttributeReference())
                                        {
                                            acAttRef.SetAttributeFromBlock(acAtt, acBlkRef.BlockTransform);
                                            acAttRef.Position = acAtt.Position.TransformBy(acBlkRef.BlockTransform);

                                            acAttRef.TextString = acAtt.TextString;

                                            acBlkRef.AttributeCollection.AppendAttribute(acAttRef);

                                            acTrans.AddNewlyCreatedDBObject(acAttRef, true);
                                        }
                                    }
                                }
                            }
                        }


                    }
                }


                // Save the new object to the database
                acTrans.Commit();


            }

 

Unfortunately, I encountered an issue with a function I'm using : Unhandled Access Violation Reading

 

Thank you.

0 Likes
894 Views
12 Replies
Replies (12)
Message 2 of 13

_gile
Consultant
Consultant

Hi,

The main issue I see is about ScalledPline.

The polyline should be created within the transaction (acTrans), appended to the block definition and added to the transaction.

To be able to find and correct errors in your code, you should split it into several methods which can be tested separately.

For example:

A 'GetOrCreateBlockDefinition' which checks if the BlockTable contains a bloc definition named as '_nameofbl' and, if it does, return the block definition ObjectId; else, create the block definition, add it to the block table create the polyline and append it to the block definition, create the attributes, add them to the block definition and finally return the ObjectId of this new block definition.

And another 'InsertBlockReference' method, which calls 'GetOrCreateBlockDefinition' to get the ObjectId of the block definition, and inserts a new block reference.

This way, you could first test and debug the 'GetOrCreateBlockDefinition' method until it works before trying to insert the block programmatically.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 13

shubhamraut221195
Enthusiast
Enthusiast

@youssefGC  In your code write 'bt.Add(acBlkTblRec)' before 'using (ScalledPline){ }'

Message 4 of 13

shubhamraut221195
Enthusiast
Enthusiast
Right now 'bt.Add(acBlkTblRec)' is inside 'using (ScalledPline){ }'
0 Likes
Message 5 of 13

shubhamraut221195
Enthusiast
Enthusiast

@_gile , When I created new BlockTableRecord and added it to BlockTable & then went for creating polyline , it worked. In code shared by @youssefGC , he is adding BlockTableRecord inside 'using (ScalledPline){  }'. I'm not able to understand how using(){  }  works in AutoCAD.NET. I know about 'using(Transaction ....){}' but apart from that I'm not able to understand how 'using(){}' works. Can you please give a hint?

0 Likes
Message 6 of 13

_gile
Consultant
Consultant

@shubhamraut221195  a écrit :

 I'm not able to understand how using(){  }  works in AutoCAD.NET.


The using statement is not specific to AutoCAD .NET API. It is used to ensure ensures the correct use of an IDisposable instance (see this topic).

using (someDisposableInstance)
{
    // do your stuff with someDisposableInstance
}

is equivalent to:

try
{
    // do your stuff with someDisposableInstance
}
finally
{
    someDisposableInstance.Dispose();
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 7 of 13

shubhamraut221195
Enthusiast
Enthusiast

@_gile Ok. So why can't we add BlockTableRecord to BlockTable inside using(Polyline.....){  }? Is it because polyline which is part of BlockTableRecord which is not disposed yet?

0 Likes
Message 8 of 13

_gile
Consultant
Consultant

@shubhamraut221195  a écrit :

So why can't we add BlockTableRecord to BlockTable inside using(Polyline.....){  }? Is it because polyline which is part of BlockTableRecord which is not disposed yet?


The problem is not adding the BlockTableRecord to the BlockTable inside using(Polyline.....) statement.

The problem is adding a polyline (and AttributeDefinitions) to the BlockTableRecord before having added the BlockTableRecord to the BlockTable.

 

Typically, the process should be like below (no need to use a using statement for the Polyline because it is always added to the Transaction):

 

using(Transaction)
{
    Create blockTableRecord
    Add BlockTableRecord to BlockTable
    Add BlockTableRecord to Transaction

    Create polyline
    Add Polyline to BlockTableRecord
    Add Polyline to Transaction

    For each AttributeDefinition
        Create AttributeDefinition
        Add AttributeDefinition to BlockTableRecord
        Add AttributeDefinition to Transaction

    Commit Transaction
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 9 of 13

youssefGC
Advocate
Advocate

Thank you for your reply @_gile,
Yes, indeed, I have examined the code by subdividing it into several methods. In fact,
The problem lies with the "ScalledPline" element, which needs to be added to the "acTrans" transaction before being added as a block.

                bt.UpgradeOpen();
                bt.Add(ScalledPline); 
                acTrans.AddNewlyCreatedDBObject(ScalledPline, true);

 

0 Likes
Message 10 of 13

youssefGC
Advocate
Advocate

Yes, but the source of the problem is that I am adding "ScalledPline" to a block before it is added to the transaction.

0 Likes
Message 11 of 13

_gile
Consultant
Consultant

@youssefGC  a écrit :

Thank you for your reply @_gile,
Yes, indeed, I have examined the code by subdividing it into several methods. In fact,
The problem lies with the "ScalledPline" element, which needs to be added to the "acTrans" transaction before being added as a block.

                bt.UpgradeOpen();
                bt.Add(ScalledPline); 
                acTrans.AddNewlyCreatedDBObject(ScalledPline, true);

 


In this snippet, you add the polyline to the block table which does not make sense (you can only add new BlockTableRecord instances to the block table).

 

We do not know where the ScalledPline comes from, how it have been created.

Typically, it should be created inside the same transaction the new block definition (BlockTableRecord) is created and appended to this block definition and the transaction as described in the pseudo code of this reply.

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 12 of 13

youssefGC
Advocate
Advocate

Sorry, I didn't explain the origin of "scalledPline" properly. In fact, in my main program, "scalledPline" is a polyline resulting from a Jig. Then, I want to create a block using this polyline.

Finally, I want to save the original polyline, as well as create a block (based on the polyline, of course), and insert it in another location.

0 Likes
Message 13 of 13

_gile
Consultant
Consultant

@youssefGC  a écrit :

Sorry, I didn't explain the origin of "scalledPline" properly. In fact, in my main program, "scalledPline" is a polyline resulting from a Jig. Then, I want to create a block using this polyline.

Finally, I want to save the original polyline, as well as create a block (based on the polyline, of course), and insert it in another location.


The same polyline cannot be added to several BlockTableRecords (the model space and a block definition).

You could add the polyline to the model space after the Jig and pass its ObjectId to the method where you create the block definition. In this method, you can use DeepCloneObjects to copy the polyline into the newly created block definition.

 

Pseudo code:

using(Transaction)
{
    Create blockTableRecord
    Add BlockTableRecord to BlockTable
    Add BlockTableRecord to Transaction

    DeepClone the polyline

    For each AttributeDefinition
        Create AttributeDefinition
        Add AttributeDefinition to BlockTableRecord
        Add AttributeDefinition to Transaction

    Commit Transaction
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub