ACA 2014, WIndows 7 64, VS 2013 Express, VB.Net
The block library is coming along well and I'm at a point where one further push or assist should allow me to complete it.
At this time blocks are pulled from external drawings, shown on the cursor as it moves on the screen, rotate on the cursor prior to input, BUT insert at the original orientation. I am confused on how to pass back to InsertBlock the rotation angle of the block as shown on the cursor and have the block inserted at that orienation rather than at the original. Below are the code for my library and the jig.
Library.vb
Imports System Imports System.IO Imports System.Windows.Controls Imports Autodesk.AutoCAD Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Public Class Library Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument Dim itemstr As String Dim ed As Editor Private Sub Library_Load(sender As Object, e As EventArgs) Handles MyBase.Load For Each i As String In Directory.GetDirectories("R:\2014\Library\") ListView1.Items.Add(Path.GetFileName(i)) Next ListView1.Items.Remove(ListView1.Items(0)) If ListView1.Items.Count > 0 Then ListView1.Items(0).Focused = True ListView1.Items(0).Selected = True itemstr = (Me.ListView1.FocusedItem.Text) PictureBox1.ImageLocation = ("R:\2014\Library\" & itemstr & "\template.wmf") End If End Sub Private Sub ListView1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListView1.SelectedIndexChanged If Me.ListView1.SelectedItems.Count > 0 Then itemstr = (Me.ListView1.FocusedItem.Text) PictureBox1.ImageLocation = ("R:\2014\Library\" & itemstr & "\template.wmf") End If End Sub Private Sub PictureBox1_MouseClick(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseClick Dim xPos As Integer Dim yPos As Integer If e.Button = System.Windows.Forms.MouseButtons.Left Then xPos = e.X yPos = e.Y End If doit(xPos, yPos, itemstr) End Sub Private Sub doit(ByVal xPos As Single, ByVal yPos As Single, itemstr As String) Dim box As String Dim x As Integer Dim y As Integer Dim layer As String x = (Int(xPos / 100)) y = (Int(yPos / 100)) If y > 0 Then box = (Int((x) + (Int(y) * 8))) Else : box = x End If 'Layers Using Myreader As New Microsoft.VisualBasic.FileIO.TextFieldParser("R:\2014\Library\" & itemstr & "\layers.lst") Myreader.TextFieldType = FileIO.FieldType.Delimited Myreader.SetDelimiters(",") While Not Myreader.EndOfData Try Dim fields() As String = Myreader.ReadFields layer = (fields(box)) Finally End Try End While AddLayer(layer) End Using 'Blocks Using Myreaderblocks As New Microsoft.VisualBasic.FileIO.TextFieldParser("R:\2014\Library\" & itemstr & "\library.lst") Myreaderblocks.TextFieldType = FileIO.FieldType.Delimited Myreaderblocks.SetDelimiters(",") While Not Myreaderblocks.EndOfData Try Dim fields() As String = Myreaderblocks.ReadFields Dim currentField As String = (fields(box)) If currentField.Length > 5 Then result = currentField.Substring(currentField.Length - 3) End If Dim BlockName As String = currentField.Substring(0, currentField.Length - 6) If Not result = "" Then Select Case result Case "dwg" addblock(BlockName, currentField, itemstr) End Select End If Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException End Try End While End Using End Sub Public Sub AddLayer(layer) Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor acDoc.LockDocument() Using tr As Transaction = db.TransactionManager.StartTransaction() Dim ltb As LayerTable = DirectCast(tr.GetObject(db.LayerTableId, OpenMode.ForRead), LayerTable) 'create a new layer If Not ltb.Has(layer) Then ltb.UpgradeOpen() Dim newLayer As New LayerTableRecord() newLayer.Name = layer newLayer.LineWeight = LineWeight.LineWeight005 newLayer.Description = "This is new layer" 'red color newLayer.Color = Autodesk.AutoCAD.Colors.Color.FromRgb(255, 0, 0) ltb.Add(newLayer) tr.AddNewlyCreatedDBObject(newLayer, True) End If tr.Commit() 'make it as current db.Clayer = ltb(layer) End Using End Sub Public Sub addblock(ByVal blockname As String, ByVal currentfield As String, ByVal itemstr As String) Dim db As Database = Application.DocumentManager.MdiActiveDocument.Database Using OpenDb As New Database(False, True) OpenDb.ReadDwgFile("R:\2014\Library\" & itemstr & "\" & currentfield, System.IO.FileShare.ReadWrite, True, "") acDoc.LockDocument() Dim ids As New ObjectIdCollection() Using tr As Transaction = OpenDb.TransactionManager.StartTransaction() 'For example, Get the block by name "BlkName" Dim bt As BlockTable bt = DirectCast(tr.GetObject(OpenDb.BlockTableId, OpenMode.ForRead), BlockTable) If bt.Has(blockname) Then ids.Add(bt(blockname)) End If tr.Commit() End Using 'if not found, add the block If ids.Count <> 0 Then 'get the current drawing database Dim destdb As Database = acDoc.Database Dim iMap As New IdMapping() acDoc.LockDocument() destdb.WblockCloneObjects(ids, destdb.BlockTableId, iMap, DuplicateRecordCloning.Ignore, False) End If End Using Dispose() InsertBlockWithJig(blockname, layer) End Sub Public Sub InsertBlockWithJig(ByVal blockname As String, layer As String) Dim myDB As Database myDB = HostApplicationServices.WorkingDatabase Dim myJig As BlockJig Using myTrans As Transaction = myDB.TransactionManager.StartTransaction Dim myBT As BlockTable = myDB.BlockTableId.GetObject(OpenMode.ForRead) If myBT.Has(blockname) Then Dim myBTR As BlockTableRecord = myBT(blockname).GetObject(OpenMode.ForRead) myJig = New BlockJig(myBTR.ObjectId) Else Exit Sub End If End Using Dim myBlkID As ObjectId Dim SelPt As EditorInput.PromptPointResult Dim pdr As EditorInput.PromptPointResult Do SelPt = myJig.BeginJig If Not SelPt Is Nothing Then Select Case SelPt.Status Case EditorInput.PromptStatus.OK myBlkID = insertblock(SelPt.Value, blockname) Case EditorInput.PromptStatus.Other Exit Sub End Select End If If SelPt Is Nothing Then Exit Do Loop While SelPt.Status = EditorInput.PromptStatus.OK End Sub Public Function insertblock(BasePt As Point3d, blockname As String) Dim db As Database = Application.DocumentManager.MdiActiveDocument.Database Using myT As Transaction = db.TransactionManager.StartTransaction() 'Get the block definition "Check". Dim bt As BlockTable = TryCast(db.BlockTableId.GetObject(OpenMode.ForRead), BlockTable) Dim blockDef As BlockTableRecord = TryCast(bt(blockname).GetObject(OpenMode.ForRead), BlockTableRecord) 'Also open modelspace - we'll be adding our BlockReference to it Dim ms As BlockTableRecord = TryCast(bt(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForWrite), BlockTableRecord) 'Create new BlockReference, and link it to our block definition Using blockRef As New BlockReference(BasePt, blockDef.ObjectId) 'Add the block reference to modelspace ms.AppendEntity(blockRef) myT.AddNewlyCreatedDBObject(blockRef, True) 'Iterate block definition to find all non-constant AttributeDefinitions For Each id As ObjectId In blockDef Dim obj As DBObject = id.GetObject(OpenMode.ForRead) Dim attDef As AttributeDefinition = TryCast(obj, AttributeDefinition) If (attDef IsNot Nothing) AndAlso (Not attDef.Constant) Then 'This is a non-constant AttributeDefinition 'Create a new AttributeReference Using attRef As New AttributeReference() attRef.SetAttributeFromBlock(attDef, blockRef.BlockTransform) 'Add the AttributeReference to the BlockReference blockRef.AttributeCollection.AppendAttribute(attRef) myT.AddNewlyCreatedDBObject(attRef, True) End Using End If Next End Using acDoc.TransactionManager.QueueForGraphicsFlush() myT.Commit() End Using End Function End Class
BlockJig.vb
Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD Public Class BlockJig Inherits Autodesk.AutoCAD.EditorInput.EntityJig Dim BasePt As Point3d = New Point3d(0, 0, 0) Dim myMatrix As Matrix3d Dim myBRef As DatabaseServices.BlockReference Dim myOpts As EditorInput.JigPromptPointOptions Dim CurrentKeyword As String Protected _pos As Point3d Protected _rot As Double, _ucsRot As Double Sub New(ByVal BlockID As ObjectId) MyBase.New(New DatabaseServices.BlockReference(BlockIns, BlockID)) myBRef = Me.Entity _pos = myBRef.Position _rot = myBRef.Rotation End Sub Function BeginJig() As PromptPointResult If myOpts Is Nothing Then myOpts = New EditorInput.JigPromptPointOptions() myOpts.Message = vbCrLf & "Select a point:" myOpts.Cursor = EditorInput.CursorType.Invisible myOpts.UseBasePoint = False End If Dim ed As EditorInput.Editor = Application.DocumentManager.MdiActiveDocument.Editor Dim myPR As PromptResult Dim ucs As CoordinateSystem3d = ed.CurrentUserCoordinateSystem.CoordinateSystem3d Dim ocsMat As Matrix3d = Matrix3d.WorldToPlane(New Plane(Point3d.Origin, ucs.Zaxis)) _ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis) _rot = myBRef.Rotation - _ucsRot myPR = ed.Drag(Me) Do Select Case myPR.Status Case EditorInput.PromptStatus.OK Return myPR Exit Do Case EditorInput.PromptStatus.None Return myPR Exit Do Case EditorInput.PromptStatus.Other Return myPR Exit Do End Select Loop While myPR.Status <> EditorInput.PromptStatus.Cancel Return Nothing End Function Protected Overrides Function Sampler(prompts As JigPrompts) As SamplerStatus Dim mods As System.Windows.Forms.Keys = System.Windows.Forms.Control.ModifierKeys If (mods And System.Windows.Forms.Keys.Control) > 0 Then Dim jpao As New JigPromptAngleOptions(vbLf & "Specify the rotation: ") jpao.UseBasePoint = True jpao.BasePoint = myBRef.Position jpao.Cursor = CursorType.Invisible jpao.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.UseBasePointElevation) Dim pdr As PromptDoubleResult = prompts.AcquireAngle(jpao) If _rot = pdr.Value Then Return SamplerStatus.NoChange Else _rot = pdr.Value Return SamplerStatus.OK End If Else Dim jppo As New JigPromptPointOptions(vbLf & "Specify insertion point (or press Ctrl for rotation): ") jppo.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.NullResponseAccepted) Dim ppr As PromptPointResult = prompts.AcquirePoint(jppo) If _pos.DistanceTo(ppr.Value) < Tolerance.[Global].EqualPoint Then Return SamplerStatus.NoChange Else _pos = ppr.Value End If Return SamplerStatus.OK End If End Function Protected Overrides Function Update() As Boolean myBRef.Position = _pos myBRef.Rotation = _rot + _ucsRot Return True End Function End Class
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Hi,
I didn't read your code enough attentively.
Here's a little sample closer to my understanding of your request (the code don't care about loading the block if not existing in the block table to focus to the BlockJig).
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; namespace InsertBlockSample { public class CommandMethods { [CommandMethod("Test")] public void Test() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptResult psr = ed.GetString("\nEnter the block name: "); if (psr.Status != PromptStatus.OK) return; string blockName = psr.StringResult; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); if (bt.Has(blockName)) { using (BlockReference br = new BlockReference(Point3d.Origin, bt[blockName])) { br.TransformBy(ed.CurrentUserCoordinateSystem); BlockJig jig = new BlockJig(br); PromptResult pr = ed.Drag(jig); if (pr.Status == PromptStatus.OK) { BlockTableRecord curspace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); curspace.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); } } } else { Application.ShowAlertDialog(string.Format("Block '{0}' not found.", blockName)); } tr.Commit(); } } } class BlockJig : EntityJig { protected BlockReference _br; protected Point3d _pos; protected double _rot, _ucsRot; public BlockJig(BlockReference br) : base(br) { _br = br; _pos = br.Position; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Matrix3d ucsMat = ed.CurrentUserCoordinateSystem; CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d; Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis)); _ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis); _rot = br.Rotation - _ucsRot; } protected override SamplerStatus Sampler(JigPrompts prompts) { System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys; if ((mods & System.Windows.Forms.Keys.Control) > 0) { JigPromptAngleOptions jpao = new JigPromptAngleOptions( "\nSpecify the rotation (release Ctrl to validate): "); jpao.UseBasePoint = true; jpao.BasePoint = _br.Position; jpao.Cursor = CursorType.RubberBand; jpao.UserInputControls = ( UserInputControls.Accept3dCoordinates | UserInputControls.UseBasePointElevation); PromptDoubleResult pdr = prompts.AcquireAngle(jpao); if (_rot == pdr.Value) { return SamplerStatus.NoChange; } else { _rot = pdr.Value; return SamplerStatus.OK; } } else { JigPromptPointOptions jppo = new JigPromptPointOptions( "\nSpecify the insertion point (or press Ctrl for rotation): "); jppo.UserInputControls = (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted); PromptPointResult ppr = prompts.AcquirePoint(jppo); if (_pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) { return SamplerStatus.NoChange; } else { _pos = ppr.Value; } return SamplerStatus.OK; } } protected override bool Update() { _br.Position = _pos; _br.Rotation = _rot + _ucsRot; return true; } } }
One step further.
If the block have attributes and you want to diplay them during the jig dragging, you may add the block reference and the attribute references before dragging and erase it if ed.Drag(jig) does not return PromptStatus.OK
I use another class (BlockAttribJig) which inherits from BlockJig and only cares of updating the attributes geometry.
I also use a little structure (AttInfo) to store the text informations of the attribute definitions.
using System.Collections.Generic; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; namespace InsertBlockSample { public class CommandMethods { [CommandMethod("Test")] public void Test() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptResult psr = ed.GetString("\nEnter the block name: "); if (psr.Status != PromptStatus.OK) return; string blockName = psr.StringResult; InsertBlock(blockName); } private void InsertBlock(string blockName) { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); if (bt.Has(blockName)) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead); using (BlockReference br = new BlockReference(Point3d.Origin, bt[blockName])) { br.TransformBy(ed.CurrentUserCoordinateSystem); BlockTableRecord curspace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); curspace.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); BlockJig jig; if (btr.HasAttributeDefinitions) { Dictionary<string, TextInfo> attInfos = new Dictionary<string, TextInfo>(); RXClass attribClass = RXClass.GetClass(typeof(AttributeDefinition)); foreach (ObjectId id in btr) { if (id.ObjectClass == attribClass) { AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead); AttributeReference attRef = new AttributeReference(); attRef.SetAttributeFromBlock(attDef, br.BlockTransform); br.AttributeCollection.AppendAttribute(attRef); tr.AddNewlyCreatedDBObject(attRef, true); attInfos.Add(attDef.Tag, new TextInfo(attDef)); } } jig = new BlockAttribJig(br, attInfos); } else { jig = new BlockJig(br); } PromptResult pr = ed.Drag(jig); if (pr.Status != PromptStatus.OK) { br.Erase(); } } } else { Application.ShowAlertDialog(string.Format("Block '{0}' not found.", blockName)); } tr.Commit(); } } } class BlockJig : EntityJig { protected BlockReference br; protected Point3d pos; protected double ucsRot, rot; public BlockJig(BlockReference br) : base(br) { this.br = br; pos = br.Position; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Matrix3d ucsMat = ed.CurrentUserCoordinateSystem; CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d; Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis)); ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis); rot = br.Rotation - ucsRot; } protected override SamplerStatus Sampler(JigPrompts prompts) { System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys; if ((mods & System.Windows.Forms.Keys.Control) > 0) { JigPromptAngleOptions jpao = new JigPromptAngleOptions( "\nSpecify the rotation (release Ctrl to validate): "); jpao.UseBasePoint = true; jpao.BasePoint = br.Position; jpao.Cursor = CursorType.RubberBand; jpao.UserInputControls = ( UserInputControls.Accept3dCoordinates | UserInputControls.UseBasePointElevation); PromptDoubleResult pdr = prompts.AcquireAngle(jpao); if (rot == pdr.Value) { return SamplerStatus.NoChange; } else { rot = pdr.Value; return SamplerStatus.OK; } } else { JigPromptPointOptions jppo = new JigPromptPointOptions( "\nSpecify the insertion point (or press Ctrl for rotation): "); jppo.UserInputControls = (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted); PromptPointResult ppr = prompts.AcquirePoint(jppo); if (pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) { return SamplerStatus.NoChange; } else { pos = ppr.Value; } return SamplerStatus.OK; } } protected override bool Update() { br.Position = pos; br.Rotation = rot + ucsRot; return true; } } class BlockAttribJig : BlockJig { private Dictionary<string, TextInfo> attRefInfos; private Transaction tr; public BlockAttribJig(BlockReference br, Dictionary<string, TextInfo> attInfos) : base(br) { attRefInfos = attInfos; tr = br.Database.TransactionManager.TopTransaction; } protected override bool Update() { base.Update(); foreach (ObjectId id in br.AttributeCollection) { AttributeReference att = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite); string tag = att.Tag.ToUpper(); if (attRefInfos.ContainsKey(tag)) { TextInfo ti = attRefInfos[tag]; att.Position = ti.Position.TransformBy(br.BlockTransform); att.Rotation = ti.Rotation + br.Rotation; if (ti.IsAligned) { att.AlignmentPoint = ti.Alignment.TransformBy(br.BlockTransform); att.AdjustAlignment(br.Database); } if (att.IsMTextAttribute) { att.UpdateMTextAttribute(); } } } return true; } } struct TextInfo { public readonly Point3d Position, Alignment; public readonly bool IsAligned; public readonly double Rotation; public TextInfo(DBText text) { this.Position = text.Position; this.Alignment = text.AlignmentPoint; this.IsAligned = text.Justify != AttachmentPoint.BaseLeft; this.Rotation = text.Rotation; } } }
One more step further.
Using a class which implements the System.Windows.Forms.IMessageFilter interface allows to catch some key down during the jig drag and perform some changes to the block rotation.
In the following sample, it's used to add 90° to the block current rotation each time the user hits the Tab key.
using System; using System.Collections.Generic; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; namespace InsertBlockSample { public class CommandMethods { [CommandMethod("Test")] public void Test() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptResult psr = ed.GetString("\nEnter the block name: "); if (psr.Status != PromptStatus.OK) return; string blockName = psr.StringResult; InsertBlock(blockName); } private void InsertBlock(string blockName) { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); if (bt.Has(blockName)) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead); using (BlockReference br = new BlockReference(Point3d.Origin, bt[blockName])) { br.TransformBy(ed.CurrentUserCoordinateSystem); BlockTableRecord curspace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); curspace.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); BlockJig jig; if (btr.HasAttributeDefinitions) { Dictionary<string, TextInfo> attInfos = new Dictionary<string, TextInfo>(); RXClass attribClass = RXClass.GetClass(typeof(AttributeDefinition)); foreach (ObjectId id in btr) { if (id.ObjectClass == attribClass) { AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead); AttributeReference attRef = new AttributeReference(); attRef.SetAttributeFromBlock(attDef, br.BlockTransform); br.AttributeCollection.AppendAttribute(attRef); tr.AddNewlyCreatedDBObject(attRef, true); attInfos.Add(attDef.Tag, new TextInfo(attDef)); } } jig = new BlockAttribJig(br, attInfos); } else { jig = new BlockJig(br); } TabKeyDownFilter tabFilter = new TabKeyDownFilter(jig); System.Windows.Forms.Application.AddMessageFilter(tabFilter); PromptResult pr = ed.Drag(jig); if (pr.Status != PromptStatus.OK) { br.Erase(); } System.Windows.Forms.Application.RemoveMessageFilter(tabFilter); } } else { Application.ShowAlertDialog(string.Format("Block '{0}' not found.", blockName)); } tr.Commit(); } } } class BlockJig : EntityJig { protected BlockReference br; protected Point3d pos; protected double ucsRot; protected internal double Rotation { get; set; } public BlockJig(BlockReference br) : base(br) { this.br = br; pos = br.Position; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Matrix3d ucsMat = ed.CurrentUserCoordinateSystem; CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d; Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis)); ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis); this.Rotation = br.Rotation - ucsRot; } protected override SamplerStatus Sampler(JigPrompts prompts) { System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys; if ((mods & System.Windows.Forms.Keys.Control) > 0) { JigPromptAngleOptions jpao = new JigPromptAngleOptions( "\nSpecify the rotation (release Ctrl to validate): "); jpao.UseBasePoint = true; jpao.BasePoint = br.Position; jpao.Cursor = CursorType.RubberBand; jpao.UserInputControls = ( UserInputControls.Accept3dCoordinates | UserInputControls.UseBasePointElevation); PromptDoubleResult pdr = prompts.AcquireAngle(jpao); if (this.Rotation == pdr.Value) { return SamplerStatus.NoChange; } else { this.Rotation = pdr.Value; return SamplerStatus.OK; } } else { JigPromptPointOptions jppo = new JigPromptPointOptions( "\nSpecify the insertion point (or press Ctrl for rotation): "); jppo.UserInputControls = (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted); PromptPointResult ppr = prompts.AcquirePoint(jppo); if (pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) { return SamplerStatus.NoChange; } else { pos = ppr.Value; } return SamplerStatus.OK; } } protected override bool Update() { br.Position = pos; UpdateRotation(); return true; } internal void UpdateRotation() { br.Rotation = this.Rotation + ucsRot; } } class BlockAttribJig : BlockJig { private Dictionary<string, TextInfo> attRefInfos; private Transaction tr; public BlockAttribJig(BlockReference br, Dictionary<string, TextInfo> attInfos) : base(br) { attRefInfos = attInfos; tr = br.Database.TransactionManager.TopTransaction; } protected override bool Update() { base.Update(); foreach (ObjectId id in br.AttributeCollection) { AttributeReference att = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite); string tag = att.Tag.ToUpper(); if (attRefInfos.ContainsKey(tag)) { TextInfo ti = attRefInfos[tag]; att.Position = ti.Position.TransformBy(br.BlockTransform); att.Rotation = ti.Rotation + br.Rotation; if (ti.IsAligned) { att.AlignmentPoint = ti.Alignment.TransformBy(br.BlockTransform); att.AdjustAlignment(br.Database); } if (att.IsMTextAttribute) { att.UpdateMTextAttribute(); } } } return true; } } struct TextInfo { public readonly Point3d Position, Alignment; public readonly bool IsAligned; public readonly double Rotation; public TextInfo(DBText text) { this.Position = text.Position; this.Alignment = text.AlignmentPoint; this.IsAligned = text.Justify != AttachmentPoint.BaseLeft; this.Rotation = text.Rotation; } } class TabKeyDownFilter : System.Windows.Forms.IMessageFilter { private const int WM_KEYDOWN = 0x100; private BlockJig jig; public TabKeyDownFilter(BlockJig jig) { this.jig = jig; } public bool PreFilterMessage(ref System.Windows.Forms.Message m) { if (m.Msg == WM_KEYDOWN && (int)m.WParam == 9) { jig.Rotation += Math.PI / 2.0; jig.UpdateRotation(); } return false; } } }
And now I know why you are an "Expert Elite". Many thanks for this. I'm going to try to get back the modeless library window and multiple insertion on my own. Had it previously so I don't think that will be difficult. Again, if I could upgrade your rating I would.
At this time I am unable to visually see the attributes in the blocks during or after insertion. I've gone back to a point where I had that working but am so far I have proven inadequate to integrating it into the block insertion with rotate with attInfos. The attributes are there in the block, the blocks haven't changed in construction, I can attedit the attributes, but I can't see them. Any ideas?
If I just insert the block in the attributes are fully visible and operate as desired. Insert through the code you and I have been discussing, even with the block previously inserted and still visible doesn't show attributes even though they are editable through attedit.
what I want to say is that you have to add the new block reference and its attributes to the transaction in the code before dragging it (and erase it if Prompt Status is not OK).
Try this VB code, it works for me.
Imports System.Collections.Generic Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.Runtime Namespace InsertBlockSample Public Class CommandMethods <CommandMethod("Test")> _ Public Sub Test() Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor Dim psr As PromptResult = ed.GetString(vbLf & "Enter the block name: ") If psr.Status <> PromptStatus.OK Then Return End If Dim blockName As String = psr.StringResult InsertBlock(blockName) End Sub Private Sub InsertBlock(blockName As String) Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using tr As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) If bt.Has(blockName) Then ' Insert the block at (0, 0, 0) Dim br As New BlockReference(Point3d.Origin, bt(blockName)) br.TransformBy(ed.CurrentUserCoordinateSystem) Dim curspace As BlockTableRecord = DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord) curspace.AppendEntity(br) tr.AddNewlyCreatedDBObject(br, True) Dim btr As BlockTableRecord = DirectCast(tr.GetObject(bt(blockName), OpenMode.ForRead), BlockTableRecord) Dim jig As BlockJig If btr.HasAttributeDefinitions Then ' Add the attributes and get their TextInfo Dim attInfos As New Dictionary(Of String, TextInfo)() Dim attribClass As RXClass = RXClass.GetClass(GetType(AttributeDefinition)) For Each id As ObjectId In btr If id.ObjectClass = attribClass Then Dim attDef As AttributeDefinition = DirectCast(tr.GetObject(id, OpenMode.ForRead), AttributeDefinition) Dim attRef As New AttributeReference() attRef.SetAttributeFromBlock(attDef, br.BlockTransform) br.AttributeCollection.AppendAttribute(attRef) tr.AddNewlyCreatedDBObject(attRef, True) attInfos.Add(attDef.Tag, New TextInfo(attDef)) End If Next jig = New BlockAttribJig(br, attInfos) Else jig = New BlockJig(br) End If ' start dragging the block Dim pr As PromptResult = jig.Drag() If pr.Status <> PromptStatus.OK Then br.[Erase]() End If Else Application.ShowAlertDialog(String.Format("Block '{0}' not found.", blockName)) End If tr.Commit() End Using End Sub End Class Class BlockJig Inherits EntityJig Protected br As BlockReference Protected pos As Point3d Protected rot As Double, ucsRot As Double Private ed As Editor Protected Friend Property Rotation() As Double Get Return rot End Get Set(value As Double) rot = value Update() End Set End Property Public Sub New(br As BlockReference) MyBase.New(br) Me.br = br pos = br.Position ed = Application.DocumentManager.MdiActiveDocument.Editor Dim ucsMat As Matrix3d = ed.CurrentUserCoordinateSystem Dim ucs As CoordinateSystem3d = ucsMat.CoordinateSystem3d Dim ocsMat As Matrix3d = Matrix3d.WorldToPlane(New Plane(Point3d.Origin, ucs.Zaxis)) ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis) rot = br.Rotation - ucsRot End Sub Protected Overrides Function Sampler(prompts As JigPrompts) As SamplerStatus Dim mods As System.Windows.Forms.Keys = System.Windows.Forms.Control.ModifierKeys If (mods And System.Windows.Forms.Keys.Control) > 0 Then Dim jpao As New JigPromptAngleOptions(vbLf & "Specify the rotation (release Ctrl to validate): ") jpao.UseBasePoint = True jpao.BasePoint = br.Position jpao.Cursor = CursorType.RubberBand jpao.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.UseBasePointElevation) Dim pdr As PromptDoubleResult = prompts.AcquireAngle(jpao) If rot = pdr.Value Then Return SamplerStatus.NoChange Else rot = pdr.Value Return SamplerStatus.OK End If Else Dim jppo As New JigPromptPointOptions(vbLf & "Specify the insertion point (or press Ctrl for rotation): ") jppo.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.NullResponseAccepted) Dim ppr As PromptPointResult = prompts.AcquirePoint(jppo) If pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint Then Return SamplerStatus.NoChange Else pos = ppr.Value End If Return SamplerStatus.OK End If End Function Protected Overrides Function Update() As Boolean br.Position = pos br.Rotation = rot + ucsRot Return True End Function Friend Function Drag() As PromptResult Dim tabFilter As New TabKeyDownFilter(Me) System.Windows.Forms.Application.AddMessageFilter(tabFilter) Dim pr As PromptResult = ed.Drag(Me) System.Windows.Forms.Application.RemoveMessageFilter(tabFilter) Return pr End Function End Class Class BlockAttribJig Inherits BlockJig Private attRefInfos As Dictionary(Of String, TextInfo) Private tr As Transaction Public Sub New(br As BlockReference, attInfos As Dictionary(Of String, TextInfo)) MyBase.New(br) attRefInfos = attInfos tr = br.Database.TransactionManager.TopTransaction End Sub Protected Overrides Function Update() As Boolean MyBase.Update() For Each id As ObjectId In br.AttributeCollection Dim att As AttributeReference = DirectCast(tr.GetObject(id, OpenMode.ForWrite), AttributeReference) Dim tag As String = att.Tag.ToUpper() If attRefInfos.ContainsKey(tag) Then Dim ti As TextInfo = attRefInfos(tag) att.Position = ti.Position.TransformBy(br.BlockTransform) att.Rotation = ti.Rotation + br.Rotation If ti.IsAligned Then att.AlignmentPoint = ti.Alignment.TransformBy(br.BlockTransform) att.AdjustAlignment(br.Database) End If If att.IsMTextAttribute Then att.UpdateMTextAttribute() End If End If Next Return True End Function End Class Structure TextInfo Public ReadOnly Position As Point3d, Alignment As Point3d Public ReadOnly IsAligned As Boolean Public ReadOnly Rotation As Double Public Sub New(text As DBText) Me.Position = text.Position Me.Alignment = text.AlignmentPoint Me.IsAligned = text.Justify <> AttachmentPoint.BaseLeft Me.Rotation = text.Rotation End Sub End Structure Class TabKeyDownFilter Implements System.Windows.Forms.IMessageFilter Private Const WM_KEYDOWN As Integer = &H100 Private Const VK_TAB As Integer = &H9 Private jig As BlockJig Public Sub New(jig As BlockJig) Me.jig = jig End Sub Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean _ Implements System.Windows.Forms.IMessageFilter.PreFilterMessage If m.Msg = WM_KEYDOWN AndAlso CInt(m.WParam) = VK_TAB Then jig.Rotation += Math.PI * 0.5 End If Return False End Function End Class End Namespace
And now we come to the point where I explain that it was my fault that the attributes weren't showing up. Years ago I had written a module in VBA that allowed me to insert attributes with spaces in the tag and lower case letters where I wanted them. I've used this to create many blocks in our library with attributes. Those blocks with attributes made this way are the ones that the attributes don't visibly show when the block is inserted using the code I'm writing now. Eventually I'm going to try to figure out exactly why but for now I can recreate any attributes that need to be and have that done by tomorrow afternoon (estimate).
For giggles below is the VBA code I use to put the attributes in:
Sub Add_Attribute() Dim blockObj As AcadBlock Dim insertionPnt(0 To 2) As Double insertionPnt(0) = 0 insertionPnt(1) = 0 insertionPnt(2) = 0 Set blockObj = ThisDrawing.Blocks.Add _ (insertionPnt, "Attribute") Dim attributeObj As AcadAttribute Dim height As Double Dim mode As Long Dim prompt As String Dim insertionPoint(0 To 2) As Double Dim tag As String Dim value As String height = 1 mode = acAttributeModePreset prompt = "Head Detail #:" insertionPoint(0) = 0 insertionPoint(1) = 0 insertionPoint(2) = 0 tag = "Head Detail #:" value = "X" Set attributeObj = blockObj.AddAttribute(height, mode, _ prompt, insertionPoint, tag, value) Dim blockRefObj As AcadBlockReference insertionPnt(0) = 2 insertionPnt(1) = 2 insertionPnt(2) = 0 Set blockRefObj = ThisDrawing.ModelSpace.InsertBlock _ (insertionPnt, "Attribute", 1#, 1#, 1#, 0) attributeObj.Backward = False attributeObj.LockPosition = True attributeObj.Alignment = acAlignmentMiddleCenter attributeObj.Update End Sub