.NET

Reply
Active Contributor
klahie
Posts: 26
Registered: ‎03-10-2012
Message 1 of 23 (1,567 Views)
Accepted Solution

Block-Functions (Select, Explode, etc.)

1567 Views, 22 Replies
04-15-2012 02:16 AM

Hi community!

 

I have a C#-WPF-Application. In this application there is a list of blocks in my drawing. When I click one of these blocks in the listbox I have several button-functions. 

 

One of them:

Select the in the list selected block in the drawing.

 

Another:

Explode the in the list selected block.

 

I found several topics with Selection, but none of them worked for me. I'm looking for a selection via ObjectId or blockname of a single block, in the most cases the selectionfilter needs an array(?). 

And I'm absolutly helpless with block-explosion. :smileysad:

 

I'm really new to Autocad and C#-Addin-programming. :smileyindifferent:

 

Thank you in advance!

Greets

Klaus

*Expert Elite*
chiefbraincloud
Posts: 752
Registered: ‎02-13-2008
Message 2 of 23 (1,523 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 09:52 AM in reply to: klahie

This blog post explains the selection filter mechanism

http://through-the-interface.typepad.com/through_the_interface/2008/07/conditional-sel.html

 

What you basically need for the selection filter (by blockname) is:

TypedValue[] SSfilter = {
	new TypedValue(0, "INSERT"),
	new TypedValue(2, "BLOCKNAME")
};

 If you have objectid(s) and want them to be selected in the editor, you use editor.SetImpliedSelection(objectid())

 

Assuming you are using the managed API, there are two ways to explode a BlockReference, one is the Explode method inherited from entity.  You pass in a DbObjectCollection, and AutoCAD fills the collection with the entities generated from the explode.  You are then responsible for either adding them to the database, or disposing of them when you are done, depending on your needs.

 

The other is BlockReference.ExplodeToOwnerSpace().  That one automatically appends the generated entities to the parent of the block reference.  This could be the Model space, or a paper space layout, or even another block if the block was nested.  The Block must be uniformly scaled in both methods.

Dave O.                                                                  Sig-Logos32.png
Active Contributor
klahie
Posts: 26
Registered: ‎03-10-2012
Message 3 of 23 (1,512 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 11:29 AM in reply to: klahie

Okay, first problem: Selection:

 

I tried the Selection like this:

public void selectBlock(string blockName)
{
   BlockTableRecord btr = null;
   Editor ed = doc.Editor;
   using (tr = db.TransactionManager.StartTransaction())
   {
      BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);
      foreach (ObjectId objId in bt)
      {
         btr = (BlockTableRecord)tr.GetObject(objId, OpenMode.ForWrite);

         if (blockName == btr.Name)
         {
            TypedValue[] tvs = new TypedValue[] {
               new TypedValue(
                  (int)DxfCode.Operator,
                  "<or"
               ),
               new TypedValue (
                  (int)DxfCode.BlockName,
                  btr.Name
               ),
               new TypedValue(
                  (int)DxfCode.Operator,
                  "or>"
               )
            };

            SelectionFilter sf = new SelectionFilter(tvs);
            PromptSelectionResult psr = ed.SelectAll(sf);
            ed.WriteMessage("\nFound {0} entit{1}.", psr.Value.Count, (psr.Value.Count == 1 ? "y" : "ies"));
         }
      }
   }
}

I know, I could simply give the blockName to the DxfCode.BlockName-variable without foreach and so on, but I also tried it with objectId. Hmm, theeditor-message shows up that AutoCAD finds one entity. But this entity isn't selected!? :smileysad:

 

But Kean Walmsley also writes in his blog: " This simply tells you how many entities met the selection criteria - it doesn't leave them selected for use by further commands.".


Okay, now... How do I leave it selected for use by further commands? :smileywink:

 

I've also tried the selection like you posted it with :

new TypedValue(0, "INSERT"),
new TypedValue(2, blockName)

 If this is, what you've meant, it doesn't work for me either.

 

And I didn't get your point with the objectId-selection, my editor wants an array of ObjectIds. Should I create an Array of ObjectIds with the objectid of the block on the first position? :smileysad:

But if I get it working with the blockname, I do not need the selection by objectid. I simply want the easiest way to select the block. :smileywink:

__________________________________

 

Second problem: Explosion

 

My block is a BlockTableRecord. How do I get a BlockReference to work with my BlockTableRecord?

 

Many thanks!

Klaus

 


*Expert Elite*
chiefbraincloud
Posts: 752
Registered: ‎02-13-2008
Message 4 of 23 (1,493 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 04:07 PM in reply to: klahie

Well, I've already done all this once, then when I went to post it, the discussion group kicked me out, so here goes again.

 

First, I think you need to do a little research on the difference between a BlockTableRecord, and a BlockReference.  It has been covered on this discussion group so I'm not going to get into here, especially because I have no idea what your experience level with AutoCAD is.

 

Kean's comment "This simply tells you how many entities met the selection criteria - it doesn't leave them selected for use by further commands."  Means that the entities are not left selected in the editor, there is a selection set created (psr.value) that can be used by code to do other things with the selection, they just don't appear to be selected to the user after function ends.

 

I have worked up these two quick examples of how to get the objects you want and leave them selected in the editor after your command completes.  One piece that is not shown here is the the CommandMethod that is called to use this code must have the "CommandFlags.Redraw" set.

 

public void selectBlock(string blockName)
{
Database db = HostApplicationServices.WorkingDatabase;
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
    try
    {
        BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead, false, true);
        if (bt.Has(blockName))
        {
            ObjectId btrid = bt[blockName];
            if (!btrid.IsEffectivelyErased)
            {
                BlockTableRecord btr = (BlockTableRecord)trans.GetObject(btrid, OpenMode.ForRead, false, true);
                ObjectIdCollection brefIDs = btr.GetBlockReferenceIds(true, false);
                ObjectId[] oids = new ObjectId[brefIDs.Count];
                brefIDs.CopyTo(oids, 0);
                ed.SetImpliedSelection(oids);
            }
        }
        trans.Commit();
    }
    catch (System.Exception ex)
    {
        //
    }
}
}

public void selectBlock2(string blockName)
{
Database db = HostApplicationServices.WorkingDatabase;
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
TypedValue[] tvs = new TypedValue[] {
	new TypedValue(0, "INSERT"),
	new TypedValue(2, blockName)
};
SelectionFilter sf = new SelectionFilter(tvs);
PromptSelectionResult psr = ed.SelectAll(sf);
if (psr.Status == PromptStatus.OK)
{
    ed.SetImpliedSelection(psr.Value.GetObjectIds());
}
}

 I would use the second method, as it does not require a transaction, but the first method does demonstrate something that may be useful for your Explode problem.

 

You can not call Explode on a BlockTableRecord.  You also can not "Select" them using selection methods or filters.  There is no graphical representation of a BlockTableRecord.

 

So you say you have a BlockTableRecord.  If all you want is to find out what is in that BlockTableRecord, you can iterate through it's contents with a For Each of DbObject.  I am assuming that is not what you want, and that what you want is to Explode the BlockReferences that point to that BlockTableRecord.  The first example above shows how to get the ObjectIds of all BlockReferences that point to a particular BlockTableRecord.  You can then use those ObjectIds to open each BlockReference and call .ExplodeToOwnerSpace if you want to duplicate the behavior of the AutoCAD Explode command (I'm not sure if you have to erase the original block, or if the API does it for you, but the API does add each of the resultant entities to the owner of the BlockReference), or you can call .Explode and the use the results of that explode to do whatever it is you need to do (In which case you must add the objects to the database or dispose of them, and if you want the original block to be erased you have to do that yourself).

 

Give that a shot.  I'll see if I can find a good article describing the difference between a BlockTableRecord and a BlockReference.

 

Dave O.                                                                  Sig-Logos32.png
Active Contributor
klahie
Posts: 26
Registered: ‎03-10-2012
Message 5 of 23 (1,488 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 04:46 PM in reply to: chiefbraincloud

chiefbraincloud wrote:

  It has been covered on this discussion group so I'm not going to get into here, especially because I have no idea what your experience level with AutoCAD is.

 


Hmm, well... My AutoCAD-experiences belong to an half-an-hour introduction of a friend of mine. :smileyfrustrated:

It is a small project for my school leaving examination. I have to make a little plugin for a company, it isn't really bad for them if it is only a prototype. :smileywink:

 

Many thanks, the Selection works like a charm! :smileyhappy:

 

I tried the explosion like this:

public void explodeBlock(string blockName)
{
   Editor ed = doc.Editor;
   using (tr = db.TransactionManager.StartTransaction())
   {
      BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false, true);
      if (bt.Has(blockName)) {
         ObjectId btrid = bt[blockName];
         if (!btrid.IsEffectivelyErased) {
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(btrid, OpenMode.ForRead, false, true);

            ObjectIdCollection brefIds = btr.GetBlockReferenceIds(true, false);
            ObjectId[] oids = new ObjectId[brefIds.Count];
            brefIds.CopyTo(oids, 0);
            foreach (ObjectId objid in brefIds) {
               BlockReference br = (BlockReference)tr.GetObject(objid, OpenMode.ForWrite);
               br.ExplodeToOwnerSpace();
            }
         }
      }
   }
}

 I do not really realize if something has changed, can I check this anywhere?

 

Greets

Klaus

*Expert Elite*
chiefbraincloud
Posts: 752
Registered: ‎02-13-2008
Message 6 of 23 (1,486 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 05:34 PM in reply to: klahie

first: these two lines are useless in your function

 

ObjectId[] oids = new ObjectId[brefIds.Count];
brefIds.CopyTo(oids, 0);

Second, I would definitely put the ExplodeToOwnerSpace() method inside a Try/Catch, because if the block has a non-uniform scale (ie. the X, Y, and Z scales are not the same) which is not that uncommon, the ExplodeToOwnerSpace method will fail.

 

To see the change, try using your selection tool to select all references of a block.  Look at the drawing window, and you should see some grips (little colored squares), by default there should be one grip per block (but there is a user setting that would cause more grips to show up).

 

then zoom in to where you can see one or more of the blocks. 

 

Run your explode tool on the same blockName.  Then you can perform two tests. one, try using the mouse and selecting a part of the block you have in your view.  If it is exploded, you will now have individual entities (lines, arcs, circles, etc...) instead of multiple entities grouped into a single block, and you will see more than one grip, and the whole block will not be selected, but just the entity or entities you pick.

 

test two, try running your select tool again on the same block.  If the original blocks were erased when they were exploded, you will get zero selected objects.  If not you will have to add a line of code in your for each to erase them manually.

 

Basically not being able to see a difference should be a good thing (if you aren't getting any errors) but the help docs are not clear about whether the original block is erased by ExplodeToOwnerSpace, so you could have a bunch of new entities created right on top of the old block, but what you really want is a bunch of new entities, and no more old block.

Dave O.                                                                  Sig-Logos32.png
Active Contributor
klahie
Posts: 26
Registered: ‎03-10-2012
Message 7 of 23 (1,475 Views)

Re: Block-Functions (Select, Explode, etc.)

04-16-2012 11:16 PM in reply to: chiefbraincloud

Hmm for me it does nothing. :smileysad: Not even if i call

br.Erase()

 I tried it with the drawing I've got from alfred.neswadba (http://forums.autodesk.com/t5/NET/Edit-a-Block-via-C/m-p/3366541#M27591), you find it attached. Seems not to work. :smileysad:

 

Oh, the last function I have to implement should copy a block and paste it. For paste there should be a cursor to set the insertpoint for the block (like the usual insert in AutoCAD). How complex would this be? :smileywink: 

 

Greets

Klaus

*Expert Elite*
Hallex
Posts: 1,569
Registered: ‎10-08-2008
Message 8 of 23 (1,455 Views)

Re: Block-Functions (Select, Explode, etc.)

04-17-2012 07:28 AM in reply to: klahie

See if this is working for you,

tested on A2010 only

 

    Public Sub ApplyAttributes(db As Database, tr As Transaction, bref As BlockReference)

        Dim btrec As BlockTableRecord = TryCast(tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)

        If btrec.HasAttributeDefinitions Then

            Dim atcoll As Autodesk.AutoCAD.DatabaseServices.AttributeCollection = bref.AttributeCollection

            For Each subid As ObjectId In btrec

                Dim ent As Entity = DirectCast(subid.GetObject(OpenMode.ForRead), Entity)

                Dim attDef As AttributeDefinition = TryCast(ent, AttributeDefinition)

                If attDef IsNot Nothing Then

                    Dim attRef As New AttributeReference()

                    attRef.SetDatabaseDefaults()

                    attRef.SetAttributeFromBlock(attDef, bref.BlockTransform)

                    attRef.Position = attDef.Position.TransformBy(bref.BlockTransform)

                    attRef.Tag = attDef.Tag

                    attRef.TextString = attDef.TextString

                    attRef.AdjustAlignment(db)

                    atcoll.AppendAttribute(attRef)

                    tr.AddNewlyCreatedDBObject(attRef, True)

                End If

            Next
        End If
    End Sub
    Public Sub TestInsert()
        Dim blkname As String = "VT_VZ_VZ52_10a_ATT" ''<-- as per as on your drawing
        Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim ed As Editor = doc.Editor
        Dim db As Database = doc.Database
        Try
            Using docloc As DocumentLock = doc.LockDocument

                Using tr As Transaction = db.TransactionManager.StartTransaction
                    Dim bt As BlockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead)

                    If Not bt.Has(blkname) Then
                        MsgBox("Block does not exists")
                        Return
                    End If

                    Dim pto As PromptPointOptions = New PromptPointOptions(vbLf + "Pick a block insertion point: ")
                    Dim ptres As PromptPointResult = ed.GetPoint(pto)
                    Dim ipt As Point3d

                    If ptres.Status <> PromptStatus.Cancel Then
                        ipt = ptres.Value
                    End If

                    Dim btr As BlockTableRecord = DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite, False), BlockTableRecord)
                    Dim blk As BlockTableRecord = DirectCast(tr.GetObject(bt(blkname), OpenMode.ForRead, False), BlockTableRecord)
                    Dim bref As New BlockReference(ipt, blk.ObjectId)
                    bref.BlockUnit = UnitsValue.Millimeters
                    bref.Rotation = 0
                    bref.ScaleFactors = New Scale3d(1000.0) ''<-- as per as on your drawing
                    btr.AppendEntity(bref)
                    tr.AddNewlyCreatedDBObject(bref, True)
                    ApplyAttributes(db, tr, bref)
                    ed.Regen()
                    tr.Commit()
                End Using
            End Using
        Catch ex As System.Exception
            MsgBox(ex.Message)
        End Try
    End Sub

 

~'J'~

_____________________________________
C6309D9E0751D165D0934D0621DFF27919
Active Contributor
klahie
Posts: 26
Registered: ‎03-10-2012
Message 9 of 23 (1,444 Views)

Re: Block-Functions (Select, Explode, etc.)

04-17-2012 08:58 AM in reply to: Hallex

Okay, Explosion is working! :smileyvery-happy:

 

Just add an tr.Commit() to my function.... :smileyembarrassed:

 

To the insert:

 

I'm not very common with VB, so i translated it with < Convert C# to VB.NET >. What I got is (in your TestInsert()-method):

 

PromptPointOptions pto = new PromptPointOptions(Constants.vbLf + "Pick a block insertion point: ");

 Hmm Constants."anything" doesn't tell me anything, so i've deleted "Constants.", but vbLf is nowhere declared in this method. How do you use it?

 

The second thing:

BlockTableRecord blk = (BlockTableRecord)tr.GetObject(bt(blkname), OpenMode.ForRead, false);

 'bt' is a variable but used like a method, can you explain this to me? 

 

And many thanks to chiefbraincloud for your help at Selection & Explosion! :smileyvery-happy:

 

Greets

Klaus

*Expert Elite*
chiefbraincloud
Posts: 752
Registered: ‎02-13-2008
Message 10 of 23 (1,439 Views)

Re: Block-Functions (Select, Explode, etc.)

04-17-2012 09:20 AM in reply to: klahie

Oops.  I didn't even look to see if you had the .commit in there.

 

vbLf is a constant for a line feed character (in Visual Basic), I don't know if C# has a similar set of constants, but I know you can use chr(13).

 

2nd prob should be square braces instead of parenthesis:

BlockTableRecord blk = (BlockTableRecord)tr.GetObject(bt[blkname], OpenMode.ForRead, false);

 

I didn't look at Hallex's code other than what you posted as conversion problems.

Dave O.                                                                  Sig-Logos32.png
Announcements
Are you familiar with the Autodesk Expert Elites? The Expert Elite program is made up of customers that help other customers by sharing knowledge and exemplifying an engaging style of collaboration. To learn more, please visit our Expert Elite website.
Need installation help?

Start with some of our most frequented solutions or visit the Installation and Licensing Forum to get help installing your software.