Author Topic: Help with WBlockCloneObjects  (Read 14873 times)

0 Members and 1 Guest are viewing this topic.

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Help with WBlockCloneObjects
« on: March 08, 2011, 05:32:13 PM »
Attached is a VS2008-C# project. It is a stripped down version of something I've been working on for a while now. At one time, this code (or something close to it is more like it) worked. While streamlining and doing other things that I thought were unrelated, I've managed to break it. I've been staring at it for 2 days now, finally ripping out what I'm posting here down to the minimums.

The idea is to create a table in the current drawing which shows an image and description (ok, a legend of sorts) of the Blocks being used in either A) The current drawing, B) All Open drawings, or C) Drawings selected by the user. Option A works just fine. Options B & C DID work on most, if not all, drawings. Then it started not working.....it goes through all the correct motions of gathering the blocks from other drawings, copying the definitions to the start dwg, and asking for the insertion point of the Table....Sometimes it will place the table ok, other times it will Fatal Error during the table creation. On those occasions where the table IS created, any attempt to zoom which requires a regen causes the Fatal Error.

Since I did have this working at one time, I'm thinking there is something obvious that I'm just not seeing.....and I'm hoping that someone with a fresh set of eyes and a little bit of time can have a look for me. I'd sure appreciate whatever insights anyone can offer. For anyone daring to try it on it's own, the compiled dll (for Acad2010) is included which can be Netload'ed, and the command name is LegendTest.

I used the ObjectArx2010 32 bit AutoCAD managed references. Although I think this should probably also work if those were changed to 2011.

Thanks!
« Last Edit: March 09, 2011, 12:45:01 PM by Jeff_M »

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Help with WBlockCloneObjects
« Reply #1 on: March 08, 2011, 05:56:06 PM »
I noticed on some of my code that when working with an open drawing, that wasn't current, I had to lock that document.  I didn't see you locking any document but the one calling the program.  Maybe that will help.
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #2 on: March 08, 2011, 06:47:23 PM »
Have not looked at it but tables changed in 2010 or 2011.

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #3 on: March 08, 2011, 06:53:35 PM »
Thanks Tim & Jeff for the quick comments.

Tim, just tried locking all documents, no change.

Jeff, the tables create fine in 2010. In fact I can comment out the code for the tables, then manually attempt to insert one of the imported blocks and get the same Fatal Error.  My next step was to get this working in 2011 as well, but since this version isn't yet working.....

So this tells me that somehow I am mucking up the BlockTable, which the only way I can do that is by my use of WBlockCloneObjects (I think).

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #4 on: March 08, 2011, 07:15:27 PM »
I am trying it on a2011 - is not even let me select the insertion point (there is some { missing? right after:
           
PromptPointResult pr = ed.GetPoint("\nLegend Table insertion point: ");

below your call to PromptStatus.OK

Still can't pass after that line... will give it another try later...

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #5 on: March 08, 2011, 07:18:38 PM »
I get the designer can not be shown for this file error.

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #6 on: March 08, 2011, 07:38:57 PM »
Luis, I'm getting the same as you in 2011. There is no missing { }'s because there is only one item after the "if", that being the "using" so this should work just fine (and does in 2010).

Stepping through the code in 2011, this line never pauses for input:
                PromptPointResult pr = ed.GetPoint("\nLegend Table insertion point: ");
instead it automatically returns Cancel.

Jeff, I got that when I first ripped it out of the bigger program. Try building before opening the Form in the editor.
« Last Edit: March 08, 2011, 07:43:40 PM by Jeff_M »

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #7 on: March 08, 2011, 07:40:50 PM »
Test under A2011 using the autocad assemblies (that come with the acad.exe installation, since these are not available in the ARX SDK)

If I insert the table at a predefined point, it works, no problems.
Code: [Select]
           //PromptPointOptions ppo = new PromptPointOptions("\nLegend Table insertion point: "); // test only
            //ppo.AllowArbitraryInput = false;
            //ppo.AllowNone = false;

            //PromptPointResult pr = ed.GetPoint(ppo); //"\nLegend Table insertion point: ");
            //if (pr.Status != PromptStatus.OK) return;

            Point3d pt = Point3d.Origin; // <<<<
                
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
                if (!btr.Name.ToUpper().Equals("*MODEL_SPACE"))
                    scale = 1.0;
                ObjectId tblId;
                if (lb_LegendTable.SelectedIndex != -1)
                {
                    DBDictionary nod = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
                    DBDictionary dict = (DBDictionary)tr.GetObject(nod.GetAt("ACAD_TABLESTYLE"), OpenMode.ForRead);
                    tblId = (ObjectId)dict[lb_LegendTable.SelectedValue.ToString()];
                }
                else
                    tblId = db.Tablestyle;

                TableStyle tstyle = (TableStyle)tr.GetObject(tblId, OpenMode.ForRead);
                double titleHgt = tstyle.TextHeight(RowType.TitleRow) * scale;
                double headerHgt = tstyle.TextHeight(RowType.HeaderRow) * scale;
                double dataHgt = tstyle.TextHeight(RowType.DataRow) * scale;
                double hzMrgn = tstyle.HorizontalCellMargin * scale;
                double vrtMrgn = tstyle.VerticalCellMargin * scale;

                AcDb.Table legendtbl = new AcDb.Table();
                double rowHgt = dataHgt + (2.0 * vrtMrgn);
                double colOneWdth = headerHgt * 7.0; //6 characters in SYMBOL + 1 for spacing
                double colTwoWdth = dataHgt * maxdesclength;
                legendtbl.TableStyle = tblId;
                legendtbl.InsertRows(1, rowHgt, blklist.Count + 1);
                legendtbl.VerticalCellMargin = vrtMrgn;
                legendtbl.HorizontalCellMargin = hzMrgn;
                legendtbl.DeleteColumns(0, 3);
                legendtbl.InsertColumns(0, colOneWdth, 1);
                legendtbl.InsertColumns(1, colTwoWdth, 1);
                legendtbl.Cells.Borders.Bottom.IsVisible = false;
                legendtbl.Cells.Borders.Horizontal.IsVisible = false;
                legendtbl.Cells.Borders.Left.IsVisible = false;
                legendtbl.Cells.Borders.Right.IsVisible = false;
                legendtbl.Cells.Borders.Top.IsVisible = false;
                legendtbl.Cells.Borders.Vertical.IsVisible = false;
                legendtbl.Rows[0].Height = titleHgt + (2.0 * vrtMrgn);
                legendtbl.Rows[0].TextHeight = titleHgt;
                legendtbl.Rows[1].TextHeight = headerHgt;
                legendtbl.Cells[0, 0].TextString = "Symbol Legend";
                legendtbl.Cells[1, 0].TextString = "Symbol";
                legendtbl.Cells[1, 1].TextString = "Denotes";
                legendtbl.Cells[1, 0].Alignment = CellAlignment.MiddleCenter;
                legendtbl.Cells[1, 1].Alignment = CellAlignment.MiddleLeft;
                legendtbl.SuppressRegenerateTable(true);
                for (int i = 0; i < blklist.Count; i++)
                {
                    BlockTableRecord blkid = (BlockTableRecord)tr.GetObject(usedBlks[blklist[i]], OpenMode.ForRead);
                    string desc = blkid.Comments;
                    if (desc == "")
                        desc = blklist[i];
                    //legendtbl.Cells[i + 2, 0].VerticalLine.Margin = vrtMrgn; //This doesn't work even though we are told to use it...2010
                    legendtbl.Cells[i + 2, 0].BlockTableRecordId = usedBlks[blklist[i]];
                    legendtbl.Cells[i + 2, 1].TextString = desc;
                    legendtbl.Rows[i + 2].TextHeight = dataHgt;
                    legendtbl.Cells[i + 2, 0].Alignment = CellAlignment.MiddleCenter;
                    legendtbl.Cells[i + 2, 1].Alignment = CellAlignment.MiddleLeft;
                }
                legendtbl.SuppressRegenerateTable(false);
                legendtbl.Position = pt; //pr.Value; <<<<<

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #8 on: March 08, 2011, 08:09:44 PM »
When I do all drawings here at

Code: [Select]


            Document doc = AcApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            double scale = 20.0;

            List<string> blklist = usedBlks.Keys.ToList<string>();
            blklist.Sort();
                PromptPointResult pr = ed.GetPoint("\nLegend Table insertion point: ");
                if (pr.Status == PromptStatus.OK)

The doc is set temp/s$rty/.........
Some temp file
So pr.status is false and skips over
So to figure maybe if this is this  is the problem and where the doc is being set improperly

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #9 on: March 08, 2011, 08:12:46 PM »
Now, if I remove the command session flag, and make the drawing current manually it works if I also add this line: legendtbl.GenerateLayout()
And by doing the above, I can define the insertion point too.

So it is something about making active the drawing, where the table it is going to be placed...
Code: [Select]
   [CommandMethod("LegendTest")] //, CommandFlags.Session)] // test only
        public void LegendBuilderCommand()

Code: [Select]
           PromptPointOptions ppo = new PromptPointOptions("\nLegend Table insertion point: ");
            ppo.AllowArbitraryInput = false;
            ppo.AllowNone = false;

            PromptPointResult pr = ed.GetPoint(ppo); //"\nLegend Table insertion point: ");
            if (pr.Status != PromptStatus.OK) return;

            //Point3d pt = Point3d.Origin;
                
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
                if (!btr.Name.ToUpper().Equals("*MODEL_SPACE"))
                    scale = 1.0;
                ObjectId tblId;
                if (lb_LegendTable.SelectedIndex != -1)
                {
                    DBDictionary nod = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
                    DBDictionary dict = (DBDictionary)tr.GetObject(nod.GetAt("ACAD_TABLESTYLE"), OpenMode.ForRead);
                    tblId = (ObjectId)dict[lb_LegendTable.SelectedValue.ToString()];
                }
                else
                    tblId = db.Tablestyle;

                TableStyle tstyle = (TableStyle)tr.GetObject(tblId, OpenMode.ForRead);
                double titleHgt = tstyle.TextHeight(RowType.TitleRow) * scale;
                double headerHgt = tstyle.TextHeight(RowType.HeaderRow) * scale;
                double dataHgt = tstyle.TextHeight(RowType.DataRow) * scale;
                double hzMrgn = tstyle.HorizontalCellMargin * scale;
                double vrtMrgn = tstyle.VerticalCellMargin * scale;

                AcDb.Table legendtbl = new AcDb.Table();
                double rowHgt = dataHgt + (2.0 * vrtMrgn);
                double colOneWdth = headerHgt * 7.0; //6 characters in SYMBOL + 1 for spacing
                double colTwoWdth = dataHgt * maxdesclength;
                legendtbl.TableStyle = tblId;
                legendtbl.InsertRows(1, rowHgt, blklist.Count + 1);
                legendtbl.VerticalCellMargin = vrtMrgn;
                legendtbl.HorizontalCellMargin = hzMrgn;
                legendtbl.DeleteColumns(0, 3);
                legendtbl.InsertColumns(0, colOneWdth, 1);
                legendtbl.InsertColumns(1, colTwoWdth, 1);
                legendtbl.Cells.Borders.Bottom.IsVisible = false;
                legendtbl.Cells.Borders.Horizontal.IsVisible = false;
                legendtbl.Cells.Borders.Left.IsVisible = false;
                legendtbl.Cells.Borders.Right.IsVisible = false;
                legendtbl.Cells.Borders.Top.IsVisible = false;
                legendtbl.Cells.Borders.Vertical.IsVisible = false;
                legendtbl.Rows[0].Height = titleHgt + (2.0 * vrtMrgn);
                legendtbl.Rows[0].TextHeight = titleHgt;
                legendtbl.Rows[1].TextHeight = headerHgt;
                legendtbl.Cells[0, 0].TextString = "Symbol Legend";
                legendtbl.Cells[1, 0].TextString = "Symbol";
                legendtbl.Cells[1, 1].TextString = "Denotes";
                legendtbl.Cells[1, 0].Alignment = CellAlignment.MiddleCenter;
                legendtbl.Cells[1, 1].Alignment = CellAlignment.MiddleLeft;
                legendtbl.SuppressRegenerateTable(true);
                for (int i = 0; i < blklist.Count; i++)
                {
                    BlockTableRecord blkid = (BlockTableRecord)tr.GetObject(usedBlks[blklist[i]], OpenMode.ForRead);
                    string desc = blkid.Comments;
                    if (desc == "")
                        desc = blklist[i];
                    //legendtbl.Cells[i + 2, 0].VerticalLine.Margin = vrtMrgn; //This doesn't work even though we are told to use it...2010
                    legendtbl.Cells[i + 2, 0].BlockTableRecordId = usedBlks[blklist[i]];
                    legendtbl.Cells[i + 2, 1].TextString = desc;
                    legendtbl.Rows[i + 2].TextHeight = dataHgt;
                    legendtbl.Cells[i + 2, 0].Alignment = CellAlignment.MiddleCenter;
                    legendtbl.Cells[i + 2, 1].Alignment = CellAlignment.MiddleLeft;
                }
                legendtbl.SuppressRegenerateTable(false);
                legendtbl.Position = pr.Value; //pt
                
                legendtbl.GenerateLayout(); // <<<<

                btr.UpgradeOpen();

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #10 on: March 08, 2011, 08:21:41 PM »
If I select All open drawings and one drawing is open works.

If more than one open say E101 and E102 and do all open drawings

If E102 is current drawing @ C:\Drawings\E102.dwg a
It will create another drawing @  C:\User\Apdata\Temp\E102.Dwg
with same name just different location and that is the database it is trying to insert legend into at previous post

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #11 on: March 09, 2011, 07:44:38 AM »
This helped or made it work on this end for selecting all open drawings

After debugging a little I thought it had something to do with  locking the doc and the doc changing


commenting out  //AcApp.DocumentManager.MdiActiveDocument = opndoc;
got it working here
Code: [Select]
foreach (Document opndoc in AcApp.DocumentManager)
                    {
                        //now get the blocks in all other drawings, except the LegendDWG
                        if (doc.Name != opndoc.Name)
                        {
                            //AcApp.DocumentManager.MdiActiveDocument = opndoc;
                            gatherdatafromDWG(db, opndoc.Database);
                        }
                    }


Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #12 on: March 09, 2011, 01:02:31 PM »
Thanks for testing this out guys. There are a few things that are a "must" for this overall program to work.

The biggest is that each document must be set current in order to work with the Civil3D API. There is a huge amount of code that relies on this so it's not something I can do without. (The Civil3D API only works in the context of Current Document).

The next is that in order to make each document current, the CommandFlags.Session and DocLock must be used, at least everything I've read, and tested, thus far says so.

I'm attaching a revised project that simplifies the code. I no longer include the form or the Table creation code. So now it's down to just assuming the user has at least 2 drawings open (one in the background must have block insertions, the current dwg can be essentially empty), then run the command and it loops thru all open drawings leaving you back at the command prompt in the original drawing with Done! in the command line. This works in both 2010 and 2011. Now for the fun part, use the Insert command and select one of the blocks that did not exist in this drawing before running the BlockTest command, set the Insertion Point and Rotation options to "Specify on screen". You should see the "jig" for the block, while you pick the insertion point and rotation angle. What happens next is where we see that something is amiss...... Note that I changed the command name for this version.

With this trimmed down code I'm hoping it will be easier to spot where I went astray... Thanks again for any pointers you might have. Oh, it is currently set for a 2011 build.

Jeff, I do see in 2011 that there appears to be a 'new' drawing added to the doc collection based on the Icons in the taskbar. However, while debugging and walking thru the code, the Documents Collection Count never changes. This very well may be were things are tripping up, but I'm not seeing any way to deal with this.
« Last Edit: March 09, 2011, 01:08:08 PM by Jeff_M »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #13 on: March 09, 2011, 01:12:59 PM »
Jeff, I do see in 2011 that there appears to be a 'new' drawing added to the doc collection based on the Icons in the taskbar. However, while debugging and walking thru the code, the Documents Collection Count never changes. This very well may be were things are tripping up, but I'm not seeing any way to deal with this.

It did not do it on 64-bit I do not think.

You should be able to switch documents and lock them but

Lock Document
Create Form
Method....
Method....
Method....
Method....
Method....
Method....
End Lock

Instead Lock only when making modification then disposeing imediatley
I am about to download new version and have toying around with something similiar, but If you do not mind I might take parts of youra and itergrate them with what I was working and post back.

If that is okay with you of course

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Help with WBlockCloneObjects
« Reply #14 on: March 09, 2011, 02:34:01 PM »
The new version worked here in '09 Electrical.
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #15 on: March 09, 2011, 02:44:06 PM »
If that is okay with you of course
Go for it. :-)

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #16 on: March 15, 2011, 09:32:19 PM »
I have not had time to really work on it, but here is something too see how it does.(If it is too slow, just plain crap etc.....)

This is just a basic method to copy all blocks from every open drawing into the current drawing.

If one drawing is open or none of the other open drawings pass the logical conditions then nothing will happen 

I went a little different route.

Basically here is the run down,
To keep each document from becoming active I just opened the databases and read it from  there.
I iterated the BlockTable and made sure it was not a Layout, from x-ref, anonymous, some things to weed out MEP objects, and has been at least inserted once etc.....

Maybe is not any better or probably worst than what you have just throwing it out there.


Code: [Select]
[CommandMethod("InsertBlocks", CommandFlags.Session)]
        public void InsertBlocks()
        {
            List<string> blockNames = new List<string>(); // A generic list to keep up with all blocks to check later if a block has already inserted.

            DocumentCollection documents = Application.DocumentManager; // All the current open documents

            Document doc = Application.DocumentManager.MdiActiveDocument; // Active Document
            Editor ed = doc.Editor; // Active Document's Editor
            Database db = doc.Database; // Active Document Database

            Document openDocument; // Temporay variable to hold each non-active Document
            Database openDatabase; //Temporay variable to hold each non-active Document's Database

            using (DocumentLock doclock = doc.LockDocument()) // Start Lock on Active Document
            using (Transaction trx = db.TransactionManager.StartTransaction()) // Start Transaction with Active Document's Database
            {
                // Start looping through each document
                foreach (Document document in documents)
                {
                    openDocument = document;
                    string fileName = openDocument.Name;
                    // If the document is the current or Active document skip it
                    if (doc.Name == fileName || fileName == null || fileName == String.Empty)
                        continue;

                    openDatabase = new Database(false, true);
                    // 'Open' Open documents database
                    openDatabase.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, true, "");

                    using (Transaction openDbTrx = openDatabase.TransactionManager.StartTransaction())
                    {//Start Transaction with Open Document's Database

                        BlockTable openBlockTable = openDbTrx.GetObject(openDatabase.BlockTableId, OpenMode.ForRead) as BlockTable;

                        // Loop through each BlockTableRecord
                        foreach (ObjectId btrId in openBlockTable)
                        {
                            BlockTableRecord btr = openDbTrx.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                            // Make sure it is not a ModelSpace or any of the PaperSpaces, any type of Xref
                           // or MEP type Object, or has already been clone before etc.... & and the block has been inserted at least once
                            if (btr.IsLayout || btr.IsAnonymous || btr.IsFromExternalReference || btr.IsFromOverlayReference || blockNames.Contains(btr.Name)
                                || btr.Name.StartsWith("Aec", StringComparison.OrdinalIgnoreCase) || btr.Name.StartsWith("_")
                                || btr.GetBlockReferenceIds(true, false).Count < 1)
                                continue;
                            //Add Block to list of blocks
                            blockNames.Add(btr.Name);

                            //Do Cloning
                            ObjectIdCollection objIdCollection = new ObjectIdCollection();
                            objIdCollection.Add(btr.ObjectId);
                            IdMapping idMap = new IdMapping();
                            openDatabase.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);

                        }
                        openDbTrx.Commit();
                    }//End Transaction with Open Document's Database
                    openDatabase.Dispose(); // Dispose Database
                }// End foreach
                trx.Commit();
            }// End Lock on Active Document & Transaction with Active Document's Database

        }


Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #17 on: March 15, 2011, 11:33:17 PM »
Thanks Jeff. This works on 2 of the drawings mine fails with. It gives me something to work with. Although if it's the open database separately that does it, then I'm not sure what I'll be able to do to get my code working, as I need to be able to gather blocks used in Civil3D object styles and the only way (I know of anyway) is to use the C3D API, which requires the drawing to be current. This is also why I walk through each Layout's BTR.

I see is that you omit Dynamic blocks (by omitting anonymous blocks), which I am also trying to be sure to include....including each Visibility state used. I had thought that this is where my code was getting tripped up. Until I had a drawing with no DB's in it that failed in the same fashion.

I probably won't be able to look into this until this coming weekend, but this has helped me to look in other directions. Thanks!



Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #18 on: March 16, 2011, 01:26:01 AM »
Not fimilar with C3D, so I guess this does not help much but for anyone else who cares you could grab dynamic blocks with some like this added after first logical check.

Could check if IsDynamicBlock then get Anonymous references that way

I just copied from http://www.theswamp.org/index.php?topic=31859.msg412448#msg412448
and then used some of Jeff_M's code noted

Probably not the best solution or I am pretty sure there is a better way was just testing out.
Would have to look back into dynamic blocks

Helper Class
Code: [Select]
  public class DynamicBlocks
        {
            public string BlockName { get; set; }
            public List<object> VisibilityProperties;

            public DynamicBlocks(string blockName)
            {
                BlockName = blockName;
                VisibilityProperties = new List<object>();
            }

        }

Code: [Select]
   if (btr.IsDynamicBlock)
                            {
                                DynamicBlocks dynBlks = new DynamicBlocks(btr.Name);
                                ObjectIdCollection brefObjidColl = btr.GetBlockReferenceIds(true, true);
                                ObjectIdCollection anonObjIdsColl = btr.GetAnonymousBlockIds();

                                foreach (ObjectId anonObjId in anonObjIdsColl)
                                {
                                    BlockTableRecord btrAnon = (BlockTableRecord)anonObjId.GetObject(OpenMode.ForRead);
                                    ObjectIdCollection ObjidColl = btrAnon.GetBlockReferenceIds(true, true);
                                    foreach (ObjectId Id in ObjidColl)
                                        brefObjidColl.Add(Id);
                                }

                           
                                foreach (ObjectId brefObjId in brefObjidColl)
                                {
                                    BlockReference bref = brefObjId.GetObject(OpenMode.ForWrite) as BlockReference;
                                    DynamicBlockReferencePropertyCollection dynBrefColl = bref.DynamicBlockReferencePropertyCollection;
                                    foreach (DynamicBlockReferenceProperty dynBrefProps in dynBrefColl)
                                    {
                                        // Jeff_M code
                                        if (dynBrefProps.PropertyTypeCode == 5)
                                        {
                                            dynBlks.VisibilityProperties.Add(dynBrefProps.Value);
                                        }
                                    }
                                }
                            }



Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #19 on: March 16, 2011, 03:06:13 AM »
Tested it real quick to add a refrence for each visibility state at random points for each dynamic block with visibility parameter

Code: [Select]
     ///////////////////////////////////Start Add/////////////////////////////
        public class DynamicBlocks
        {
            public string BlockName { get; set; }
            public List<object> VisibilityProperties;

            public DynamicBlocks(string blockName)
            {
                BlockName = blockName;
                VisibilityProperties = new List<object>();
            }

        }
        ///////////////////////////////////End Add/////////////////////////////

        //A method to iterate all open drawings and to get all blocks that are inserted in each
        //drawing and copying them over to the current active document.
        [CommandMethod("InsertBlocks", CommandFlags.Session)]
        public void InsertBlocks()
        {
            List<string> blockNames = new List<string>(); // A generic list to keep up with all blocks to check later if a block has already inserted.

            List<DynamicBlocks> dynamicBlocks = new List<DynamicBlocks>();//Added

            DocumentCollection documents = Application.DocumentManager; // All the current open documents

            Document doc = Application.DocumentManager.MdiActiveDocument; // Active Document
            Editor ed = doc.Editor; // Active Document's Editor
            Database db = doc.Database; // Active Document Database

            Document openDocument; // Temporay variable to hold each non-active Document
            Database openDatabase; //Temporay variable to hold each non-active Document's Database

            using (DocumentLock doclock = doc.LockDocument()) // Start Lock on Active Document
            using (Transaction trx = db.TransactionManager.StartTransaction()) // Start Transaction with Active Document's Database
            {
                // Start looping through each document
                foreach (Document document in documents)
                {
                    openDocument = document;
                    string fileName = openDocument.Name;
                    // If the document is the current or Active document skip it
                    if (doc.Name == fileName || fileName == null || fileName == String.Empty)
                        continue;

                    openDatabase = new Database(false, true);
                    // 'Open' Open documents database
                    openDatabase.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, true, "");

                    using (Transaction openDbTrx = openDatabase.TransactionManager.StartTransaction())
                    {//Start Transaction with Open Document's Database

                        BlockTable openBlockTable = openDbTrx.GetObject(openDatabase.BlockTableId, OpenMode.ForRead) as BlockTable;

                        // Loop through each BlockTableRecord
                        foreach (ObjectId btrId in openBlockTable)
                        {
                            BlockTableRecord btr = openDbTrx.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                            // Make sure it is not a ModelSpace or any of the PaperSpaces, any type of Xref
                           // or MEP type Object, or has already been clone before etc.... & and the block has been inserted at least once
                            if (btr.IsLayout || btr.IsAnonymous || btr.IsFromExternalReference || btr.IsFromOverlayReference || blockNames.Contains(btr.Name)
                                || btr.Name.StartsWith("Aec", StringComparison.OrdinalIgnoreCase) || btr.Name.StartsWith("_")
                                || btr.GetBlockReferenceIds(true, false).Count < 1)
                                continue;

                            ///////////////////////////////////Start Add/////////////////////////////
                            if (btr.IsDynamicBlock)
                            {
                                DynamicBlocks dynBlks = new DynamicBlocks(btr.Name);
                                ObjectIdCollection brefObjidColl = btr.GetBlockReferenceIds(true, true);
                                ObjectIdCollection anonObjIdsColl = btr.GetAnonymousBlockIds();

                                foreach (ObjectId anonObjId in anonObjIdsColl)
                                {
                                    BlockTableRecord btrAnon = (BlockTableRecord)anonObjId.GetObject(OpenMode.ForRead);
                                    ObjectIdCollection ObjidColl = btrAnon.GetBlockReferenceIds(true, true);
                                    foreach (ObjectId Id in ObjidColl)
                                        brefObjidColl.Add(Id);
                                }

                                foreach (ObjectId brefObjId in brefObjidColl)
                                {
                                    BlockReference bref = brefObjId.GetObject(OpenMode.ForWrite) as BlockReference;
                                    DynamicBlockReferencePropertyCollection dynBrefColl = bref.DynamicBlockReferencePropertyCollection;
                                    foreach (DynamicBlockReferenceProperty dynBrefProps in dynBrefColl)
                                    {
                                        // Jeff_M code
                                        if (dynBrefProps.PropertyTypeCode == 5)
                                        {
                                            dynBlks.VisibilityProperties.Add(dynBrefProps.Value);
                                        }
                                    }
                                }
                                dynamicBlocks.Add(dynBlks);
                            }
                            ///////////////////////////////////End Add/////////////////////////////
                            //Add Block to list of blocks
                            blockNames.Add(btr.Name);

                            //Do Cloning
                            ObjectIdCollection objIdCollection = new ObjectIdCollection();
                            objIdCollection.Add(btr.ObjectId);
                            IdMapping idMap = new IdMapping();
                            openDatabase.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);

                        }
                        openDbTrx.Commit();
                    }//End Transaction with Open Document's Database
                    openDatabase.Dispose(); // Dispose Database
                }// End foreach
                trx.Commit();
            }// End Lock on Active Document & Transaction with Active Document's Database

            ///////////////////////////////////Start Add/////////////////////////////
            using (DocumentLock doclock = doc.LockDocument())
            using (Transaction trx = db.TransactionManager.StartTransaction()) // Start Transaction with Active Document's Database
            {
                BlockTable bt = trx.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                BlockTableRecord ms = bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite) as BlockTableRecord;
                Random rdn = new Random();
                foreach (DynamicBlocks dynBlks in dynamicBlocks)
                {
                    foreach (string visi in dynBlks.VisibilityProperties)
                    {
                        BlockTableRecord btr = bt[dynBlks.BlockName].GetObject(OpenMode.ForRead) as BlockTableRecord;
                        double rdnDouble = rdn.Next(100);
                        BlockReference bref = new BlockReference(new Point3d(rdnDouble, rdnDouble, 0.0), btr.ObjectId);
                        ms.AppendEntity(bref);
                        trx.AddNewlyCreatedDBObject(bref, true);

                        DynamicBlockReferencePropertyCollection dynBrefColl = bref.DynamicBlockReferencePropertyCollection;
                        foreach (DynamicBlockReferenceProperty dynBrefProps in dynBrefColl)
                        {
                            if (dynBrefProps.PropertyTypeCode == 5)
                            {
                                dynBrefProps.Value = visi;
                            }
                        }                 

                    }
                   
                }

                trx.Commit();
            }
            ///////////////////////////////////End Add/////////////////////////////
        }
« Last Edit: March 16, 2011, 03:10:36 AM by Jeff H »

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #20 on: March 17, 2011, 12:03:37 AM »
Had a chance to play around tonight. Your last code worked for the most part...I had to switch around the Dynamic block code to before the other conditions, otherwise if a DB had been inserted and it had been altered (dynamic property changed) leaving no BlockReferences being shown, then the DB portion of code never got touched. Doing the switch corrected that.

Then I thought to try that working code and change it to set each doc current like I need. It now fails...so there's definitely an issue with setting documents current then coming back to the start document. I think this is where I need to focus my research on now.

Thank you much for your time and insight, Jeff. You've also shown me alternate (better?) ways of going about some things that will help me in the future. Much appreciated.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #21 on: March 17, 2011, 01:40:19 AM »
The last part was not implemented very well and that is weird the basic funtionality differs between C3D and ACAD.

I am sure you got an idea what you are going to do but when I get a chance will post what I come up with changing active drawings.



You are creating a type of symbol legend?


Take care and have a good one.

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #22 on: March 17, 2011, 02:01:15 AM »
A Legend, yes, of just blocks actually inserted into the chosen or open drawings.

C3D is different in that the API (both .NET and COM) were written such that you can only access certain properties and use methods when the drawing the C3D object exists in is current. And since blocks are used to display Point Symbols and Structures (Manhole type structures), in order to find which blocks are in use by these objects I need the dwg current.

One other thing I'm not quite getting....on some drawings it works just fine (my original code), but most others it breaks the drawing which starts the command. It's like there's a limit to how many table entries get added (block/text/layer, these all get added if not in the target drawing but are in use by the block definition). I've got enough now to submit this to ADN, perhaps they can clue me in on what can be done.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #23 on: March 17, 2011, 02:10:10 AM »
I know WblockCloneObjects will fail on some MEP objects and you use a CloningHelper class
might be similar?

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Help with WBlockCloneObjects
« Reply #24 on: March 17, 2011, 10:09:07 AM »
I remember Tony T telling me that .Net could not set a document current within a code.  Current document does not mean the active one in reality.  I wonder if that is the issue you are having, and if it is, if you can ever hope to get it working with .Net.  You can set a document current with Arx though.  I'll see if I can find the post.  I think it's on the Adesk site.

Maybe a P/Invoke would work?

Edit:  Found post.  Look at his second in this thread.
[ http://forums.autodesk.com/t5/NET/Updating-blocks-in-open-non-current-drawing/m-p/2678788/highlight/true#M19093 ]
« Last Edit: March 17, 2011, 10:34:26 AM by T.Willey »
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #25 on: March 17, 2011, 12:05:30 PM »
^
did a quick test with some of my old code in arx, and have been able to generate the table from all open drawings and to insert the table in the current one, don't have all my new code for tables (still it is on my all broke pc), just noticed that I don't get the block previews from the non current drawings, edit: forgot to add the cloning process, it returns the block name, and the block count on the drawings.
« Last Edit: March 17, 2011, 12:23:08 PM by LE »

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #26 on: March 17, 2011, 01:45:36 PM »
Hi Jeff_M,

and here it is a binary file to run a test under 2010 or 2011 and see if works in c3d, it is a quick one no more than 20-30 minutes of coding...

Steps:
1. Load instable18.arx
2. Open drawings with blocks
3. Run from your current drawing: INSTABLE and select the insertion point for the table.
4. ***Press <RETURN> to erase cloned blocks...  this will erase the temp cloned blocks from the current drawing - might find a better aproach for this, but it is just a sample of how can be possible done from the arx side.

5. For the source code, in case it is useful, PM me.
6. HTH.

edit: updated without the manifest file
« Last Edit: March 17, 2011, 05:35:47 PM by LE »

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #27 on: March 17, 2011, 05:25:03 PM »
Thanks, Luis. Only have a few minutes before I must leave for a meeting, but first attempt in 2011:

instable18.arx is incompatible with this version of AutoCAD.
AcRxDynamicLinker failed to load 'c:\users\jeff-i7\downloads\instable18.arx'
C:\Program Files\Autodesk\AutoCAD Civil 3D 2011\acad.exeUnable to load
instable18.arx file.

Off to try 2010.....

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #28 on: March 17, 2011, 05:33:01 PM »
Oops... must be the 'manifest file' that it is set to on... let me recompile it.

Thanks, Luis. Only have a few minutes before I must leave for a meeting, but first attempt in 2011:

instable18.arx is incompatible with this version of AutoCAD.
AcRxDynamicLinker failed to load 'c:\users\jeff-i7\downloads\instable18.arx'
C:\Program Files\Autodesk\AutoCAD Civil 3D 2011\acad.exeUnable to load
instable18.arx file.

Off to try 2010.....

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #29 on: March 17, 2011, 05:33:54 PM »
...C3D2010 it works, Luis. This is quite fast, but it looks like it uses the same idea Jeff_H used of not making each drawing current. If so, the .NET version works, too, but not quite as quickly.

Tim, thanks for that link. I'm going to have to look into that some more, but I'm not sure it applies since I'm not altering anything graphical in the other databases. But it may be a related issue, I guess.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Help with WBlockCloneObjects
« Reply #30 on: March 17, 2011, 05:42:50 PM »
I was thinking it was the same as in the issue of making a document current.  Not sure if changing the document to active within the editor makes it current, when another document is what called the program, and that is in essence the current document.  That is how the thinking went on in my head at least.

Quote from: Arx help: Accessing the Current Document and Its Related Objects
The key call an ObjectARX application must make when it gains control is to find out the current document, which can be accomplished with the function acDocManager->curDocument().

Note  The current document is not always the active document. This is the case during transitional states, such as when the documentToBeActivated() reactor occurs. Do not attempt extensive processing during transitional states. Consider using mdiActiveDocument() if you are interested in the active document.
From the current document, you can determine the current database, the relevant transaction manager, and your application's associated document-specific state, and then do whatever needs to be done before returning.

Once a command has stored the current document and associated information on its stack, it does not need to query the current document again until completion. Whenever a prompt for user input is made, the user can switch documents, but if that is done, the current command is suspended and its stack state is saved until the document is reactivated.

If your application is operating from the application execution context, it must lock and unlock the current document to modify anything associated with it. It can do so by directly invoking the AcApDocManager::lockDocument() and unlockDocument() member function.

If your application is operating from an ObjectARX or AutoLISP function, no locking should be necessary, as the system establishes the locks and removes them automatically around commands and AutoLISP expressions.
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Help with WBlockCloneObjects
« Reply #31 on: March 17, 2011, 05:43:17 PM »
I remember Tony T telling me that .Net could not set a document current within a code.  Current document does not mean the active one in reality.  I wonder if that is the issue you are having, and if it is, if you can ever hope to get it working with .Net.  You can set a document current with Arx though.  I'll see if I can find the post.  I think it's on the Adesk site.

Maybe a P/Invoke would work?

Edit:  Found post.  Look at his second in this thread.
[ http://forums.autodesk.com/t5/NET/Updating-blocks-in-open-non-current-drawing/m-p/2678788/highlight/true#M19093 ]

It's unfortunate that Tony's posts at Augi are not displaying.



Hi Jeff_M,

and here it is a binary file to run a test under 2010 or 2011 and see if works in c3d, it is a quick one no more than 20-30 minutes of coding...
< .. >

Luis, What does your solution entail ?
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #32 on: March 17, 2011, 05:44:10 PM »
...C3D2010 it works, Luis. This is quite fast, but it looks like it uses the same idea Jeff_H used of not making each drawing current. If so, the .NET version works, too, but not quite as quickly.
From arx you can do something like(have not tried the same from C#):
Code: [Select]
AcApDocumentIterator *pIter = acDocManager->newAcApDocumentIterator();
for (; !pIter->done();)
{
AcApDocument *pDoc = pIter->document();
// avoid target drawing - grab ids from the other ones opened...
if (pCurDoc != pDoc)
{
if (acDocManager->setCurDocument(pDoc) == Acad::eOk && acDocManager->lockDocument(pDoc) == Acad::eOk)
{
ids.removeAll();
ScanForAllBlockReference(ids);
if (ids.length() >0)
{
for (int i = 0; i < ids.length(); i++)
{
allIds.append(ids[i]);
}
}
acDocManager->unlockDocument(pDoc);
}
}
pIter->step();
}
delete pIter;

Then from the above, you call: wblockCloneObjects on the current drawing, create the table and do the insert.

LE3

  • Guest
Re: Help with WBlockCloneObjects
« Reply #33 on: March 17, 2011, 05:47:53 PM »
Luis, What does your solution entail ?
Just posted the main part of the code.

I also knew about Tony wrapper solution, but it is not avail anymore...

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Help with WBlockCloneObjects
« Reply #34 on: March 18, 2011, 12:08:03 AM »
Could the database used to call member function WblockCloneObjects cause problems?

I have done litte testing but worked fine for each scnerio.

It did not matter which Database I used

From post http://www.theswamp.org/index.php?topic=37401.msg425024#msg425024
Code: [Select]
                            ObjectIdCollection objIdCollection = new ObjectIdCollection();
                            objIdCollection.Add(btr.ObjectId);
                            IdMapping idMap = new IdMapping();
                            openDatabase.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);
replacing openDatabase with wb
Code: [Select]
                          db.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);


Worked for switching active document and did it with db and openDatabase for calls to member function WblockCloneObjects

Code: [Select]
[CommandMethod("ImportBlocks3", CommandFlags.Session)]
        public void ImportBlocks3()
        {
            List<string> blockNames = new List<string>(); // A generic list to keep up with all blocks to check later if a block has already inserted.

            DocumentCollection documents = Application.DocumentManager; // All the current open documents

            Document doc = Application.DocumentManager.MdiActiveDocument; // Active Document
            Editor ed = doc.Editor; // Active Document's Editor
            Database db = doc.Database; // Active Document Database

            Document openDocument; // Temporay variable to hold each non-active Document
            Database openDatabase; //Temporay variable to hold each non-active Document's Database

            using (DocumentLock doclock = doc.LockDocument()) // Start Lock on Active Document
            using (Transaction trx = db.TransactionManager.StartTransaction()) // Start Transaction with Active Document's Database
            {
                // Start looping through each document
                foreach (Document document in documents)
                {
                    openDocument = document;
                    string fileName = openDocument.Name;
                    openDatabase = openDocument.Database;

                    // If the document is the current or Active document skip it
                    if (doc.Name == fileName || fileName == null || fileName == String.Empty)
                        continue;

                    documents.MdiActiveDocument = openDocument;
                    using (Transaction openDbTrx = openDatabase.TransactionManager.StartTransaction())
                    {//Start Transaction with Open Document's Database

                        BlockTable openBlockTable = openDbTrx.GetObject(openDatabase.BlockTableId, OpenMode.ForRead) as BlockTable;

                        // Loop through each BlockTableRecord
                        foreach (ObjectId btrId in openBlockTable)
                        {
                            BlockTableRecord btr = openDbTrx.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                            // Make sure it is not a ModelSpace or any of the PaperSpaces, any type of Xref
                            // or MEP type Object, or has already been clone before etc.... & and the block has been inserted at least once
                            if (btr.IsLayout || btr.IsAnonymous || btr.IsFromExternalReference || btr.IsFromOverlayReference || blockNames.Contains(btr.Name)
                                || btr.Name.StartsWith("Aec", StringComparison.OrdinalIgnoreCase) || btr.Name.StartsWith("_")
                                || btr.GetBlockReferenceIds(true, false).Count < 1)
                                continue;
                            //Add Block to list of blocks
                            blockNames.Add(btr.Name);

                            //Do Cloning
                            ObjectIdCollection objIdCollection = new ObjectIdCollection();
                            objIdCollection.Add(btr.ObjectId);
                            IdMapping idMap = new IdMapping();
                            openDatabase.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);

                        }
                        openDbTrx.Commit();
                    }//End Transaction with Open Document's Database
                    openDatabase.Dispose(); // Dispose Database
                }// End foreach
                trx.Commit();
            }// End Lock on Active Document & Transaction with Active Document's Database
            documents.MdiActiveDocument = doc;
        }

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: Help with WBlockCloneObjects
« Reply #35 on: March 24, 2011, 01:46:06 PM »
Well I submitted this to ADN late last week. After 2 techs looked at it, it was concluded that Civil3D (and, presumably, all the verticals that use the Aec* base) needs to have the target drawing for the blocks to be current at the time of the WblockCloneObjects().  I still have a bunch of testing to do with my full code, but initial tests show that this does solve the problem.

Thanks to everyone for their input!

Code: [Select]
[CommandMethod("InsertBlocks", CommandFlags.Session)]
        static public void InsertBlocks()
        {
            List<string> blockNames = new List<string>();
            DocumentCollection documents = Application.DocumentManager;
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db  = doc.Database;

            foreach (Document openDocument in documents)
            {
                ObjectIdCollection objIdCollection = new ObjectIdCollection();
                IdMapping idMap = new IdMapping();

                string fileName = openDocument.Name;

                if (doc.Name == fileName || fileName == null || fileName == String.Empty)
                    continue;

                // Needed for Civil 3D since it works on the active document.
                Application.DocumentManager.MdiActiveDocument = openDocument;

                using (Transaction openDbTrx = openDocument.Database.TransactionManager.StartTransaction())
                {
                    BlockTable openBlockTable = openDbTrx.GetObject(openDocument.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    foreach (ObjectId btrId in openBlockTable)
                    {
                        BlockTableRecord btr = openDbTrx.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;

                        if (btr.IsLayout || btr.IsAnonymous || btr.IsFromExternalReference || btr.IsFromOverlayReference || blockNames.Contains(btr.Name)
                            || btr.Name.StartsWith("Aec", StringComparison.OrdinalIgnoreCase) || btr.Name.StartsWith("_")
                            || btr.GetBlockReferenceIds(true, false).Count < 1)
                            continue;

                        blockNames.Add(btr.Name);
                        objIdCollection.Add(btr.ObjectId);
                    }
                    openDbTrx.Commit();
                }

                using (DocumentLock doclock = doc.LockDocument())
                {
                    // Needed for Civil 3D since the destination database has to be active.
                    Application.DocumentManager.MdiActiveDocument = doc;
                    openDocument.Database.WblockCloneObjects(objIdCollection, db.BlockTableId, idMap, DuplicateRecordCloning.Ignore, false);
                }
            }
        }