Dim hingeBlockFilePath As String = "C:\cad\support\ads\ss edge\DOORNUM.dwg"
Dim inspt As New Point3d(7.53, 10.35, 0)
Dim newBlockName As String = "DOORNUMBER"
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Using docloc As DocumentLock = doc.LockDocument
Using trans As Transaction = doc.TransactionManager.StartTransaction
'Read Block Drawing's Database
Dim db As New Database(False, False)
db.ReadDwgFile(hingeBlockFilePath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString)
'insert it as a new block
Dim idBTR As ObjectId = doc.Database.Insert(newBlockName, db, True)
Using bt As BlockTable = trans.GetObject(doc.Database.BlockTableId, OpenMode.ForRead), _
btr As BlockTableRecord = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite), _
bref As BlockReference = New BlockReference(inspt, idBTR)
btr.AppendEntity(bref)
trans.TransactionManager.AddNewlyCreatedDBObject(bref, True)
Dim blkTblR As BlockTableRecord = btr.Id.GetObject(OpenMode.ForRead)
For Each objId As ObjectId In blkTblR
Dim obj As DBObject = objId.GetObject(OpenMode.ForRead)
If TypeOf obj Is AttributeDefinition Then
Dim ad As AttributeDefinition = objId.GetObject(OpenMode.ForRead)
Dim ar As AttributeReference = New AttributeReference()
ar.SetAttributeFromBlock(ad, bref.BlockTransform)
ar.Position = ad.Position.TransformBy(bref.BlockTransform)
bref.AttributeCollection.AppendAttribute(ar)
trans.AddNewlyCreatedDBObject(ar, True)
End If
Next
Dim myAttColl As AttributeCollection = bref.AttributeCollection
For Each myAttRefId As ObjectId In myAttColl
Dim myAttRef As AttributeReference = myAttRefId.GetObject(OpenMode.ForWrite)
If myAttRef.Tag = "DRNUM" Then myAttRef.TextString = "TEST"
Next
trans.Commit()
End Using
End Using
End Using
Public Sub Main()
Dim hingeBlockFilePath As String = "C:\cad\support\ads\ss edge\DOORNUM.dwg"
Dim inspt As New Point3d(7.53, 10.35, 0)
Dim newBlockName As String = "DOORNUMBER"
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Using docloc As DocumentLock = doc.LockDocument
Dim curdb As Database = HostApplicationServices.WorkingDatabase
Using trans As Transaction = doc.TransactionManager.StartTransaction
'Read Block Drawing's Database
Dim bt As BlockTable = curdb.BlockTableId.GetObject(OpenMode.ForRead)
If bt.Has(newBlockName) = False Then
Dim db As New Database(False, False)
db.ReadDwgFile(hingeBlockFilePath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString)
Dim idBTR As ObjectId = curdb.Insert(newBlockName, db, True)
db.Dispose()
End If
Dim btr As BlockTableRecord = bt(newBlockName).GetObject(OpenMode.ForRead)
Dim btrPS As BlockTableRecord = bt(BlockTableRecord.PaperSpace).GetObject(OpenMode.ForWrite)
Dim bref As New BlockReference(inspt, btr.ObjectId)
btrPS.AppendEntity(bref)
trans.AddNewlyCreatedDBObject(bref, True)
Dim blkTblR As BlockTableRecord = btr.Id.GetObject(OpenMode.ForRead)
For Each objId As ObjectId In blkTblR
Dim obj As DBObject = objId.GetObject(OpenMode.ForRead)
If TypeOf obj Is AttributeDefinition Then
Dim ad As AttributeDefinition = objId.GetObject(OpenMode.ForRead)
Dim ar As AttributeReference = New AttributeReference()
ar.SetAttributeFromBlock(ad, bref.BlockTransform)
ar.Position = ad.Position.TransformBy(bref.BlockTransform)
bref.AttributeCollection.AppendAttribute(ar)
trans.AddNewlyCreatedDBObject(ar, True)
End If
Next
Dim myAttColl As AttributeCollection = bref.AttributeCollection
For Each myAttRefId As ObjectId In myAttColl
Dim myAttRef As AttributeReference = myAttRefId.GetObject(OpenMode.ForWrite)
If myAttRef.Tag = "DRNUM" Then myAttRef.TextString = "test"
Next
trans.Commit()
End Using
End Using
End Sub
Will,
Thanks for the suggestions.
Do you have any code examples for creating a dictionary of attribute tags and object id?
Thanks
Brian
BlockTableRecord btr = // assign to a BlockTableRecord
Dictionary<string, ObjectId> attdefs =
btr.GetObjects<AttributeDefinition>()
.ToDictionary(
a => a.Tag,
a => a.ObjectId,
StringComparer.OrdinalIgnoreCase );
Hi,Tony
It seems that BlockTableRecord doesnot support your GetObjects method.
It seems that BlockTableRecord doesnot support your GetObjects method.Just a quick test, seems to me it works on my A2010
public static class BlockExtensions
{
public static T GetObject<T>(this ObjectId id) where T : DBObject
{
return id.GetObject(OpenMode.ForRead, false, false) as T;
}
public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject
{
return ids
.Cast<ObjectId>()
.Select(id => id.GetObject(OpenMode.ForRead, false, false))
.OfType<T>();
}
public static Dictionary<string, ObjectId> GetAttributeDefinitions(this BlockReference br)
{
BlockTableRecord btr = (BlockTableRecord)(br.IsDynamicBlock ? br.BlockTableRecord.GetObject(OpenMode.ForRead,false) : br.DynamicBlockTableRecord.GetObject(OpenMode.ForRead,false));
Dictionary<string, ObjectId> attdefs =
btr.GetObjects<AttributeDefinition>()
.ToDictionary(
a => a.Tag,
a => a.ObjectId,
StringComparer.OrdinalIgnoreCase);
return attdefs;
}
[CommandMethod("TTD")]
public static void testAttDefs()
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
doc.TransactionManager.EnableGraphicsFlush(true);
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false, true);
PromptEntityOptions peo = new PromptEntityOptions("\nPlease, select block: ");
peo.SetRejectMessage("\nYou have to select BlockReference only!");
peo.AllowNone = false;
peo.AllowObjectOnLockedLayer = true;
peo.AddAllowedClass(typeof(BlockReference), false);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
ObjectId objId = per.ObjectId;
if (!objId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(BlockReference))))
{
ed.WriteMessage("\nYou didn't select a BlockReference, please try again...\n");
return;
}
DBObject blkobj = (DBObject)tr.GetObject(objId, OpenMode.ForRead, false);
BlockReference bref = blkobj as BlockReference;
if (bref == null) return;
Dictionary<string, ObjectId> attefs = GetAttributeDefinitions(bref);
foreach (KeyValuePair<string, ObjectId> kvp in attefs)
{
ed.WriteMessage("\n{0}\t{1}", kvp.Key, kvp.Value);
}
tr.Commit();
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n" + ex.Message);
}
}
}
Hi,Tony
It seems that BlockTableRecord doesnot support your GetObjects method.
Dim attributes as Dictionary<string, ObjectId> = new Dictionary<string, ObjectId>()
For Each objId As ObjectId In blkTblR
Dim obj As DBObject = objId.GetObject(OpenMode.ForRead)
If TypeOf obj Is AttributeDefinition Then
Dim ad As AttributeDefinition = objId.GetObject(OpenMode.ForRead)
Dim ar As AttributeReference = New AttributeReference()
ar.SetAttributeFromBlock(ad, bref.BlockTransform)
ar.Position = ad.Position.TransformBy(bref.BlockTransform)
attributes.Add(ar.Tag, bref.AttributeCollection.AppendAttribute(ar))
trans.AddNewlyCreatedDBObject(ar, True)
End If
Next
Code - C#: [Select]
public static T GetObject<T>(this ObjectId id) where T : DBObject { return id.GetObject(OpenMode.ForRead, false, false) as T; }
Code - C#: [Select]
public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject { return ids .Cast<ObjectId>() .Select(id => id.GetObject(OpenMode.ForRead, false, false)) .OfType<T>(); }
Also the code does not check for constant attribute definitions which do not get a corresponding attribute reference (they make no sense to me as they are just like text.... does anybody know why they exist?)I have wondered the same thing.
Certain people abhor passing a non-generic sequence to a functon which will produce run-time errors when fed an argument of the wrong type, like string (IEnumerable of char), when a sequence of ObjectId is expected. Make it an overload of the specific class.That is the way it always seemed easier for me was creating some generic methods and then more specific for classes up the hierarchy.
For example creating generic functions for SymbolTables then more specific for each of the Tables derived from SymbolTable,
Hi,
The Yield statement only exists in VB 11 .NET 4.5.
Gaston Nunez
BlockTableRecord implements IEnumarable, and that's the only SymbolTableRecord off the top of my head I can think of that is a "container"For example creating generic functions for SymbolTables then more specific for each of the Tables derived from SymbolTable,
Funny you should mention that. Since SymbolTableRecord doesn't implement IEnumerable, but its dependents are expected to, we could use this loophole.Code - vb.net: [Select]
<ExtensionAttribute()> Public Iterator Function GetObjects(Of T As DBObject)(str As SymbolTableRecord) As IEnumerable(Of T) For Each objectId As ObjectId In CType(str, IEnumerable) Dim target As T = TryCast(objectId.GetObject(OpenMode.ForRead, False, False), T) If target IsNot Nothing Then Yield target End If Next End Function
Certain people abhor passing a non-generic sequence to a functon which will produce run-time
errors when fed an argument of the wrong type, like string (IEnumerable of char), when a sequence of ObjectId is expected. Make it an overload of the specific class.