Author Topic: On Drawing Close (save or No Save)  (Read 5253 times)

0 Members and 1 Guest are viewing this topic.

GumbyCAD

  • Newt
  • Posts: 84
On Drawing Close (save or No Save)
« on: June 03, 2014, 08:02:37 AM »
Hi Team,

A quick question or 2.  More like peoples opinion / ideals.

I want to be able to trap / catch when a person tries to exit the drawing and capture if they choose Yes or No to Saving of Changes.

If they say Yes I want to execute code.

Any suggestions?

Stephan

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: On Drawing Close (save or No Save)
« Reply #1 on: June 03, 2014, 09:02:36 AM »
Check out the Database BeginSave event

GumbyCAD

  • Newt
  • Posts: 84
Re: On Drawing Close (save or No Save)
« Reply #2 on: June 04, 2014, 09:35:30 AM »
Thanks, I did.

I also found the following link on this forum.

http://www.theswamp.org/index.php?topic=42961.0

My question is if I attached my code to the Beginsave Event on the OpenDoc, do I need to remove the event before the document is closed or will it just remove itself automatically.
(be gentle I have not played much with events, and lots of example always show add and remove)

Code - Visual Basic: [Select]
  1.  
  2.             Dim OpenDoc As Document = DocumentCollectionExtension.Open(mDocMan, FileOpen, False)
  3.  
  4.             AddHandler OpenDoc.Database.BeginSave, AddressOf ExtractOnSave
  5.  
  6.  

Thanks.

BillZndl

  • Guest
Re: On Drawing Close (save or No Save)
« Reply #3 on: June 04, 2014, 11:51:07 AM »
If you are going to close the document, then no, you don't have to remove the handler.
But if you are going to run code after the event fires, then you may want to remove it.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: On Drawing Close (save or No Save)
« Reply #4 on: June 04, 2014, 02:48:25 PM »
To add to what Bill has said, when I am working on events with per-database data I use the Disposed event to clean up on my end.

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: On Drawing Close (save or No Save)
« Reply #5 on: June 04, 2014, 03:22:52 PM »
Hi Will,


Would you mind posting an example?  I haven't had a chance to use the disposed event and I am interested in seeing what you are cleaning up.  Thanks.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: On Drawing Close (save or No Save)
« Reply #6 on: June 04, 2014, 08:54:30 PM »
I'm away from my development machine but here's the pseudo code for my database watcher class:

Create a dictionary to hold all the per database info.  In my case it was dictionary of <ObjectId (of database), dictionary of <ObjectId (of objects within database), MyClass (to hold this object data)>>

-add handler to Application.DocumentManager.DocumentCreated event
     This handler will register any other per document or per database events for the new database (IF the database was found to contain info that I wanted to watch)

-Database.BeginSave handler:
    This handler will serialize the data held in the MyClass objects and store in an XRecord which is thrown into a dictionary right before the drawing is saved

-Database.Disposed handler:
    This handler removes the per database info from the dictionary and unregisters all other events from this database

FYI to anybody interested, this watcher sits next to an overrule where I've written my own close method to observe how the 'interesting' objects are changing through the drawing's life. Will have more time to elaborate later, hope this helps point in the right direction!

GumbyCAD

  • Newt
  • Posts: 84
Re: On Drawing Close (save or No Save)
« Reply #7 on: June 04, 2014, 09:29:09 PM »
I look forward to see what Will will show.

A second question:
So in principal because I am attaching my Code to the event of the Document itself then I could execute different code for different drawing types.

I.e.
Detail Drawings Execute CodeX on Begin Save
Elevation Drawings execute CodeY on BeginSave
.....


Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: On Drawing Close (save or No Save)
« Reply #8 on: June 04, 2014, 09:59:52 PM »
Yes Stephan.

Do you use distinctive borders for each ? .. just thinking about how your routine would tell the difference.
You may need to establish a document dictionary at creation to hold the config data. ( or use an external sql database )

Looks like you are progressing nicely :)
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.

GumbyCAD

  • Newt
  • Posts: 84
Re: On Drawing Close (save or No Save)
« Reply #9 on: June 04, 2014, 10:05:47 PM »
Thanks Kerry.

I have managed to get the Model Data to extract to an SQL database on Save of the drawing.
Wanted to extend it to extract Drawing data too.

And I think your suggestion to save config type is a good idea.

I am already using an extension dictionary to store some of my model data.

Stephan

Jeff H

  • Needs a day job
  • Posts: 6150
Re: On Drawing Close (save or No Save)
« Reply #10 on: June 04, 2014, 10:11:53 PM »
I use DatabaseSummaryInfo so can read quickly by reading the beginning bytes and not have to open whole file, people can see dwg properties in window explorer, etc.......

GumbyCAD

  • Newt
  • Posts: 84
Re: On Drawing Close (save or No Save)
« Reply #11 on: June 04, 2014, 10:23:07 PM »
Interesting Jeff.

I found the following Example:
Code: [Select]
[CommandMethod("WriteSummary")]
 static public void WriteSummary()
 {
     Document doc = Application.DocumentManager.MdiActiveDocument;
     Database db = doc.Database;
 
     DatabaseSummaryInfoBuilder infobuilder =
                            new DatabaseSummaryInfoBuilder();
 
     infobuilder.Title = "Test";
     infobuilder.Author = "DevTech";
     infobuilder.Comments = "This is a test drawing";
     infobuilder.Subject = "Testing";
 
     infobuilder.CustomPropertyTable.Add("A", "1");
     infobuilder.CustomPropertyTable.Add("B", "2");
     infobuilder.CustomPropertyTable.Add("C", "3");
 
     DatabaseSummaryInfo info = infobuilder.ToDatabaseSummaryInfo();
     db.SummaryInfo = info; //set the summary info
 }

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: On Drawing Close (save or No Save)
« Reply #12 on: June 04, 2014, 11:57:00 PM »
Well Gumby, I propose a trade.

I have had a hard time getting anything working with an sql database.  It should be easy (I think) but keep running into issues.

Since the code I'm going to be sharing is at the heart of an application I've spent about 400 hours working on I'd like to see what you've got going on as well.

GumbyCAD

  • Newt
  • Posts: 84
Re: On Drawing Close (save or No Save)
« Reply #13 on: June 05, 2014, 12:05:08 AM »
Will happy to share anything.
I understand that if you have spent many hours on work you just don't want to give it away.
I was really only interested in learning from others i.e. the way people think about how the code gets put together.

My only drama is the data I am transferring is from a Proxys Objects (3rd Party App for AUtoCAD) at this stage.
Plan is to extend it to store attribute information from Drawing Sheet Blocks.

Principal goes like this:
  • I collect all the data (being thur a third Party API) and add it a DataTable
  • Pass the DataTable to Stored Procedure sitting on the SQL server
  • SQL server / Stored Procedure does the rest of the work (i.e Insert Update delete etc).

And all this is done by a command at the moment.

I am just trying to implement the same so that when the user saves / closes the DWG and hits save the data gets pushed back up to the SQL server.

Stephan

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: On Drawing Close (save or No Save)
« Reply #14 on: June 05, 2014, 12:41:37 AM »
Sorry, guess I lied and used the DatabaseToBeDestroyed event here and not Disposed... Can't remember why, but I do see that I tried out disposed so maybe there was a good reason...

For those who have been around for awhile you will recognize a pattern in the overrule here that I learned from Tony.

A bit of background... In the oil and gas industry in Calgary most small to mid sized companies are doing their 3d piping and structural modelling with cadworx.  When I first started my engineering career I was dumped into doing the tedious civil/structural tasks (because we didn't have a civil guy in house and he didn't care about pile schedules) like checking pile schedules.

It was awful! Imagine checking the info for 200 piles, finding it on a 2d layout that tags it, finding it in the model, id'ing the top and inputting to excel.  I knew there was a better way.

The piling models are stand alone models with nothing but piles (HS structural tubing) and pile caps in them.  Cadworx uses a centerline which it draws 3d solids around.  It stores info on these lines in the xdata (hence the overrule applies to lines only).  The overall application has a mechanism for numbering piles from a top level approach start here then go there then go there concept, ignoring all the details about how many are in between. 

Once the database has had piles numbered in it it stores info into the extension dictionary on modelspace, which is used to define the database as interesting.  Interesting databases are tracked as a part of the revision management system, which allows me to spit out a new pile schedule which includes showing what changes have been made to help with clouding, would also like to be able to roll forward/back through the design.  That tracking was the purpose for this part of the application.

[edit]
Guess I should add that this application sits on it's own and is loaded at startup, the IExtensionApplication interface is the mechanism used to instantiate the class at that time (along with licensing, updates, etc.)
[/edit]

HTH

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Autodesk.AutoCAD.Runtime;
  7. using Autodesk.AutoCAD.DatabaseServices;
  8. using Autodesk.AutoCAD.ApplicationServices;
  9. using Autodesk.AutoCAD.Geometry;
  10. //[assembly: CommandClass(typeof(CivilPiling.PileOverruleCommands))]
  11.  
  12. namespace CivilPiling
  13. {
  14.     public static partial class Tools
  15.     {
  16.         static Tools()
  17.                 {
  18.                         Application.QuitWillStart += quitWillStart;
  19.                 }
  20.  
  21.                 static void quitWillStart( object sender, EventArgs e )
  22.                 {
  23.                         Application.QuitWillStart -= quitWillStart;
  24.                         if( overrule != null )
  25.                         {
  26.                 //Application.ShowAlertDialog("Quit start");
  27.                                 overrule.Dispose();
  28.                                 overrule = null;
  29.                 GC.Collect(2, GCCollectionMode.Default, true);
  30.                         }
  31.                 }
  32.  
  33.                 public static PileOverrule overrule = new PileOverrule(true);
  34.     }
  35.     public class PileOverrule : ObjectOverrule
  36.     {
  37.  
  38.         bool enabled = false;
  39.         RXClass rxclass = RXClass.GetClass(typeof(Line));
  40.         static string AppName = "CADWORX_STEEL";
  41.         public static Dictionary<IntPtr, PileRevision> currentPiles;
  42.         public static Dictionary<IntPtr, PileRevision> issuedPiles;
  43.         public PileOverrule(bool enabled = true)
  44.         {
  45.             currentPiles = new Dictionary<IntPtr, PileRevision>();
  46.             issuedPiles = new Dictionary<IntPtr, PileRevision>();
  47.             Application.DocumentManager.DocumentCreated += DocumentManager_DocumentCreated;
  48.             Enabled = enabled;
  49.             this.SetXDataFilter(AppName);
  50.         }
  51.         //Add database to watch list if it has the appname we're interested in
  52.         void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
  53.         {
  54.             WatchDatabase(e.Document.Database);
  55.         }
  56.  
  57.         public static void WatchDatabase(Database db)
  58.         {
  59.             if (db.isInteresting())
  60.             {
  61.                 if (!currentPiles.ContainsKey(db.UnmanagedObject))
  62.                 {
  63.                     //Application.ShowAlertDialog(
  64.                     string.Format("Watching {0}", db.Filename).WriteMessage();
  65.                     db.BeginSave += Database_BeginSave;
  66.                     db.DatabaseToBeDestroyed += db_DatabaseToBeDestroyed;
  67.                     currentPiles.Add(db.UnmanagedObject, new PileRevision(db, Tools.RevCurrent));
  68.                     if (db.isInteresting())//if db had invalid data it would no longer be interesting at this point
  69.                     {
  70.                         PileRev value = db.GetLastIssuedRevision();
  71.                         if (!value.IsNull()) issuedPiles.Add(db.UnmanagedObject, new PileRevision(db, value));
  72.                     }
  73.                 }
  74.             }
  75.         }
  76.         public static void StopWatching(Database db)
  77.         {
  78.             if (currentPiles.ContainsKey(db.UnmanagedObject))
  79.                 currentPiles.Remove(db.UnmanagedObject);
  80.             if (issuedPiles.ContainsKey(db.UnmanagedObject))
  81.                 issuedPiles.Remove(db.UnmanagedObject);
  82.             db.BeginSave -= Database_BeginSave;
  83.             db.DatabaseToBeDestroyed -= db_DatabaseToBeDestroyed;
  84.         }
  85.         public static void UpdateIssuedRev(Database db, string Revision)
  86.         {
  87.             //update issued piles
  88.             if (issuedPiles.ContainsKey(db.UnmanagedObject)) issuedPiles[db.UnmanagedObject] = new PileRevision(db, Revision);
  89.             else issuedPiles.Add(db.UnmanagedObject, new PileRevision(db, Revision));
  90.             //update current piles
  91.             currentPiles[db.UnmanagedObject].ClearFlags();
  92.         }
  93.         static void db_DatabaseToBeDestroyed(object sender, EventArgs e)
  94.         {
  95.             try
  96.             {
  97.                 string.Format("Database {0} to be destroyed", ((Database)sender).Filename).WriteMessage();
  98.             }
  99.             finally
  100.             {
  101.  
  102.             }
  103.             Database db = (Database)sender;
  104.             if (currentPiles.ContainsKey(db.UnmanagedObject))
  105.                 currentPiles.Remove(db.UnmanagedObject);
  106.             if (issuedPiles.ContainsKey(db.UnmanagedObject))
  107.                 issuedPiles.Remove(db.UnmanagedObject);
  108.         }
  109.         ////remove database from our dictionary
  110.         //void Database_Disposed(object sender, EventArgs e)
  111.         //{
  112.         //    string.Format("Database {0} disposed", ((Database)sender).Filename).WriteMessage();
  113.         //    Database db = (Database)sender;
  114.         //    if (currentPiles.ContainsKey(db.UnmanagedObject))
  115.         //        currentPiles.Remove(db.UnmanagedObject);
  116.         //}
  117.  
  118.         //save our dictionary into the current rev place
  119.         static void Database_BeginSave(object sender, DatabaseIOEventArgs e)
  120.         {
  121.             try
  122.             {
  123.                 Database db = (Database)sender;
  124.                 if (currentPiles.ContainsKey(db.UnmanagedObject))
  125.                     using (Transaction tr = db.TransactionManager.StartTransaction())
  126.                     {
  127.                         //Don't think this is required
  128.                         //DBDictionary revDict;
  129.                         //db.GetCreateRevDict(out revDict);
  130.  
  131.                         string.Format("Database {0} begin save", e.FileName).WriteMessage();
  132.                         currentPiles[db.UnmanagedObject].Issue(new PileRev(Tools.RevCurrent));
  133.                         tr.Commit();
  134.                     }
  135.             }
  136.             catch (System.Exception ex)
  137.             {
  138.                 ex.Show();
  139.             }
  140.         }
  141.  
  142.  
  143.         public virtual bool Enabled
  144.         {
  145.             get
  146.             {
  147.                 return this.enabled;
  148.             }
  149.             set
  150.             {
  151.                 if (this.enabled ^ value)
  152.                 {
  153.                     this.enabled = value;
  154.                     if (value)
  155.                     {
  156.                         Overrule.AddOverrule(rxclass, this, true);
  157.                         this.SetXDataFilter(Tools.AppName);
  158.                     }
  159.                     else
  160.                         Overrule.RemoveOverrule(rxclass, this);
  161.                     OnEnabledChanged(this.enabled);
  162.                 }
  163.             }
  164.         }
  165.         public override void Erase(DBObject dbObject, bool erasing)
  166.         {
  167.             Database db = dbObject.Database;
  168.             if (currentPiles.ContainsKey(db.UnmanagedObject) && currentPiles[db.UnmanagedObject].LivePiles.ContainsKey(dbObject.ObjectId))
  169.             {
  170.                 if (erasing)
  171.                 {
  172.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nRemoving {0} from {1}", dbObject.Id, db.Filename);
  173.                     currentPiles[db.UnmanagedObject].LivePiles[dbObject.Id].Changes |= PileChanges.Deleted;
  174.                     currentPiles[db.UnmanagedObject].LivePiles[dbObject.Id].Erased = true;
  175.                 }
  176.                 else
  177.                 {
  178.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nUndoing removing {0} from {1}", dbObject.Id, db.Filename);
  179.                     currentPiles[db.UnmanagedObject].LivePiles[dbObject.Id].Changes &= PileChanges.NewPile;
  180.                     currentPiles[db.UnmanagedObject].LivePiles[dbObject.Id].Erased = false;
  181.                 }
  182.             }
  183.             base.Erase(dbObject, erasing);
  184.         }
  185.         public override void Close(DBObject obj)
  186.         {
  187.             try
  188.             {
  189.                 PileDataSet pds = obj.ReportExData();
  190.                 if (pds != null)
  191.                 {
  192.                     Database db = obj.Database;
  193.                     if (currentPiles.ContainsKey(db.UnmanagedObject))
  194.                     {
  195.                         ObjectId id = obj.ObjectId;
  196.                         if (obj.IsErased)
  197.                         {
  198.                         }
  199.                         else if (obj.IsNewObject)
  200.                         {
  201.                             ////////////////////////////////////////////////need to thrash pile tag in xdata
  202.                             try
  203.                             {
  204.                                 obj.SetPileTag(" ");
  205.                                 pds.Changes = PileChanges.NewPile;
  206.                                 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nAdding {0} to {1}", id, db.Filename);
  207.                                 currentPiles[db.UnmanagedObject].LivePiles.Add(obj.ObjectId, pds);
  208.                             }
  209.                             catch (System.Exception e)
  210.                             {
  211.                                 Application.ShowAlertDialog(string.Format("Fail: {0}", e));
  212.  
  213.                             }
  214.                         }
  215.                         else if (obj.IsModified)
  216.                         {
  217.                             ////////////////////////////////////////////////////////////////////determine modifications, update current rev
  218.                             if (issuedPiles.ContainsKey(db.UnmanagedObject) && issuedPiles[db.UnmanagedObject].LivePiles.ContainsKey(id))
  219.                             {
  220.                                 //check all properties against original
  221.                                 PileDataSet issued = issuedPiles[db.UnmanagedObject].LivePiles[pds.Id];
  222.                                 if (pds.PileTag != issued.PileTag) pds.Changes |= PileChanges.PileTag;
  223.                                 if (pds.PileLength != issued.PileLength) pds.Changes |= PileChanges.PileLength;
  224.                                 if (pds.PileSize != issued.PileSize) pds.Changes |= PileChanges.PileSize;
  225.                                 if (pds.Location.X != issued.Location.X) pds.Changes |= PileChanges.XCoordinate;
  226.                                 if (pds.Location.Y != issued.Location.Y) pds.Changes |= PileChanges.YCoordinate;
  227.                                 if (pds.Location.Z != issued.Location.Z) pds.Changes |= PileChanges.ZCoordinate;
  228.                                 string.Format("Changing {0}: {1}", pds.PileTag == null ? "" : pds.PileTag, pds.GetFlags()).WriteMessage();
  229.  
  230.                             }
  231.                             else
  232.                             {
  233.                                 //must be new object since last issue? we can or the changes of the pds we're looking at with the one in current issue?
  234.                                 pds.Changes |= currentPiles[db.UnmanagedObject].LivePiles[pds.Id].Changes;
  235.                                 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nNew since last rev {0}: {1}", id, pds.GetFlags());
  236.                             }
  237.                             currentPiles[db.UnmanagedObject].LivePiles[id] = pds;
  238.                         }
  239.                     }
  240.                 }
  241.             }
  242.             catch (System.Exception e) { e.Show(); }
  243.             base.Close(obj);
  244.         }
  245.  
  246.         protected virtual void OnEnabledChanged(bool enabled)
  247.         {
  248.         }
  249.  
  250.         protected override void Dispose(bool disposing)
  251.         {
  252.             if (disposing && !base.IsDisposed)
  253.             {
  254.                 Enabled = false;
  255.                 rxclass.Dispose();
  256.                 foreach (IntPtr db in currentPiles.Keys)
  257.                 {
  258.                     string.Format("Destroying {0} data", db).WriteMessage();
  259.                     currentPiles.Remove(db);
  260.                 }
  261.                
  262.                 currentPiles = null;
  263.                 Application.DocumentManager.DocumentCreated -= DocumentManager_DocumentCreated;
  264.             }
  265.             base.Dispose(disposing);
  266.         }
  267.  
  268.     }
  269. }
  270.  
« Last Edit: June 05, 2014, 01:06:18 AM by WILL HATCH »