TheSwamp
Code Red => .NET => Topic started by: wannabe on July 04, 2009, 02:41:53 PM
-
Hi Everyone,
I'm trying to access blocks from other drawings for manipulation purposes, but I only want those that reside in paper space. Initially I looked at the DXFCodes, but I couldn't see one that was applicable for a typedValue filter. I then checked out PromptSelectionOptions() which only allows me to get all blocks from the active space. If the drawing isn't actually open, would paper space be the active space (I'm copying drawings into Database references via the ReadDWG() method to make global changes)?
Are there any DXF codes that can do this to save me from checking each selected block against the ObjectId for Model Space, which seems a "dirty" way of operating. The layoutName doesn't seem robust enough.
EDIT: Actually, I think the LayoutName DXF Code will be fine. I'll post up if I get any reported problems.
As always, comments are appreciated.
-
When I tried it on the host drawing it worked. When I copied a selection of drawings into a Database reference it failed to recognise any blocks not in paper space with the filtering criteria in the post above.
Any suggestions?
-
Hi,
Did you try (67, 1) ?
Another way should be iterating through each layout, and in each layout block table reference, through each object looking for a block references.
-
Yeah, I just realised that using a selection filter won't work when the Editor object is only going to work on open drawings.
So now it's a case of scanning each layout as you suggest. Not a problem, but could be cleaner.
-
Here's a quick and dirty.
Let me know if there's a cleaner way (I'm newby and learning...)
public ArrayList GetBlockRefInLayouts()
{
Database db = Application.DocumentManager.MdiActiveDocument.Database;
ArrayList result = new ArrayList();
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary NOD = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary layouts = (DBDictionary)tr.GetObject(NOD.GetAt("ACAD_LAYOUT"), OpenMode.ForRead);
foreach (DBDictionaryEntry entry in layouts)
{
if (entry.Key != "Model")
{
Layout lay = (Layout)tr.GetObject(entry.Value, OpenMode.ForRead);
BlockTableRecord layoutBtr = (BlockTableRecord)tr.GetObject(lay.BlockTableRecordId, OpenMode.ForRead);
foreach (ObjectId id in layoutBtr)
{
BlockReference blk = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (blk != null)
result.Add(blk);
}
}
}
tr.Commit();
}
return result;
}
-
Thats basically what I did, with slight differences that provide the same result.
Cheers for posting.
-
gile,
thats essentially how I'd do it as well.
except there is no necessity to tr.Commit() because you are not revising the database , so there is nothing to commit.
... at least that's my understanding
:)
/// kdub
-
If you don't commit, then the transaction aborts, and a commit has less overhead than an abort. At least, according to some of Kean's blog posts. It might be an interesting thing to test.
-
Richard, yes, appears I was incorrect ..
I found that reference
http://through-the-interface.typepad.com/through_the_interface/2007/04/iterating_throu.html
There's a DevNote on the ADN site covering this topic:
http://adn.autodesk.com/adn/servlet/devnote?siteID=4814862&id=5546971&linkID=4900509
Here's an excerpt:
>>>
... there is a huge performance difference between Commit() and Abort(). So, when opening objects for read, you definitely want to explicitly call Commit() even though you didn't really change anything. As evidenced by a few test functions, Abort() should only be used under "duress" because of the performance hit.
<<<
A read-only transaction (even one that opens objects for write, but doesn't modify them) will not set the database modified flag (DBMOD) when committed. It's the individual "set" methods that call assertWriteEnabled(), forcing DBMOD to non-zero.
-
Thanks all,
I was lucky if the 'commit' way is the right way.
The commit/dispose/abort stuff keeps difficult to undersand for me.
-
I thought I was told by Tony T. that you didn't want to return an object, but rather an ObjectId. I'm talking about this section of gile's code
foreach (ObjectId id in layoutBtr)
{
BlockReference blk = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (blk != null)
result.Add(blk);
}
I would even think that one would want to return the handle, as the ObjectId is created upon opening the database ( drawing ), and would not be the same the next time you open the drawing, but the handle always would be, so the handle and the drawing name would be the best way to go.
That was my understanding at least. If I'm wrong, carry on. :-)
-
I thought I was told by Tony T. that you didn't want to return an object, but rather an ObjectId. I'm talking about this section of gile's code
Yeah, good catch. That piece of code is creating a transaction and getting objects, then trying to return the objects after closing the transaction. That's the no-no.
You can return objects in a method, but the transaction should still be active when you do so.
I'm not sure why you would want the handle... It depends on your code, and what it does. But for most purposes, the ObjectId is preferable.
-
and also, it will be much easier to return an ObjectIdCollection, since you can just call:
result layoutBtr.GetBlockReferenceIds();
maybe?
-
Thanks for the confirmation Sinc.
Luis,
That would just get all the inserts of that specific block definition, not all the blocks that are inserted ( nested ) within the definition.
-
So, if I understand, it should have been :
foreach (ObjectId id in layoutBtr)
{
BlockReference blk = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (blk != null)
result.Add(id); // or result.Add(id.Handle);
}
Or is there another way to evaluate if the object is a BlockReference ?
-
Thanks for the confirmation Sinc.
Luis,
That would just get all the inserts of that specific block definition, not all the blocks that are inserted ( nested ) within the definition.
dang!.... sorry
lately I'm way out of my miserable C# skills (== -0)
4 slaps on my face. :oops:
-
For most instances the Id will be fine to work with. In rare cases the Handle will have to be used, but here I think you can stick with the Id.
As for testing if it's a BlockReference, what you have works, and would most likely be what I used ( take that for what it's worth ). If you wanted another way, you could just cast it as an entity, then test it with the ' is ' keyword ( I think below is how it works ). Just another option.
Entity Ent = tr.GetObject( id, OpenMode.ForRead ) as Entity
if ( Ent is BlockReference )
result.Add( id );
-
Thanks for the confirmation Sinc.
Luis,
That would just get all the inserts of that specific block definition, not all the blocks that are inserted ( nested ) within the definition.
dang!.... sorry
lately I'm way out of my miserable C# skills (== -0)
4 slaps on my face. :oops:
Not a problem Luis. :-)
-
Thanks Tim.
My C# 'stuff' isn't much more than hacking some snippets here and there :-(
-
Thanks Tim.
My C# 'stuff' isn't much more than hacking some snippets here and there :-(
You're welcome Gile. I'm learning just like you, and I see you with some good stuff, and I'm sure you will keep at it and develop some good stuff.
-
Thanks Tim.
My C# 'stuff' isn't much more than hacking some snippets here and there :-(
The guys on here recommended Pro C# 2008 and the .NET platform. It was a great read, but I already had a good foundation of C# by then. Headfirst C# is a great entry-level hands-on book, though (in my opinion).
What level are you currently at?
-
Well, I have some time to play today with little c# and here it is Gile's function with some touch ups.... HTH-maybe :)
public ObjectIdCollection GetBlockRefInLayouts()
{
ObjectIdCollection ids = new ObjectIdCollection();
Database db = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
foreach (DBDictionaryEntry entry in layoutDict)
{
if (entry.Key == "Model") continue;
Layout layout = (Layout)tr.GetObject(entry.Value, OpenMode.ForRead);
BlockTableRecord layoutBtr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead, false);
if (!layoutBtr.IsAnonymous)
{
foreach (ObjectId id in layoutBtr)
{
BlockReference blk = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (blk != null) ids.Add(id);
}
}
}
tr.Commit();
}
return ids;
}
[CommandMethod("PSBLOCKS")]
public void psblocks()
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
ObjectIdCollection ids = new ObjectIdCollection();
ids = GetBlockRefInLayouts();
ed.WriteMessage("\nBlocks on paper-space: {0}", ids.Count.ToString());
}
-
That's a touch closer to the methodology I used. Rather than going through the NOD I directly grabbed the LayoutDictionaryID from each database I readDWg()'ed.
The only part I would like to query is the .IsAnonyMous property. I think I've read somewhere, or deduced from someone else's code that this means is not in an Xref? Close? :-)
-
Una vez más, muchas gracias Luis.
He apprendido el uso de ObjectIdCollection, db.LayoutDictionaryId y continue, pero nunca he visto de anonymous layout block.
¿Cuáles son esos objetos?
I learned the using of ObjectIdCollection, db.LayoutDictionaryId and continue but I never saw any anonymous layout block.
What kind of objects are there ?
-
Una vez más, muchas gracias Luis.
He apprendido el uso de ObjectIdCollection, db.LayoutDictionaryId y continue, pero nunca he visto de anonymous layout block.
¿Cuáles son esos objetos?
I learned the using of ObjectIdCollection, db.LayoutDictionaryId and continue but I never saw any anonymous layout block.
What kind of objects are there ?
:)
For example, remove the IsAnonymous condition, and then in the drawing draw one dimension... it will count the anonymous block.
Also, on this line:
BlockTableRecord layoutBtr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead, false);
notice the false argument, this will ignore the recent erased blocks.
And also about the continue, we can use that here too:
if (layoutBtr.IsAnonymous) continue;
To skip the anonymous blocks.
EDIT:.... --- I need to finish some work, and if someone can insert an anonymous block and tested... I'll be back.
-
OK, I know anonymous blocks, but AFAIK, they're stored in the blocks collection, not in the layout block.
As far as I understand, layoutBtr refers to the layout block definition as the object got with the vlisp expression (I'm more comfortable with LISP):
(vla-get-Block (vla-item (vla-get-Layouts ...) "Layout1")).
If I'm true, the false parameter should have been here:
BlockReference blk = tr.GetObject(id, OpenMode.ForRead, false) as BlockReference;
EDIT: I tried both an exploded dimension and a dynamic bloc.
Only the dynamic bloc is founded whatever the IsAnonymous condition, even I found "*D2" and "*U4" in the block collection (BlockTable).
And it seems there's no need to the false parameter if a block reference have been erased.
-
OK, I know anonymous blocks, but AFAIK, they're stored in the blocks collection, not in the layout block.
As far as I understand, layoutBtr refers to the layout block definition as the object got with the vlisp expression (I'm more comfortable with LISP):
(vla-get-Block (vla-item (vla-get-Layouts ...) "Layout1")).
If I'm true, the false parameter should have been here:
BlockReference blk = tr.GetObject(id, OpenMode.ForRead, false) as BlockReference;
I need another coffee... and need to play a little more with C#.
Have been lately in the miserable ATL COM stuff... and that might be my excuse...
-
I don't think you need to check if the block is anonymous, because I don't think a layout could have an anonymous block associated with it.
I'm not sure if you would need to check if the block has been erased either, as it might not be in the layout dictionary, but a quick test could be done. Just create a layout, and run the code. Then delete the layout, and run the code. Then you should be able to tell if the layout information is still in the dictionary.
-
OK, I know anonymous blocks, but AFAIK, they're stored in the blocks collection, not in the layout block.
As far as I understand, layoutBtr refers to the layout block definition as the object got with the vlisp expression (I'm more comfortable with LISP):
(vla-get-Block (vla-item (vla-get-Layouts ...) "Layout1")).
If I'm true, the false parameter should have been here:
BlockReference blk = tr.GetObject(id, OpenMode.ForRead, false) as BlockReference;
EDIT: I tried both an exploded dimension and a dynamic bloc.
Only the dynamic bloc is founded whatever the IsAnonymous condition, even I found "*D2" and "*U4" in the block collection (BlockTable).
And it seems there's no need to the false parameter if a block reference have been erased.
Yep... did that too.... I'm glad that I don't code in C# for a living.... :-P
-
I don't think you need to check if the block is anonymous, because I don't think a layout could have an anonymous block associated with it.
I'm not sure if you would need to check if the block has been erased either, as it might not be in the layout dictionary, but a quick test could be done. Just create a layout, and run the code. Then delete the layout, and run the code. Then you should be able to tell if the layout information is still in the dictionary.
We (me) did that already Tim.... and you right.
thanks! to refresh my 3 brain cells... left
-
I don't think you need to check if the block is anonymous, because I don't think a layout could have an anonymous block associated with it.
I'm not sure if you would need to check if the block has been erased either, as it might not be in the layout dictionary, but a quick test could be done. Just create a layout, and run the code. Then delete the layout, and run the code. Then you should be able to tell if the layout information is still in the dictionary.
We (me) did that already Tim.... and you right.
thanks! to refresh my 3 brain cells... left
My pleasure Luis. You probably forgot more about programming that I will ever know. :wink: