Author Topic: Open, Bind and Close  (Read 11365 times)

0 Members and 1 Guest are viewing this topic.

mcarson

  • Guest
Open, Bind and Close
« on: August 19, 2008, 09:44:00 AM »
Some of our guys have made the mistake of binding the xrefs into the current drawing and saving; overwriting the current file. It has been asked if it is possible to create a command that binds all the loaded xrefs into the current drawing, saving as a different file.
That's not really the problem. The guys are needing the original drawing (unbound) kept open/reopened in the editor after closing the bound version!

I am using VS2005, .NET 2.0 and AutoCAD 2008.

Code to bind all xrefs into the current drawing: (converted from VB)

Code: [Select]
public static bool BindAllXRefs()
{
    //Get the active document
    Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    //Get the active database
    Database db = doc.Database;
    //Get the xref graph
    XrefGraph xrefg = db.GetHostDwgXrefGraph(true);
    //Check if the xref graph is empty or contains just the current drawing
    if (xrefg.IsEmpty || xrefg.NumNodes == 1) {
        return false;
        return; // TODO: might not be correct. Was : Exit Function
    }
   
    //Start a new transaction to get the exreferences
    using (Transaction tr = doc.TransactionManager.StartTransaction) {
        //Get the current block table
        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
       
        //Loop through all the xrefs in the block table
        for (int i = 1; i <= xrefg.NumNodes - 1; i++) {
            //Get the graph node
            XrefGraphNode xrefn = (XrefGraphNode)xrefg.GetXrefNode(i);
            //Only bind loaded xrefs
           
            if (!(xrefn.XrefStatus == XrefStatus.Unloaded)) {
                //Get the blocktable record
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(xrefn.BlockTableRecordId, OpenMode.ForWrite, false);
                //Get any inserted xrefs
                ObjectIdCollection BlkRefCollection = btr.GetBlockReferenceIds(true, true);
                //If the block exists then add and bind
                if (BlkRefCollection.Count > 0) {
                    ObjectIdCollection ObjIdCollection = new ObjectIdCollection();
                    //Add the object id to the collection
                    ObjIdCollection.Add(btr.Id);
                    //Do the bind
                    db.BindXrefs(ObjIdCollection, true);
                }
                else {
                    //Detach the xrefs if not loaded
                    db.DetachXref(btr.Id);
                }
            }
        }
        tr.Commit();
        return true;
    }
}

I tried the following 'horrible' workaround attempt; the idea of which was to work on a temporary copy of the drawing:

Code: [Select]
public static bool BindDrawing(string OpenFileName, string SaveFileName)
{
    //Open the database transparently
    string tempname = "C:\\temp.dwg";
    System.IO.File.Copy(OpenFileName, tempname);
    using (Database db = new Database(false, true)) {
        //Read in the drawing file
        db.ReadDwgFile(tempname, System.IO.FileShare.Read, true, null);
        //Set the current working database
        if (db != HostApplicationServices.WorkingDatabase) {
            HostApplicationServices.WorkingDatabase = db;
        }
        //Get the xref graph
        XrefGraph xrefg = db.GetHostDwgXrefGraph(true);
        //Check if the xref graph is empty (contains just the current drawing)
        if (xrefg.IsEmpty || xrefg.NumNodes == 1) {
            //We don't need to bind, so discard the database
            db.Dispose();
            return true;
            return; // TODO: might not be correct. Was : Exit Function
        }
        //Drawing has ex-references
        //Start a new transaction
        using (Transaction tr = db.TransactionManager.StartTransaction) {
            //Get the block table of the database
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false);
            //Loop through the records in the block table to get all the xrefs
            for (int i = 1; i <= xrefg.NumNodes - 1; i++) {
                //Get the graph node
                XrefGraphNode xrefn = (XrefGraphNode)xrefg.GetXrefNode(i);
                //Only bind the loaded xrefs
                if (!(xrefn.XrefStatus == XrefStatus.Unloaded)) {
                    //Get the block table record that matches the xref node reference
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(xrefn.BlockTableRecordId, OpenMode.ForWrite, false);
                    ObjectIdCollection BlkRefCollection = btr.GetBlockReferenceIds(true, true);
                    //If the xref exists as a block table record then add and bind
                    if (BlkRefCollection.Count > 0) {
                        ObjectIdCollection ObjIDCollection = new ObjectIdCollection();
                        //Add the object id to the collection
                        ObjIDCollection.Add(btr.Id);
                        //Do the bind
                        db.BindXrefs(ObjIDCollection, true);
                    }
                    else {
                        //Detach the xref if not loaded
                        db.DetachXref(btr.Id);
                    }
                }
            }
            tr.Commit();
        }
        db.RetainOriginalThumbnailBitmap = true;
        db.SaveAs(SaveFileName, DwgVersion.Current);
        db.CloseInput(true);
       
    }
    HostApplicationServices.WorkingDatabase = AppServices.DocumentManager.MdiActiveDocument.Database;
}
Some of the code used in the second method came from this site (Thanks Draftek).

This does not work as expected, in fact it doesn't bind in some cases!

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #1 on: August 19, 2008, 09:52:26 AM »
It's probably not binding when it strikes a nested xref.

The way I would try to do this off the top of my head is:

1. Issue command in drawing to bind.
2. Save the current drawing or prompt user to save drawing before running command.
3. Create a new dbase and use ReadDwgFile to read the current drawing into the new dbase.
4. Do Bind Mojo
5. Probably use COM SaveAs to save in-memory dbase to another file.
6. Destroy temp dbase.
Me

mcarson

  • Guest
Re: Open, Bind and Close
« Reply #2 on: August 19, 2008, 11:01:02 AM »
I think I understand.
The drawing I am using for test purposes contains no nested xrefs and binds perfectly with the first method. I am able to do a save as, but I cannot CloseAndDiscard() 'Drawing is busy'
The second method doesn't allow me to bind. No errors, just strange behavior! Only difference is using the in-memory database.
If I was to readDwgFile the current open file, wouldn't I get a file lock error?

Maybe this:
Save the current drawing.
Issue the Bind Command.
Save the bound drawing as 'whatever'
Open the original drawing
Close the bound drawing.

I cannot seem to get my head around this. Do I have to start the command in the ApplicationContext? If so, how do I make sure I don't hit a zero-document interface.

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #3 on: August 19, 2008, 11:33:01 AM »
Drawing A is open in the editor and has xrefs you want to bind.

1. Issue your command and bail if not saved.
2. Assuming drawing is saved, readdwg the file corresponding to drawing A into memory.
3. Issue a ResolveXrefs call on this new dbase.
4. Bind away.
5. SaveAs
6. Destroy.

I don't see a need for this to be ApplicationContext. Also, you will not hit a zero document state as drawing A is still open in the editor.
Me

mcarson

  • Guest
Re: Open, Bind and Close
« Reply #4 on: August 19, 2008, 04:05:33 PM »
Thanks for clarifying
Success - in part

I coded the following method (quickly)
Code: [Select]
public void BindXReferences()
{
    //Get the current document
    AppServices.Document doc = AppServices.Application.DocumentManager.MdiActiveDocument;
    //Get the active document's database
    Database db = doc.Database;
    //Save the drawing
    //db.Save()
    //Load a copy of the database into memory
    Database newdb = new Database(true, true);
    newdb.ReadDwgFile(doc.Name, IO.FileShare.ReadWrite, false, null);
    //resolve the xrefs in the newly created database
    newdb.ResolveXrefs(false, false);
    //now bind the xrefs
    //get the xref graph
    XrefGraph xrefg = newdb.GetHostDwgXrefGraph(true);
    //check if the xref graph contains more than just the current drawing
    if (xrefg.IsEmpty || xrefg.NumNodes == 1) {
        newdb.Dispose();
        return; // TODO: might not be correct. Was : Exit Sub
    }
   
    //Start a new transaction to get the exreferences
    using (Transaction tr = newdb.TransactionManager.StartTransaction) {
        //Get the current block table
        BlockTable bt = (BlockTable)tr.GetObject(newdb.BlockTableId, OpenMode.ForRead);
        for (int i = 1; i <= xrefg.NumNodes - 1; i++) {
           
            //Loop through all the xrefs in the block table
            //Get the graph node
            XrefGraphNode xrefn = (XrefGraphNode)xrefg.GetXrefNode(i);
            //Only bind loaded xrefs
           
            if (!(xrefn.XrefStatus == XrefStatus.Unloaded)) {
                //Get the blocktable record
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(xrefn.BlockTableRecordId, OpenMode.ForWrite, false);
                //Get any inserted xrefs
                ObjectIdCollection BlkRefCollection = btr.GetBlockReferenceIds(true, true);
                //If the block exists then add and bind
                if (BlkRefCollection.Count > 0) {
                    ObjectIdCollection ObjIdCollection = new ObjectIdCollection();
                    //Add the object id to the collection
                    ObjIdCollection.Add(btr.Id);
                    //Do the bind
                    newdb.BindXrefs(ObjIdCollection, true);
                }
                else {
                    //Detach the xrefs if not loaded
                    newdb.DetachXref(btr.Id);
                }
            }
        }
        tr.Commit();
    }
    //Save the database
    newdb.SaveAs("C:\\Documents and Settings\\Mark\\Desktop\\test\\final.dwg", DwgVersion.Current);
    //Destroy it
    newdb.Dispose();
}

Am I missing something? The bind doesn't work!

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #5 on: August 19, 2008, 04:16:09 PM »
It's not binding because the ObjectIdCollection that 'BindXrefs' expects, is id's of BlockTableRecords, NOT, BlockReferenceIds.
Me

mcarson

  • Guest
Re: Open, Bind and Close
« Reply #6 on: August 19, 2008, 04:20:10 PM »
 :oops: Thanks for the correction - I think?
Wrote the following SimpleBind to demonstrate what's happening...

Code: [Select]
public void SimpleBind()
{
    //Load a copy of the database into memory
    Database newdb = new Database(true, true);
    newdb.ReadDwgFile("C:\\Documents and Settings\\Mark\\Desktop\\test\\main.dwg", IO.FileShare.ReadWrite, false, null);
    //resolve the xrefs in the newly created database
    newdb.ResolveXrefs(false, false);
    //now bind the xrefs
    //get the xref graph
    XrefGraph xrefg = newdb.GetHostDwgXrefGraph(true);
    //check if the xref graph contains more than just the current drawing
    if (xrefg.IsEmpty || xrefg.NumNodes == 1) {
        newdb.Dispose();
        return; // TODO: might not be correct. Was : Exit Sub
    }
   
    //Start a new transaction to get the exreferences
    using (Transaction tr = newdb.TransactionManager.StartTransaction) {
        //Loop through all the xrefs in the block table
        for (int i = 1; i <= xrefg.NumNodes - 1; i++) {
            //Get the graph node
            XrefGraphNode xrefn = (XrefGraphNode)xrefg.GetXrefNode(i);
            //Create a new object id collection for the block table record
            ObjectIdCollection ObjIdCollection = new ObjectIdCollection();
            //Add the blocktablerecord id to the collection
            ObjIdCollection.Add(xrefn.BlockTableRecordId);
            //Do the bind
            newdb.BindXrefs(ObjIdCollection, true);
        }
        tr.Commit();
    }
    //Save the database
    newdb.SaveAs("C:\\Documents and Settings\\Mark\\Desktop\\test\\final.dwg", DwgVersion.Current);
    //Destroy it
    newdb.Dispose();
}

Please note the lines:
            //Add the blocktablerecord id to the collection
            ObjIdCollection.Add(xrefn.BlockTableRecordId);
            //Do the bind
            newdb.BindXrefs(ObjIdCollection, true);
The bind does not work?!
I have checked the locals while debugging and everyting seems okay... maybe I'm tired - 9:45PM here
« Last Edit: August 19, 2008, 04:46:47 PM by mcarson »

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #7 on: August 19, 2008, 05:06:06 PM »
I'm not a mind reader mate - why doesn't it work.
Also, you should be looping thru the XrefGraphNodes, adding the id's of BlockTableRecords you're interested in to your collection, then AFTER your loop, calling the bind exactly once - it doesn't make sense to be doing on each iteration of your loop.

As an aside, are you new to programming and .NET and ObjectARX in general?
Me

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #8 on: August 19, 2008, 05:27:10 PM »
Also, it slipped my mind to ask: Have you done a search on this forum for all things relating to Binding?
Me

mcarson

  • Guest
Re: Open, Bind and Close
« Reply #9 on: August 19, 2008, 05:31:58 PM »
I did a few searches before posting - I did mention Draftek as a source for some of my code... maybe I should have replied to those.

Bit of my background...
AutoCAD Technician (AEC & Mechanical) about 13 years too long
Bit of a programmer, started with VBA in Office and then with a lot of AutoCAD VBA.
Employed as a CAD Manager type guy now (260 users!) and trying to create creating a standard for everyone.
Studying for my MCSD.
The AutoCAD API is fairly new to me. I have a lot to learn.

I don't want to leech; just learn by example.

Why?

« Last Edit: August 19, 2008, 05:35:46 PM by mcarson »

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #10 on: August 19, 2008, 05:47:55 PM »
I was curious because of some of the code. The code from Draftek is actually from me originally I believe, vultured by somebody else. But that's neither here nor there.

As far as learning by example, that's fine - I can understand it. However, having said that, you're trying to go from VBA to VB.NET (if I remember correctly), which is a rather large jump. You have to contend with .NET in it's largesse and then the ObjectARX .NET API - not an easy task.

I'm just south of you (London), so we can converse tomorrow, however, I will leave you with this to navel gaze over.

Cheers,
Glenn.
Me

mcarson

  • Guest
Re: Open, Bind and Close
« Reply #11 on: August 20, 2008, 05:33:42 AM »
Yeah, I would agree that the AutoCAD API is quite a learning curve. Due to the 'limitations' of VBA in AutoCAD, I decided to move into .NET

Thanks for the link. A few queries though...

Used the code posted by you, but removed the dll import and added BindXRefs(). Worked perfectly.
The next stage was to find out if a bind could be carried out on an in-memory database.
The modified method quit out when resolving the xrefs.

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #12 on: August 20, 2008, 06:06:22 AM »
Post your current code as something I can drop into a project and run ie a CommandMethod

260 users...by yourself!? That's steep.
Me

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #13 on: August 20, 2008, 06:18:39 AM »
Hmmm....after looking through reflector at the '08 dll's it looks like BindXrefs calls the underlying ObjectARX function 'acdbBindXrefs'.

It's first argument is a pointer to the host dbase to do the bind operation on. Now, seeing the signature for BindXrefs doesn't ask what dbase to bind to, it's either assuming the current dbase or whatever HostApplicationServices.WorkingDatabase is set to.

You may have to do what I did in that other thread and use acedXrefBind and pass the last argument which is the dbase to bind to....
Me

Glenn R

  • Water Moccasin
  • Posts: 1932
  • What idiot child of married cousins wrote this?!
Re: Open, Bind and Close
« Reply #14 on: August 20, 2008, 06:21:38 AM »
Actually, it is assuming the current dbase as it's a method of Database :)
Me