• Industries
  • Products
  • Buy
  • Services & Support
  • Communities
  • Discussion Groups

    .NET

    Reply
    Active Contributor
    Posts: 26
    Registered: ‎03-10-2012
    Accepted Solution

    Block-Functions (Select, Explode, etc.)

    914 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

    Please use plain text.
    *Expert Elite*
    chiefbraincloud
    Posts: 736
    Registered: ‎02-13-2008

    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
    Please use plain text.
    Active Contributor
    Posts: 26
    Registered: ‎03-10-2012

    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

     


    Please use plain text.
    *Expert Elite*
    chiefbraincloud
    Posts: 736
    Registered: ‎02-13-2008

    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
    Please use plain text.
    Active Contributor
    Posts: 26
    Registered: ‎03-10-2012

    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

    Please use plain text.
    *Expert Elite*
    chiefbraincloud
    Posts: 736
    Registered: ‎02-13-2008

    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
    Please use plain text.
    Active Contributor
    Posts: 26
    Registered: ‎03-10-2012

    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

    Please use plain text.
    *Expert Elite*
    Hallex
    Posts: 1,371
    Registered: ‎10-08-2008

    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
    Please use plain text.
    Active Contributor
    Posts: 26
    Registered: ‎03-10-2012

    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

    Please use plain text.
    *Expert Elite*
    chiefbraincloud
    Posts: 736
    Registered: ‎02-13-2008

    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
    Please use plain text.