Author Topic: Updating blocks in an opened drawing  (Read 3748 times)

0 Members and 1 Guest are viewing this topic.

T.Willey

  • Needs a day job
  • Posts: 5251
Updating blocks in an opened drawing
« on: April 12, 2010, 01:18:37 PM »
I'm working on a block replacement code.  It will update blocks in any drawing ( opened/unopened, current/non-current ).  It works well with unopened drawings right now.  It works with opened drawings, but not as well as I would like to release it.  The issue seems to be with attributes.  It will not update them correctly all the time, and I don't know why.

RedefiningBlock = a custom class that holds information about the redefining of the block.

Here is the main portion of the code that does the updating.
Code: [Select]
void ProceedButton ( object sender, EventArgs e )
{
    DocumentCollection DocCol = AcadApp.DocumentManager;
    Database cDb = DocCol.MdiActiveDocument.Database;
    Editor Ed = DocCol.MdiActiveDocument.Editor;
    foreach ( KeyValuePair<string, List<RedefiningBlock>> kvp in FinalMappingDict ) {
        Database reDb;
        Document reOpenDoc = null;
        DocumentLock reDocLock = null;
        if ( kvp.Value.Count == 0 ) continue;
        reOpenDoc = MyUtility.GetDocumentFrom( DocCol, kvp.Key );
        if ( reOpenDoc == null ) {
            reDb = new Database( false, true );
            reDb.ReadDwgFile( kvp.Key, FileShare.ReadWrite, true, null );
            HostApplicationServices.WorkingDatabase = reDb;
        }
        else {
            reDocLock = reOpenDoc.LockDocument();
            reDb = reOpenDoc.Database;
        }
        using ( Transaction reTrans = reDb.TransactionManager.StartTransaction() ) {
            foreach ( RedefiningBlock rb in kvp.Value ) {
                Database deDb;
                Document deOpenDoc = null;
                DocumentLock deDocLock = null;
                ObjectId reObjId = MyUtility.GetNonErasedTableRecordId( reDb.BlockTableId, rb.Name );
                if ( reObjId.IsNull ) continue;
                BlockTableRecord reBlkTblRec = reTrans.GetObject( reObjId, OpenMode.ForWrite ) as BlockTableRecord;
                foreach ( ObjectId id in reBlkTblRec ) {
                    reTrans.GetObject( id, OpenMode.ForWrite ).Erase();
                }
                deOpenDoc = MyUtility.GetDocumentFrom( DocCol, rb.RedefiningDrawingPath );
                if ( deOpenDoc == null ) {
                    deDb = new Database( false, true );
                    deDb.ReadDwgFile( rb.RedefiningDrawingPath, FileShare.ReadWrite, true, null );
                }
                else {
                    deDocLock = deOpenDoc.LockDocument();
                    deDb = deOpenDoc.Database;
                }
                ObjectId deObjId = MyUtility.GetNonErasedTableRecordId( deDb.BlockTableId, rb.RedefiningName );
                if ( deObjId == null ) continue;
                using ( Transaction deTrans = deDb.TransactionManager.StartTransaction() ) {
                    ObjectIdCollection ObjIdCol = new ObjectIdCollection();
                    BlockTableRecord deBlkTblRec = deTrans.GetObject( deObjId, OpenMode.ForRead ) as BlockTableRecord;
                    foreach ( ObjectId id in deBlkTblRec ) {
                        ObjIdCol.Add( id );
                    }
                    deDb.WblockCloneObjects( ObjIdCol, reBlkTblRec.ObjectId, new IdMapping(), DuplicateRecordCloning.Ignore, true );
                }
                MapAttributes( reBlkTblRec.ObjectId, rb.AttributeMapping );
                if ( deOpenDoc == null )
                    deDb.Dispose();
                else
                    deDocLock.Dispose();
            }
            reTrans.Commit();
        }
        if ( HostApplicationServices.WorkingDatabase != cDb )
            HostApplicationServices.WorkingDatabase = cDb;
        if ( reOpenDoc == null ) {
            reDb.RetainOriginalThumbnailBitmap = true;
            reDb.SaveAs( kvp.Key, DwgVersion.Current );
            Ed.WriteMessage( "{0} Saved file: {1}", System.Environment.NewLine, reDb.Filename );
            reDb.Dispose();
        }
        else
            reDocLock.Dispose();
    }
    this.Close();
}

Here is the portion that updates the attributes.
Code: [Select]
void MapAttributes ( ObjectId id, Dictionary<string, string> mappingDict )
{
    using ( Transaction Trans = id.Database.TransactionManager.StartTransaction() ) {
        BlockTableRecord BlkTblRec = Trans.GetObject( id, OpenMode.ForRead ) as BlockTableRecord;
        foreach ( ObjectId objId in BlkTblRec.GetBlockReferenceIds( true, true ) ) {
            if ( objId.IsErased ) continue;
            Dictionary<string, string> Dict = new Dictionary<string, string>();
            BlockReference BlkRef = Trans.GetObject( objId, OpenMode.ForWrite ) as BlockReference;
            foreach ( ObjectId attId in BlkRef.AttributeCollection as AttributeCollection ) {
                if ( attId.IsErased ) continue;
                AttributeReference AttRef = Trans.GetObject( attId, OpenMode.ForWrite ) as AttributeReference;
                if ( mappingDict.ContainsKey( AttRef.Tag ) )
                    Dict.Add( mappingDict[ AttRef.Tag ], AttRef.TextString );
                AttRef.Erase();
            }
            if ( BlkTblRec.HasAttributeDefinitions ) {
                foreach ( ObjectId attId in BlkTblRec ) {
                    if ( attId.IsErased ) continue;
                    AttributeDefinition AttDef = Trans.GetObject( attId, OpenMode.ForRead ) as AttributeDefinition;
                    if ( AttDef != null ) {
                        AttributeReference AttRef = new AttributeReference();
                        AttRef.SetAttributeFromBlock( AttDef, BlkRef.BlockTransform );
                        BlkRef.AttributeCollection.AppendAttribute( AttRef );
                        Trans.AddNewlyCreatedDBObject( AttRef, true );
                        if ( Dict.ContainsKey( AttRef.Tag ) )
                            AttRef.TextString = Dict[ AttRef.Tag ];
                    }
                }
            }
        }
        Trans.Commit();
    }
}

Acad = 2009
.Net = 2.0

If anything is needed let me know.  Thanks in advance.
Tim

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

Please think about donating if this post helped you.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #1 on: April 28, 2010, 04:04:40 PM »
I found one issue, but have another one.  The problem was in the ' MapAttributes ' portion, I didn't downgrade the BlkRef variable.  Once I did that, I was able to update the blocks.

Now I have run into another error.  It seems that the objects I import from another drawing, are getting corrupted somehow.  I will try some other methods besides the ' WBlockCloneObjects '.  Here is what the audit command returns when copying a block that has a circle, line, and two attributes.

Any ideas are appreciated.

Quote
Pass 2 400     objects auditedAcDbAttributeDefinition(2D4)
                    Invalid layer eInvalidInput         $AUDIT-BAD-LAYER
AcDbAttributeDefinition(2D4)      was not repaired.
AcDbAttributeDefinition(2D5)
                    Invalid layer eInvalidInput         $AUDIT-BAD-LAYER
AcDbAttributeDefinition(2D5)      was not repaired.
AcDbCircle(2D6)     Invalid layer eInvalidInput         $AUDIT-BAD-LAYER
AcDbCircle(2D6)                   was not repaired.
AcDbLine(2D7)       Invalid layer eInvalidInput         $AUDIT-BAD-LAYER
AcDbLine(2D7)                     was not repaired.
Tim

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

Please think about donating if this post helped you.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #2 on: April 28, 2010, 06:12:41 PM »
I tested something out, and it worked.  I must not have understood what this means from the arx docs ( not sure that I understand it now even ).

Quote
Input Boolean indicating whether or not ID translation should be done

I had it set to true, because I wanted the ID's to translate to the new database, but once I set it to false, it didn't corrupt the objects being copied over.  So the line now looks like

Code: [Select]
deDb.WblockCloneObjects( ObjIdCol, reBlkTblRec.ObjectId, new IdMapping(), DuplicateRecordCloning.Ignore, false );

Can someone help me understand why it should be false?

I think I got it now.  I was looking at the description of that argument only, and not what they named it.  They named it ' deferTranslation '.  Now I'm saying, ' No I don't want to defer the translation to another point, or action.  Translate the id now.'  Is that correct?
Tim

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

Please think about donating if this post helped you.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8783
  • AKA Daniel
Re: Updating blocks in an opened drawing
« Reply #3 on: April 28, 2010, 07:15:17 PM »
It kind of skips that argument in the Arx docs, the 'Translation Phase' kind of explains what's going on,
but I don't know in what cases you would want to defer this... maybe if you want to party on the IdMapping


T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #4 on: April 29, 2010, 11:05:36 AM »
Thanks Daniel.  Maybe I'll check out the ' Translation Phase ', and see what I can learn.
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: Updating blocks in an opened drawing
« Reply #5 on: April 29, 2010, 11:27:23 AM »
not that i know... but on the old arx docs, there was a (possible) better description or more wording (taken from the 2000 migration guide).
Quote
Acad::ErrorStatus
wblockCloneObjects(
AcDbObjectIdArray& objectIds,
AcDbObjectId& owner,
AcDbIdMapping& idMap,
AcDb::DuplicateRecordCloning type,
Adesk::Boolean deferXlation = Adesk::kFalse);

objectIds List of object IDs of the objects to be cloned into the owner object.

owner Object ID of the new owner. This must be a valid

container, an AcDbDictionary for objects, or anAcDbBlockTableRecord for entities.

idMap The AcDbIdMapping object to use for the cloning.

type The DuplicateRecordCloning value to use for the cloning.
There are four acceptable values:
n AcDb::kDrcIgnore – Duplicate symbols are not cloned.
This is similar to how the INSERT command behaves.
n AcDb::kDrcReplace – Duplicate symbols are cloned and replace the existing symbol in the destination database.
n AcDb::kDrcMangleName – Duplicate symbol names are
mangled with $0$ prepended.
n AcDb::kDrcUnMangleName – Similar to kDrcIgnore
except that any mangled names found will first have
their mangling removed. This is the mode used during
reference checkin.

deferXlation Just like deepCloneObjects(), if Adesk::kTrue, the
deepCloneXlation is not performed before returning. This
makes it possible to call the function multiple times with
different owner objects. The final call must have
deferXlation set to Adesk::kFalse.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #6 on: April 29, 2010, 12:00:02 PM »
Thanks Luis.  I think with that, and the pages Daniel mentioned, I think I understand a little better now why it needs to be set to false.
Tim

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

Please think about donating if this post helped you.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #7 on: April 29, 2010, 01:33:13 PM »
I'm having two issues now in an open drawing that isn't current.  The blocks get updated, but the new versions of the blocks are not being represented ( a regen fixes the problem ), and the new attributes are not showing in the correct location ( a move fixes the problem ).

I thought that if you made the database the working database then the attributes would be updated correctly?  This does not seem to be the case when a drawing is opened but not current.  How can I fix this?

I have used the draw method on the block references, and they update nicely in the current open drawing, but not in a non-current drawing.  I have tried to use the QueueForGraphicsFlush method of the TransactionManager, but that doesn't work unless it is the current document, at least that was the error I got.  I tried the Regen method of the Editor, but that only regened the current document.  Does anyone have any other suggestions?

Thanks in advance.  This is almost ready for beta testing.  I want to get these things figured out first though.
Tim

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

Please think about donating if this post helped you.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #8 on: May 06, 2010, 06:31:59 PM »
Apparently this is an Autodesk oversight/skip when making the managed wrapper classes.  Tony T. has pointed me to a thread he had on the AUGI site about this.  Looks like the only want to do it is with a mixed managed solution.  Here is the link where he gives an example of the C++/Arx code.

[ http://forums.augi.com/showthread.php?t=107464 ]

 :cry:

This issue is that you need to set the document current, and you can't do that in .Net.
Tim

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

Please think about donating if this post helped you.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8783
  • AKA Daniel
Re: Updating blocks in an opened drawing
« Reply #9 on: May 06, 2010, 07:15:09 PM »
I bet you can p/invoke that, I will try

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8783
  • AKA Daniel
Re: Updating blocks in an opened drawing
« Reply #10 on: May 06, 2010, 10:06:17 PM »
sorry, I don't see it as being exported, let me know if you need help building a mixed module 

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #11 on: May 07, 2010, 10:59:09 AM »
sorry, I don't see it as being exported, let me know if you need help building a mixed module 

Thanks for looking into it Daniel.  If I was to go with a mixed module, would that be version specific?  I think it would, but I know nothing about that, but if so, then I think I'll find another way around it, like making the other document the active document while updating the blocks, or just not allow it to happen.
Tim

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

Please think about donating if this post helped you.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8783
  • AKA Daniel
Re: Updating blocks in an opened drawing
« Reply #12 on: May 07, 2010, 12:38:56 PM »
Right, It would be version specific just like an ARX routine. 

T.Willey

  • Needs a day job
  • Posts: 5251
Re: Updating blocks in an opened drawing
« Reply #13 on: May 07, 2010, 01:03:26 PM »
Cool.  Thanks for the info Daniel.
Tim

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

Please think about donating if this post helped you.