SelectCrossingWindow only selects objects currently visible on the screen.
Is this expected behavior? I would prefer not to be required to zoom extents before running the command.
Thanks,
Jeff
Detroit, MI
Solved! Go to Solution.
Solved by mcicognani. Go to Solution.
Hi,
Yes, it's the expected behavior (no matter if is crossing or window, fence, etc) , it's the same in Autolisp, so you have to zoom out enough to fit the objects on the screen.
Gaston Nunez
Then is there a better method for selecting objects?
How is it that creating a selection set on a closed drawing works?
Just wondering if I'm barking up the wrong tree...
Thanks,
jeff
You can use filters using dxf properties codes. This works on database, thus selecting also objects not visible.
Something like this:
TypedValue[] tvs1 = new TypedValue[4] { new TypedValue((int)DxfCode.Operator, "<and"), new TypedValue((int)DxfCode.Start, "INSERT"), new TypedValue((int)DxfCode.LayerName, szLayerCell), new TypedValue((int)DxfCode.Operator, "and>") }
PromptSelectionResult psr = doc.Editor.SelectAll(new SelectionFilter(tvsd));
if (psr.Status == PromptStatus.OK && psr.Value.Count > 0)
{
}
I used those in the beginning, thinking that an internal function was the fastest way to filter a database, but I soon discovered that traversing a drawing in .NET is quite fast, so recently I prefer to traverse the database by myself and filter whatever entities I need.
Something like this:
// get block table, here you'll find *modelspace, *paperspace and block definitions
BlockTable bt = (BlockTable)tr.GetObject(dwg.BlockTableId, OpenMode.ForRead); foreach (ObjectId btrId in bt) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
// here you may want to filter which space you need
if (!btr.Name.StartsWith("*")) continue;
foreach (ObjectId o in btr) { DBObject dbo = tr.GetObject(o, OpenMode.ForRead, true); if (!dbo.IsErased && !dbo.IsDisposed && (dbo is Entity)) { Entity ent = dbo as Entity;
// valid entity, check whatever you need if (ent.LayerId == idLayerSource) {
// open entity for change and do whatever you need ent.UpgradeOpen();
} } } }
I am currently using the selection method with dxf filters and it does not select objects off screen. I was hoping maybe I was missing a flag or option somewhere.
I may have to switch over to looping through the db as well.
Thanks everyone for the feedback,
Jeff
You said you were using SelectCrossWindow(), in the example reported I used SelectAll().
In my experience, SelectAll() works on the db directly and allows to select entities not visible on screen, even on hidden layers.
I finally had time to test this out and you are correct SelectAll() finctions differently than SelectCrossingWindow(). Thank you, I beleive this puts me on a better path.
With this new direction would you recommend I also abandon selection filters? I was already having issues filtering for block names when the blocks were dynamic anyway.
If I'm already stepping through each BTR looking for criteria I could just filter there.
Thanks,
Jeff
Given that you're looking for blocks and the database provide a neat BlockTable to traverse, I'd suggest you'd go through this path, it's also a lot faster than filtering or traversing the whole database.
From BlockTable is also easy to collect the BlockReference instances, both for standard and dynamic blocks, so it seems to have lot of advantages and no downsides.
You may want to traverse the db starting from ModelSpace/Paperspace if you need some hierarchy information, like blocks inside blocks or inside xrefs. In this case you may want a recursive function looking for blockreferences and step in for blocktablerecords.
I have plenty of this C# working code, let me know if you need something like this.
I've tried two methods. The first was to look through the entire database for blockreferences then verify their names. Then I modified it to this:
Using myTrans As Transaction = DbIn.TransactionManager.StartTransaction Dim thisSpaceBTR As BlockTableRecord = DbIn.CurrentSpaceId.GetObject(OpenMode.ForRead) For Each thisOID As ObjectId In thisSpaceBTR Dim thisEnt As Entity = thisOID.GetObject(OpenMode.ForRead) If TypeOf thisEnt Is BlockReference Then Dim thisBlockRef As BlockReference = thisOID.GetObject(OpenMode.ForRead) Dim thisBTR As BlockTableRecord = thisBlockRef.BlockTableRecord.GetObject(OpenMode.ForRead) Dim blockName As String = Tools.Blocks.GetBlockName(thisBTR) If blockName.StartsWith("Anno-Keyn-Hedr-") Or blockName.StartsWith("Anno-Keyn-Rows-") Or blockName = "Anno-Keyn-Endr" Then thisBlockRef.UpgradeOpen() thisBlockRef.Erase() End If End If Next myTrans.Commit() End Using
It only bothers with the current space. I'd imagine this is more efficient since my current need is only for the current space. The GetBlockName sub simply gets the effective name so that I can include dynamic blocks.
I originally tried to erase the BTR, but I kept receiving an exception error. Instead I erase the blockreference and it seems to work fine.
My next steps are going to be to step into blocks and xrefs so I could use your help.
Thank you,
Jeff
You can erase the BlockTableRecord only if there is no BlockReference, quite understandable, I'd say...
If you want to recurse inside blocks or XREFs, keep in mind that a block definition and the current space (ModelSpace or PaperSpace) are just of the same type: BlockTableRecord. So you may create a recursive function that accept a BlocktableRecord to analyze, the first call would be made passing the current space and check every entity like you already do. When you find a BlockReference you can get the linked BlockTableRecord and you may call the same function recursively.
Something like this (sorry, just C#):
private void ParseBlocksDb(Database db, String szFormat, System.Data.DataTable dt, int iRound) { Transaction tr = db.TransactionManager.StartTransaction(); try { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); String szFilename = Path.GetFileNameWithoutExtension(db.Filename); // browse modelspace BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); _ParseBlocksBtr(ms, tr, szFormat, dt, iRound, szFilename); // browse paperspace ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.PaperSpace], OpenMode.ForRead); _ParseBlocksBtr(ms, tr, szFormat, dt, iRound, szFilename); } catch (System.Exception ex) { MessageBox.Show("Unexpected error: " + ex.Message); } finally { tr.Dispose(); } } private void _ParseBlocksBtr(BlockTableRecord ms, Transaction tr) { int p; BlockReference br; BlockTableRecord btr; foreach (ObjectId o in ms) { DBObject dbo = tr.GetObject(o, OpenMode.ForRead, false); if ((dbo is BlockReference) && (!dbo.IsErased)) { br = dbo as BlockReference; // retrieve blockdefinition, regardless if standard or dynamic btr = (BlockTableRecord)tr.GetObject(br.IsDynamicBlock ? br.DynamicBlockTableRecord : br.BlockTableRecord, OpenMode.ForRead);
// even XRefs have a BlockTableRecord, you can check the property btr.IsFromExternalReference || btr.IsFromOverlayReference
// recurse inside block/xref, if needed
_ParseBlocksBtr(btr, tr);
// do whatever you want with current reference
}
}
}
I have re-written the remainder of my code to completely abandon the concept of selection sets and I am relying solely on stepping through the BTRs. I have also adopted the idea of passing along the transaction to use only one per command - very efficient. I have considered trying this in the past, but your example showed me it is possible. Lastly, I am using bounds of blockrefs to establich location - giving me the ability to concentrate on a particular area defined like a window.
It is going well, with one exception. It ignores xrefs. No errors, just seems to disregard them. Here is where I am at:
VB.NET
Sub DeepDive(ByVal TransIn As Transaction, ByVal BTRin As BlockTableRecord, Optional ByVal windowPt1 As Point3d = Nothing, Optional ByVal windowPt2 As Point3d = Nothing) For Each thisOID As ObjectId In BTRin Dim thisEnt As Entity = thisOID.GetObject(OpenMode.ForRead) If TypeOf thisEnt Is BlockReference Then Dim thisBlockRef As BlockReference = DirectCast(TransIn.GetObject(thisOID, OpenMode.ForRead), BlockReference) Dim thisBTR As BlockTableRecord = DirectCast(TransIn.GetObject(thisBlockRef.BlockTableRecord, OpenMode.ForRead), BlockTableRecord) 'If a window is provided make sure the block is within it... If windowPt1 <> Nothing And windowPt2 <> Nothing Then If Tools.IsWithin(thisBlockRef.Bounds.Value.MinPoint, windowPt1, windowPt2) And Tools.IsWithin(thisBlockRef.Bounds.Value.MaxPoint, windowPt1, windowPt2) Then 'Check for KN data gatherKNdata(thisBlockRef, thisBTR) 'Then dive inside to look for more blockrefs DeepDive(TransIn, thisBTR) End If Else 'If no window is provided everything is allowed... 'Check for KN data gatherKNdata(thisBlockRef, thisBTR) 'Then dive inside to look for more blockrefs DeepDive(TransIn, thisBTR) End If ElseIf TypeOf thisEnt Is Viewport Then Dim myVP As Viewport = DirectCast(TransIn.GetObject(thisOID, OpenMode.ForRead), Viewport) 'If a window is provided make sure the block is within it... If windowPt1 <> Nothing And windowPt2 <> Nothing Then 'If any of the viewports four corners are within the drawing area, include it If Tools.IsWithin(myVP.Bounds.Value.MinPoint, windowPt1, windowPt2) Or _ Tools.IsWithin(myVP.Bounds.Value.MaxPoint, windowPt1, windowPt2) Or _ Tools.IsWithin(New Point3d(myVP.Bounds.Value.MinPoint.X, myVP.Bounds.Value.MaxPoint.Y, 0), windowPt1, windowPt2) Or _ Tools.IsWithin(New Point3d(myVP.Bounds.Value.MaxPoint.X, myVP.Bounds.Value.MinPoint.Y, 0), windowPt1, windowPt2) Then Dim scaleFactor As Double = myVP.ViewHeight / myVP.Height Dim myVPviewLL As New Point3d(myVP.ViewCenter.X - ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y - (myVP.ViewHeight / 2), 0) Dim myVPviewUR As New Point3d(myVP.ViewCenter.X + ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y + (myVP.ViewHeight / 2), 0) Dim myVPviewUL As New Point3d(myVP.ViewCenter.X - ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y + (myVP.ViewHeight / 2), 0) Dim myVPviewLR As New Point3d(myVP.ViewCenter.X + ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y - (myVP.ViewHeight / 2), 0) Dim myBT As BlockTable = DirectCast(TransIn.GetObject(HostApplicationServices.WorkingDatabase.BlockTableId, OpenMode.ForRead), BlockTable) Dim msBTR As BlockTableRecord = DirectCast(TransIn.GetObject(myBT(BlockTableRecord.ModelSpace), OpenMode.ForRead), BlockTableRecord) DeepDive(TransIn, msBTR, myVPviewLL, myVPviewUR) End If End If End If Next End Sub
C#
private void DeepDive(Transaction TransIn, BlockTableRecord BTRin, Point3d windowPt1 = null, Point3d windowPt2 = null) { foreach (ObjectId thisOID in BTRin) { Entity thisEnt = thisOID.GetObject(OpenMode.ForRead); if (thisEnt is BlockReference) { BlockReference thisBlockRef = (BlockReference)TransIn.GetObject(thisOID, OpenMode.ForRead); BlockTableRecord thisBTR = (BlockTableRecord)TransIn.GetObject(thisBlockRef.BlockTableRecord, OpenMode.ForRead); //If a window is provided make sure the block is within it... if (windowPt1 != null & windowPt2 != null) { if (Tools.IsWithin(thisBlockRef.Bounds.Value.MinPoint, windowPt1, windowPt2) & Tools.IsWithin(thisBlockRef.Bounds.Value.MaxPoint, windowPt1, windowPt2)) { //Check for KN data gatherKNdata(thisBlockRef, thisBTR); //Then dive inside to look for more blockrefs DeepDive(TransIn, thisBTR); } } else { //If no window is provided everything is allowed... //Check for KN data gatherKNdata(thisBlockRef, thisBTR); //Then dive inside to look for more blockrefs DeepDive(TransIn, thisBTR); } } else if (thisEnt is Viewport) { Viewport myVP = (Viewport)TransIn.GetObject(thisOID, OpenMode.ForRead); //If a window is provided make sure the block is within it... if (windowPt1 != null & windowPt2 != null) { //If any of the viewports four corners are within the drawing area, include it if (Tools.IsWithin(myVP.Bounds.Value.MinPoint, windowPt1, windowPt2) | Tools.IsWithin(myVP.Bounds.Value.MaxPoint, windowPt1, windowPt2) | Tools.IsWithin(new Point3d(myVP.Bounds.Value.MinPoint.X, myVP.Bounds.Value.MaxPoint.Y, 0), windowPt1, windowPt2) | Tools.IsWithin(new Point3d(myVP.Bounds.Value.MaxPoint.X, myVP.Bounds.Value.MinPoint.Y, 0), windowPt1, windowPt2)) { double scaleFactor = myVP.ViewHeight / myVP.Height; Point3d myVPviewLL = new Point3d(myVP.ViewCenter.X - ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y - (myVP.ViewHeight / 2), 0); Point3d myVPviewUR = new Point3d(myVP.ViewCenter.X + ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y + (myVP.ViewHeight / 2), 0); Point3d myVPviewUL = new Point3d(myVP.ViewCenter.X - ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y + (myVP.ViewHeight / 2), 0); Point3d myVPviewLR = new Point3d(myVP.ViewCenter.X + ((myVP.Width * scaleFactor) / 2), myVP.ViewCenter.Y - (myVP.ViewHeight / 2), 0); BlockTable myBT = (BlockTable)TransIn.GetObject(HostApplicationServices.WorkingDatabase.BlockTableId, OpenMode.ForRead); BlockTableRecord msBTR = (BlockTableRecord)TransIn.GetObject(myBT(BlockTableRecord.ModelSpace), OpenMode.ForRead); DeepDive(TransIn, msBTR, myVPviewLL, myVPviewUR); } } } } }
Your example leads me to believe that you can step into an xref just like a block - even though it is technically another DB. Am I approaching this incorrectly?
Thanks,
Jeff
I figured it out! It really is recognizing xrefs. It was my own windowing that was skipping it.
Thank you mcicognani for all your help.