TheSwamp

Code Red => .NET => Topic started by: bchapman on February 02, 2013, 11:14:54 PM

Title: Third Routine - Working Progress
Post by: bchapman on February 02, 2013, 11:14:54 PM
I didn't know C# or .net less than a week ago (though am fairly strong with lisp and web-based coding like active-script), so don't hassle me to much. I'm going to go back and do the introductory stuff from Autodesk...might go through the labs.  Having trouble understanding why I have to keep re-defining junk as other junk... this language seems incredibly inefficient...though I am guessing there are ways to skip all that. FYI if you reply with experienced c# coder terms... I'll likely have no idea what you're talking about... I still think of the code below in "lisp" terms...setting variables, etc.

Thanks!


Updating code...will repost soon!
Title: Re: Third Routine - Working Progress
Post by: Kerry on February 03, 2013, 12:13:00 AM

Just running past.
Frankly, the CAPITALS make me want to keep running :(
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 03, 2013, 03:21:15 AM
Sorry... still learning... I'm using them to help me see the differences from variables and command junk. Easier for me to read than what I see everyone else write so I think I will keep doing it that way.
Title: Re: Third Routine - Working Progress
Post by: gile on February 03, 2013, 03:57:02 AM
Hi,

About capitalization convention in .NET, you can see this:
http://msdn.microsoft.com/en-us/library/vstudio/ms229043%28v=vs.100%29.aspx
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 03, 2013, 06:48:29 AM
Thanks! I read through it but don't quite understand it all... still at 101 level.  I did see the comment about all caps making it confusing for a programmer to tell the difference between a property and class... fortunately I'm writing this stuff for myself and those I choose to share it with (otherwords its not my day job). I may have to keep the all caps for a while until I can understand it better... guess only those who really want to help will help anyway...others can troll away.

Much appreciated!
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 04, 2013, 11:32:48 AM
Thanks! I read through it but don't quite understand it all... still at 101 level.  I did see the comment about all caps making it confusing for a programmer to tell the difference between a property and class... fortunately I'm writing this stuff for myself and those I choose to share it with (otherwords its not my day job). I may have to keep the all caps for a while until I can understand it better... guess only those who really want to help will help anyway...others can troll away.

Much appreciated!

I won't bother to read code that's all caps.
Title: Re: Third Routine - Working Progress
Post by: Keith™ on February 04, 2013, 12:00:33 PM
All caps in code is generally reserved for constants ... generally ...

Depending upon who you talk to, and there is alot of dissention in the subject, you should use camel case, hungarian notation, descriptive variable and function names etc.

As far as I am concerned, the particular "standard" you choose to use depends upon many different criteria.

1) Is it easily understood by those who didn't write it?
2) Is it consistent throughout the code?
3) Is the code in-house or is it shared with outside organizations?
4) Is there an existing standard in place?

These are but a few of the examples that one should consider ... but you should remember that in some programming languages, compilers are case sensitive .. so Point, point and POINT are all different.

Remember, it really only matters what "standard" you choose to use to the degree that others (and you) can read and understand what is being coded. If you are the only programmer, and your code will never be seen by those outside your group ... code it how you like. Your product is the compiled code, not the source.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 04, 2013, 07:54:41 PM
All caps in code is generally reserved for constants ... generally ...

Depending upon who you talk to, and there is alot of dissention in the subject, you should use camel case, hungarian notation, descriptive variable and function names etc.


There isn't much dissention on the question of whether all caps is reasonable. Most humans have a hard time reading all caps, whether it is code or just about anything else that's written.

The bottom line is that I and probably lots of others who might be willing to help, will not bother to read code that's all caps. 

That's the bottom line for someone that is here lookng for help.
Title: Re: Third Routine - Working Progress
Post by: Keith™ on February 04, 2013, 10:46:11 PM
You misunderstood what I said ... certainly there is no dissention on the use of all caps, it is certainly difficult to read ... the dissention is on what constitutes the "correct" standard by which one should write code.

I have written code for alot of companies ... everyone seems to have a different "standard" that I must adhere to ... I don't like it, but it pays the bills. Thankfully, none of them have ever been all caps!
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 05, 2013, 05:51:22 AM
Sorry TT I pulled the code cuz it was screwed up... I should clarify that not the entire code was in CAPs... but I was using the caps to help me identify the difference from what I could set myself, or what I would call a "variable" in Autolisp which is where I come from, versus what was being done by computer, which was regular case. I'm hoping to get good enough to read the code and understand it the way ya'll do. Frankly though, sounds like help will not be provided in this forum if I continue my current efforts... Considering that, I am wondering if help would be provided even if I did or if some other formatting issue would be found to give reason for others to move on.  It's a good thing there are lots of forums out there... bound to find some that will help regardless.
Title: Re: Third Routine - Working Progress
Post by: Kerry on February 05, 2013, 08:16:01 AM
< .. >  guess only those who really want to help will help anyway...others can troll away.

Much appreciated!

Well I won't comment on your CAPITALS again. if that's what you want.

.. a troll is the nicest thing I've been called all week :-D
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 05, 2013, 08:48:19 AM
Sorry Kerry... I am taking your advice to heart... it will be a difficult transition but should break the habit right-away.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 05, 2013, 11:32:49 AM
Sorry TT I pulled the code cuz it was screwed up... I should clarify that not the entire code was in CAPs... but I was using the caps to help me identify the difference from what I could set myself, or what I would call a "variable" in Autolisp which is where I come from...

Most of us here are from the same place, in case you didn't know.

Back when I wrote the AutoLISP chapters of Maximizing AutoCAD R13 (http://www.amazon.com/Maximizing-AutoCAD-Release-Rusty-Gesner/dp/0827379935), I wrote a code editor for LISP (called LispPad, written entirely in Borland Delphi), which was included on the CD that shipped with the book. It had a few features (like data tips and dynamic help) that predated most similar features in today's IDE code editors, that made it extremely useful for writing and debugging LISP. Of course, it's nothing like the Visual LISP IDE which didn't exist back then, but was still much more useful than plain ole Notepad.

In any event, Visual Studio's syntax highlighting is how we distinguish different types of code elements, and is much more effective at solving that problem than using upper-case identifiers.
Title: Re: Third Routine - Working Progress
Post by: Keith™ on February 05, 2013, 11:38:46 AM
^+1
Title: Re: Third Routine - Working Progress
Post by: WILL HATCH on February 05, 2013, 11:39:42 AM
BC,

At the risk of being perceived to be a member of the male genitalia I'd like to offer my protest to your thoughts of leaving the swamp.

IMO:

The swamp is the best place on the net for AutoCAD programming.  You're a fool to not utilize it.

You need to forget your ego.  I've read several times about how you're learning and you're new, but frankly I don't care.  I get the impression that you're used to knowing what you're doing and this step into unknown muddy waters is a little scary (especially with the potential ogres [yes at the swamp I like to think of them as ogres rather than trolls {hope that's ok with you kerry}]) but it's wasting everybody's time reiterating that you're learning.  Instead of telling me what you don't know, I'd like to see you tell me what you're doing, what you know (or think you know) , and what you need to know.

These guys do want to help you, they've gone as far as telling you what they want from you to minimize the time they have to spend looking through your code (they are doing it for free...) to understand what you're trying to do, considering you don't provide any explanation or comments.

That said, I suggest you suck it up and stick it out.  I don't want to see you quit, this is a very powerful application and you've made the right choice in stepping beyond lisp.  Stop complaining about what is being provided for free help, these guys know everything, you want them to be able to read your code.

If it's helpful, I spent a lot of my very early time on through the interface.  It took about a year of working on cad customizations before I came across anything really unique that I couldn't find in another post somewhere, so I didn't have any need to post any questions or comments until recently.  I was disappointed to get very little criticism, so it really irritates me to see you getting good critical input and not appreciating it.

Cheers
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 05, 2013, 11:42:45 AM
You misunderstood what I said ... certainly there is no dissention on the use of all caps, it is certainly difficult to read ... the dissention is on what constitutes the "correct" standard by which one should write code.

I think most of us have come to terms with the fact that there are two things that we all know better than to debate, one of which is Religion.

Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 05, 2013, 12:02:36 PM

....If it's helpful, I spent a lot of my very early time on through the interface.....


Kean's blog is great for seeing examples of how to use the very complicated managed API, but at the same time, it is not a good place to learn a programming language like C#, because Kean's code routinely exhibits examples of poor coding.

For example, in a recent post he shows an example of a method that uses LINQ internally, but which  returns an array, something that you should never do in a general-purpose, resuable method.

If he posted that code on StackOverflow or CodeProject, they would just eat him alive.   :laugh:

Title: Re: Third Routine - Working Progress
Post by: bchapman on February 05, 2013, 12:10:59 PM
Thanks Will... I'll keep that in consideration. I stress that I'm "learning" to do exactly what you're asking... tell people what I don't know.  I don't know anything. I went straight from Autolisp, to cutting and pasting C# code together, creating variables, and debugging it as necessary, til it worked. Using visual lisp with the COM helped me understand a little bit about using namespaces, which in Lisp I'd use to invoke methods.  With C# I know I have to start transactions. I know I have to pick an object, then define it as something else, then define that as something else to use it how I want but I don't know why.  As you can see I don't even know the appropriate terms.  I know visual lisp well, some visual basic as needed to create various excel forms, active script for company flash websites, and java for html sites so I can look at the c# code and it's almost like looking through muddy glasses... can make out what I am looking at...but can really see the details clearly.  I know ZERO coding terms. So when someone in C# talks about classes, and methods, and namespaces, etc. or whatever for c# I don't even know what they are talking about.  I haven't even looked through the tutorials... I skipped them.  Obviously the first thing every programmer here will do is tell me to go back and figure that stuff out...  I get incredibaly board with "hello world" and basic functions... and would never make it through... lol 

As far as them helping, I understand that.



BC,

At the risk of being perceived to be a member of the male genitalia I'd like to offer my protest to your thoughts of leaving the swamp.

IMO:

The swamp is the best place on the net for AutoCAD programming.  You're a fool to not utilize it.

You need to forget your ego.  I've read several times about how you're learning and you're new, but frankly I don't care.  I get the impression that you're used to knowing what you're doing and this step into unknown muddy waters is a little scary (especially with the potential ogres [yes at the swamp I like to think of them as ogres rather than trolls {hope that's ok with you kerry}]) but it's wasting everybody's time reiterating that you're learning.  Instead of telling me what you don't know, I'd like to see you tell me what you're doing, what you know (or think you know) , and what you need to know.

These guys do want to help you, they've gone as far as telling you what they want from you to minimize the time they have to spend looking through your code (they are doing it for free...) to understand what you're trying to do, considering you don't provide any explanation or comments.

That said, I suggest you suck it up and stick it out.  I don't want to see you quit, this is a very powerful application and you've made the right choice in stepping beyond lisp.  Stop complaining about what is being provided for free help, these guys know everything, you want them to be able to read your code.

If it's helpful, I spent a lot of my very early time on through the interface.  It took about a year of working on cad customizations before I came across anything really unique that I couldn't find in another post somewhere, so I didn't have any need to post any questions or comments until recently.  I was disappointed to get very little criticism, so it really irritates me to see you getting good critical input and not appreciating it.

Cheers
Title: Re: Third Routine - Working Progress
Post by: WILL HATCH on February 05, 2013, 01:21:04 PM

....If it's helpful, I spent a lot of my very early time on through the interface.....


Kean's blog is great for seeing examples of how to use the very complicated managed API, but at the same time, it is not a good place to learn a programming language like C#, because Kean's code routinely exhibits examples of poor coding.

For example, in a recent post he shows an example of a method that uses LINQ internally, but which  returns an array, something that you should never do in a general-purpose, resuable method.

If he posted that code on StackOverflow or CodeProject, they would just eat him alive.   :laugh:

You've shown me the way in that sense, thanks!  I've since learned better methods of handling problems through examples posted here.  The one that stands out in my mind the most was his method of handling anonymous/dynamic blocks with this long messy routine of looking through xdata when you showed me a very elegant method using GetBlockReferenceIds and GetAnonymousBlockIds.

Thanks!
Title: Re: Third Routine - Working Progress
Post by: WILL HATCH on February 05, 2013, 01:22:44 PM
Thanks Will... I'll keep that in consideration. I stress that I'm "learning" to do exactly what you're asking... tell people what I don't know.  I don't know anything. I went straight from Autolisp, to cutting and pasting C# code together, creating variables, and debugging it as necessary, til it worked. Using visual lisp with the COM helped me understand a little bit about using namespaces, which in Lisp I'd use to invoke methods.  With C# I know I have to start transactions. I know I have to pick an object, then define it as something else, then define that as something else to use it how I want but I don't know why.  As you can see I don't even know the appropriate terms.  I know visual lisp well, some visual basic as needed to create various excel forms, active script for company flash websites, and java for html sites so I can look at the c# code and it's almost like looking through muddy glasses... can make out what I am looking at...but can really see the details clearly.  I know ZERO coding terms. So when someone in C# talks about classes, and methods, and namespaces, etc. or whatever for c# I don't even know what they are talking about.  I haven't even looked through the tutorials... I skipped them.  Obviously the first thing every programmer here will do is tell me to go back and figure that stuff out...  I get incredibaly board with "hello world" and basic functions... and would never make it through... lol 

As far as them helping, I understand that.


I'd suggest explaining what you want to do, and you'll find you get directed on how to do it.  You can figure out the why as you go.
Title: Re: Third Routine - Working Progress
Post by: Keith™ on February 05, 2013, 02:26:45 PM
You misunderstood what I said ... certainly there is no dissention on the use of all caps, it is certainly difficult to read ... the dissention is on what constitutes the "correct" standard by which one should write code.

I think most of us have come to terms with the fact that there are two things that we all know better than to debate, one of which is Religion.



which is exactly why I gave up trying to discuss it here ... I think the only thing standard is how you spell "standard" .. but then, some folks might even question that :lmao:
Title: Re: Third Routine - Working Progress
Post by: Jeff H on February 05, 2013, 02:39:24 PM
DON"T LEAVE BC!!!


I completely understand it hard to express ideas where you missing knowledge of the core fundamentals.
Just hang in there & here.

Off to get back to work on some drawings where all text is CAPITALIZED. :D 
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 05, 2013, 09:41:14 PM
Didn't mean to give the impression I was going anywhere :/ but see how I did. This forum is great! :) with the best there seems to be in the related fields. I should have accepted Kerry's comment as constructive criticism, instead, being me, I read it "I prefer just not to read that garbage"... paraphrased in my own words lol... I believe that wasn't Kerry's intention... All of you continue to provide great advice that helps me get where I hope to be :) I really do appreciate it....  Thanks!
Title: Re: Third Routine - Working Progress
Post by: Kean on February 07, 2013, 02:10:29 PM
Kean's blog is great for seeing examples of how to use the very complicated managed API...

Thank you - that's really what it's there for.

... it is not a good place to learn a programming language like C#, because Kean's code routinely exhibits examples of poor coding.

"Routinely" is a bit strong - I prefer "occasionally". :-)

Just to state my position on this... my primary goals for the blog are to create clear code that works correctly. Now there may be optimizations that can be done to tweak extra performance out of certain routines - and that's especially true when I'm playing around with features such as LINQ, which I haven't used heavily - but my personal choice is to cover more topics rather than fewer topics with perfectly optimal code.

It's great that The Swamp - and Code Project / Stack Overflow, which I do my best to use to get as close to optimal efficiency in my code as time permits - exist to help people go that extra mile. I don't pretend in any way to compete with these resources - that's not my intention, even if I were arrogant enough to think I had the capability & capacity to do so.

Oh yes, and I do miss the odd AutoCAD API or feature that could do something more efficiently too, of course. It's a broad API and no-one knows (let alone remembers) it perfectly. But if you call me on it, I'll update the problematic post and thank you for your help.

Kean
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 07, 2013, 03:27:32 PM
lol
Title: Re: Third Routine - Working Progress
Post by: dgorsman on February 07, 2013, 04:27:06 PM
Knowing the basics of object-oriented programming (classes,methods, properties, static/instance objects, exceptions and handling, and to a lesser extent things like inheritance, abstract/sealed/override) are essential.  Its boring, and repetitive, and takes a lot of work to get it straight, but like most things along those lines the payoff is worth it.  Trying to back into the subject through AutoCAD will raise a lot of questions of "Why do that?" or "Thats pretty vague", or "I don't understand when you say XXXX".  Like when you are asking why define this, then redefine as that, then redefine as something else - this may simply be a matter of a line being an entity being an object, but an object not necessarily being an entity, which is not necessarily a line (inheritance).

Plus, the basics are common across a lot of different samples in VB.NET, C#, C++, along with the host of support namespaces in dotNET like System.XML and System.Data.  A strong base allows you to figure out how to implement somethng even if its not exactly what you are looking for.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 07, 2013, 04:49:11 PM
Kean's blog is great for seeing examples of how to use the very complicated managed API...

Thank you - that's really what it's there for.

... it is not a good place to learn a programming language like C#, because Kean's code routinely exhibits examples of poor coding.

"Routinely" is a bit strong - I prefer "occasionally". :-)

Hi Kean.

I don't bother to visit often, but whenever I do and see code, there's always something that sticks out like a sore thumb. :laugh:

And how's this for occasionally - I just put a comment on your post about stripping XData from entities created by OFFSET, but was kind enough to not lambaste you about the wasteful practice of full-time handling of both the commandWillStart and the three command-ending events, when in reality, you only have to handle the former, and add the handlers for the latter from the handler of the former, only when the command that's starting is a command of interest (along with having the end-command handlers remove themselves from their respective events, when they fire).


Quote

Just to state my position on this... my primary goals for the blog are to create clear code that works correctly. Now there may be optimizations that can be done to tweak extra performance out of certain routines - and that's especially true when I'm playing around with features such as LINQ, which I haven't used heavily - but my personal choice is to cover more topics rather than fewer topics with perfectly optimal code.

It's great that The Swamp - and Code Project / Stack Overflow, which I do my best to use to get as close to optimal efficiency in my code as time permits - exist to help people go that extra mile. I don't pretend in any way to compete with these resources - that's not my intention, even if I were arrogant enough to think I had the capability & capacity to do so.

Oh yes, and I do miss the odd AutoCAD API or feature that could do something more efficiently too, of course. It's a broad API and no-one knows (let alone remembers) it perfectly. But if you call me on it, I'll update the problematic post and thank you for your help.

Kean

And just for fun, here's my take on how to deal with stripping xdata from entities in the OFFSET command, written in about 7 minutes, using mostly reusable classes that I routinely use for lots of things. No time for comments, and that'd take all the fun out of it, but please do give it a good looking over. I hope it proves enlightening.

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Autodesk.AutoCAD.DatabaseServices;
  6. using Autodesk.AutoCAD.Runtime;
  7. using System.ComponentModel;
  8.  
  9. /// Quick/dirty - strip xdata from entities created by
  10. /// the OFFSET command, in this example uses the XDATA
  11. /// appname "MYXDATA"
  12.  
  13. namespace Autodesk.AutoCAD.ApplicationServices.MyExtensions
  14. {
  15.    public class MyApplication : IExtensionApplication
  16.    {
  17.       public void Initialize()
  18.       {
  19.          OffsetCommandObserver.Inititlize();
  20.       }
  21.  
  22.       public void Terminate()
  23.       {
  24.       }
  25.    }
  26.    
  27.    public abstract class XDataManagerOverrule : ObjectOverrule
  28.    {
  29.       string appname = null;
  30.       TypedValue[] empty = null;
  31.       Document doc = null;
  32.       Database db = null;
  33.  
  34.       public XDataManagerOverrule( Document doc, string appname )
  35.       {
  36.          this.appname = appname;
  37.          this.doc = doc;
  38.          this.db = doc.Database;
  39.          empty = new TypedValue[] { new TypedValue( 1001, appname ) };
  40.          AddOverrule( RXClass.GetClass( typeof( Entity ) ), this, true );
  41.          SetXDataFilter( appname );
  42.       }
  43.  
  44.       public override void Close( DBObject dbObject )
  45.       {
  46.          if( dbObject.IsNewObject && dbObject.IsWriteEnabled && dbObject.Database == db )
  47.          {
  48.             using( ResultBuffer rb = dbObject.GetXDataForApplication( appname ) )
  49.             {
  50.                if( rb != null )
  51.                {
  52.                   UpdateXData( dbObject, rb );
  53.                }
  54.             }
  55.          }
  56.          base.Close( dbObject );
  57.       }
  58.  
  59.       protected virtual void UpdateXData( DBObject dbObject, ResultBuffer rb )
  60.       {
  61.       }
  62.  
  63.       bool disposed = false;
  64.  
  65.       protected override void Dispose( bool disposing )
  66.       {
  67.          if( disposing && ! disposed )
  68.             RemoveOverrule( RXClass.GetClass( typeof( Entity ) ), this );
  69.          disposed = true;
  70.          base.Dispose( disposing );
  71.       }
  72.    }
  73.  
  74.    /// <summary>
  75.    /// Avoids full-time handling of end-command events;
  76.    /// </summary>
  77.    
  78.    public abstract class CommandObserver : IDisposable
  79.    {
  80.       bool disposed = false;
  81.       Document doc = null;
  82.       bool handled = false;
  83.  
  84.       public CommandObserver( Document doc )
  85.       {
  86.          if( doc == null )
  87.             throw new ArgumentNullException( "doc" );
  88.          this.doc = doc;
  89.          this.doc.CommandWillStart += commandWillStart;
  90.       }
  91.  
  92.       public enum State
  93.       {
  94.          Starting = 0,
  95.          Ended = 1,
  96.          Cancelled = 2,
  97.          Failed = 3
  98.       }
  99.  
  100.       public Document Document
  101.       {
  102.          get
  103.          {
  104.             return this.doc;
  105.          }
  106.       }
  107.  
  108.       void commandWillStart( object sender, CommandEventArgs e )
  109.       {
  110.          if( OnCommandStarting( e.GlobalCommandName ) )
  111.          {
  112.             AddEndHandlers();
  113.          }
  114.       }
  115.  
  116.       void AddEndHandlers()
  117.       {
  118.          if( !this.handled )
  119.          {
  120.             doc.CommandEnded += doc_CommandEnded;
  121.             doc.CommandFailed += doc_CommandFailed;
  122.             doc.CommandCancelled += doc_CommandCancelled;
  123.             this.handled = true;
  124.          }
  125.       }
  126.  
  127.       void RemoveEndHandlers()
  128.       {
  129.          if( this.handled )
  130.          {
  131.             doc.CommandEnded -= doc_CommandEnded;
  132.             doc.CommandFailed -= doc_CommandFailed;
  133.             doc.CommandCancelled -= doc_CommandCancelled;
  134.             this.handled = false;
  135.          }
  136.       }
  137.  
  138.       /// Overrides should return true to be notified
  139.       /// when a command ends (otherwise, there is no
  140.       /// end-of-command notification).
  141.      
  142.       protected virtual bool OnCommandStarting( string name )
  143.       {
  144.          return false;
  145.       }
  146.  
  147.       protected virtual void OnCommandEnded( string name, State state )
  148.       {
  149.       }
  150.  
  151.       void doc_CommandCancelled( object sender, CommandEventArgs e )
  152.       {
  153.          RemoveEndHandlers();
  154.          OnCommandEnded( e.GlobalCommandName, State.Cancelled );
  155.       }
  156.  
  157.       void doc_CommandFailed( object sender, CommandEventArgs e )
  158.       {
  159.          RemoveEndHandlers();
  160.          OnCommandEnded( e.GlobalCommandName, State.Failed );
  161.       }
  162.  
  163.       void doc_CommandEnded( object sender, CommandEventArgs e )
  164.       {
  165.          RemoveEndHandlers();
  166.          OnCommandEnded( e.GlobalCommandName, State.Ended );
  167.       }
  168.  
  169.       protected virtual void Dispose( bool disposing )
  170.       {
  171.          if( !disposed )
  172.          {
  173.             doc.CommandWillStart -= this.commandWillStart;
  174.             RemoveEndHandlers();
  175.             disposed = true;
  176.          }
  177.       }
  178.  
  179.       public void Dispose()
  180.       {
  181.          Dispose( true );
  182.       }
  183.    }
  184.  
  185.    public class OffsetCommandObserver : CommandObserver
  186.    {
  187.       OffsetXDataOverrule overrule = null;
  188.       static DocData<OffsetCommandObserver> manager = null;
  189.  
  190.       public static void Inititlize()
  191.       {
  192.          // Dummy method to invoke static c'tor
  193.       }
  194.  
  195.       static OffsetCommandObserver()
  196.       {
  197.          manager = new DocData<OffsetCommandObserver>(
  198.                            doc => new OffsetCommandObserver( doc ) );
  199.       }
  200.  
  201.       public OffsetCommandObserver( Document doc )
  202.          : base( doc )
  203.       {
  204.       }
  205.  
  206.       protected override bool OnCommandStarting( string name )
  207.       {
  208.          if( name.Equals( "OFFSET", StringComparison.OrdinalIgnoreCase ) )
  209.          {
  210.             overrule = new OffsetXDataOverrule( this.Document, "MYXDATA" );
  211.             return true;
  212.          }
  213.          return false;
  214.       }
  215.  
  216.       protected override void OnCommandEnded( string name, State state )
  217.       {
  218.          if( overrule != null )
  219.          {
  220.             overrule.Dispose();
  221.             overrule = null;
  222.          }
  223.       }
  224.  
  225.       class OffsetXDataOverrule : XDataManagerOverrule
  226.       {
  227.          TypedValue[] empty = null;
  228.          public OffsetXDataOverrule( Document doc, string appname )
  229.             : base( doc, appname )
  230.          {
  231.             empty = new TypedValue[] { new TypedValue( 1001, appname ) };
  232.          }
  233.  
  234.          protected override void UpdateXData( DBObject dbObject, ResultBuffer rb )
  235.          {
  236.             using( ResultBuffer buffer = new ResultBuffer( empty ) )
  237.             {
  238.                dbObject.XData = buffer;
  239.             }
  240.          }
  241.       }
  242.    }
  243.  
  244.    public class DocData<T>
  245.    {
  246.       static Type key = typeof( T );
  247.       Func<Document, T> factory = null;
  248.  
  249.       public DocData( Func<Document, T> factory )
  250.       {
  251.          this.factory = factory;
  252.          DocumentCollection docs = Application.DocumentManager;
  253.          foreach( Document doc in docs )
  254.          {
  255.             Add( doc );
  256.          }
  257.          docs.DocumentCreated += documentCreated;
  258.          docs.DocumentToBeDestroyed += documentToBeDestroyed;
  259.       }
  260.  
  261.       void documentToBeDestroyed( object sender, DocumentCollectionEventArgs e )
  262.       {
  263.          if( e.Document.UserData.ContainsKey( key ) )
  264.          {
  265.             IDisposable disposable = e.Document.UserData[key] as IDisposable;
  266.             if( disposable != null )
  267.                disposable.Dispose();
  268.          }
  269.       }
  270.  
  271.       void documentCreated( object sender, DocumentCollectionEventArgs e )
  272.       {
  273.          Add( e.Document );
  274.       }
  275.  
  276.       private void Add( Document document )
  277.       {
  278.          document.UserData[key] = factory( document );
  279.       }
  280.  
  281.    }
  282. }
  283.  
  284.  
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 07, 2013, 08:48:57 PM
Geez...
Title: Re: Third Routine - Working Progress
Post by: Kerry on February 07, 2013, 08:55:20 PM
< .. >  It's a broad API and no-one knows (let alone remembers) it perfectly. But if you call me on it, I'll update the problematic post and thank you for your help.

Kean

That sounds like a perfect reason to document the API  ..   :evil:

oh, wait, we've heard this before haven't we ..
Title: Re: Third Routine - Working Progress
Post by: owenwengerd on February 07, 2013, 09:08:50 PM
Tony & Kean, when I saw that post my reaction was that removing xdata this way is an awful solution. If the xdata is not wanted in the copy, then it should not be cloned in the first place. But I don't know off the top of my head how much access you have to cloning operations from the managed API, so I said nothing.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 07, 2013, 11:37:16 PM
Tony & Kean, when I saw that post my reaction was that removing xdata this way is an awful solution. If the xdata is not wanted in the copy, then it should not be cloned in the first place. But I don't know off the top of my head how much access you have to cloning operations from the managed API, so I said nothing.

Hi Owen.  There's a few ways to deal with cloning in the managed API, but in the case of OFFSET, I don't think there's any objects being cloned, just the xdata (and probably xdictionary).

Title: Re: Third Routine - Working Progress
Post by: owenwengerd on February 08, 2013, 12:01:03 AM
Upon reflection, I think you're right Tony. There is AcDbCurve::getOffsetCurves(), which would be the logical way to generate the new entity. If xdata is being copied, I wonder if it's done by getOffsetCurves() or by the OFFSET command. If the latter, then Kean's solution doesn't seem so awful after all.
Title: Re: Third Routine - Working Progress
Post by: Kean on February 08, 2013, 04:11:22 AM
I don't bother to visit often, but whenever I do and see code, there's always something that sticks out like a sore thumb. :laugh:

Feel free to post a comment or drop me an email when you see such issues. I appreciate the feedback, especially when it's given politely.

I like your implementation - thanks for sharing it. I choose to structure my code samples more linearly, as they're intended to demonstrate how to do certain things inside AutoCAD. I'd absolutely agree that this kind of abstraction is a good thing, architecturally-speaking, but it unfortunately tends to make it harder to follow for people who are coming to grips with an API (more advanced readers will pick out what they need, in any case). Nevertheless - as I've said - I do like your implementation.

The core issue about having command reactors in place on an ongoing basis is well taken... I'm in any case considering an alternative approach that's a bit more generic - I'll try to post something next week.

Kean
Title: Re: Third Routine - Working Progress
Post by: bchapman on February 08, 2013, 12:26:26 PM
Like I said... the first thing programmers will tell me is go back to the basics lol.... couldn't resist could ya

Knowing the basics of object-oriented programming (classes,methods, properties, static/instance objects, exceptions and handling, and to a lesser extent things like inheritance, abstract/sealed/override) are essential.  Its boring, and repetitive, and takes a lot of work to get it straight, but like most things along those lines the payoff is worth it.  Trying to back into the subject through AutoCAD will raise a lot of questions of "Why do that?" or "Thats pretty vague", or "I don't understand when you say XXXX".  Like when you are asking why define this, then redefine as that, then redefine as something else - this may simply be a matter of a line being an entity being an object, but an object not necessarily being an entity, which is not necessarily a line (inheritance).

Plus, the basics are common across a lot of different samples in VB.NET, C#, C++, along with the host of support namespaces in dotNET like System.XML and System.Data.  A strong base allows you to figure out how to implement somethng even if its not exactly what you are looking for.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 08, 2013, 08:04:06 PM
Upon reflection, I think you're right Tony. There is AcDbCurve::getOffsetCurves(), which would be the logical way to generate the new entity. If xdata is being copied, I wonder if it's done by getOffsetCurves() or by the OFFSET command. If the latter, then Kean's solution doesn't seem so awful after all.

Hi Owen, there's nothing 'awful' about Kean's solution. But I took exception to his having commandWillStart, and commandEnded/Failed/Cancelled notifications running full-time because it isn't necessary. If you search ADN, you'll find a code sample written by Cyrille Fauvel, showing correct and efficient use of reactors (in native ObjectARX/C++), where he goes to great lengths to demonstrate how to avoid having reactors running when not absolutely necessary, including the scenario involving command reactors. The concepts demonstrated in his sample apply equally to managed events (that are merely wrappers around native reactor notifications), and I consider it required reading for anyone using either native or managed ObjectARX.

Kean's solution is actually one that follows a very familar and well-known pattern (collecting ObjectIds from a reactor notification where the notifying object cannot be modified, and then at the end of the command, opening the collected ids and operating on them). That pattern has for a very long time, been the standard advice that ADN support techs and others at Autodesk have suggested folks use to solve a class of problems like the one Kean's solution solves belongs to. It is nothing new, and certainly not something germain to Kean's solution.

However that pattern and approach is in my opinion, a horrible kludge, because it requires you to collect ObjectIds and then after the referenced objects have already been closed, reopen them again to do what must be done.

The solution I show that uses an Overrule to solve the same problem, is in my opinion, infinitely better, because it doesn't require you to collect ObjectIds or even re-open the objects to do what must be done. That's because from the Close() notification of an ObjectOverrule, you can modify the notifying object without limitation. I don't think anyone can argue that it isn't a preferable way to solve the problem.

The solution I show and Kean's solution are similar in that both require command begin/end notification. They differ in how the affected objects are modified, and in the fact that my solution was built using a lot of existing, reusable classes that I've used to solve similar problems in the past.
Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 10, 2013, 04:42:21 PM
I posted an older version of the class below in the above post (the more recent version has dependencies on other code that I would've had to remove). There is a bug in that version that was fixed in later versions, and if you're going to adopt it, then you need to either fix the bug or use the version below, which has the bug fixed already. The bug only happens if a transparent command is started in the command that's being watched, which causes the end-command notification to be sent prematurely.

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Autodesk.AutoCAD.DatabaseServices;
  6. using Autodesk.AutoCAD.Runtime;
  7. using System.ComponentModel;
  8.  
  9. namespace Autodesk.AutoCAD.ApplicationServices.MyExtensions
  10. {
  11.  
  12.    /// <summary>
  13.    /// Get notified about the start and end of certain
  14.    /// commands, without the need for full-time handling
  15.    /// of end-command events;
  16.    /// </summary>
  17.  
  18.    public abstract class CommandObserver : IDisposable
  19.    {
  20.       bool disposed = false;
  21.       Document doc = null;
  22.       bool handled = false;
  23.       string current = null;
  24.  
  25.       public CommandObserver( Document doc )
  26.       {
  27.          if( doc == null )
  28.             throw new ArgumentNullException( "doc" );
  29.          this.doc = doc;
  30.          this.doc.CommandWillStart += commandWillStart;
  31.       }
  32.  
  33.       public enum State
  34.       {
  35.          Starting = 0,
  36.          Ended = 1,
  37.          Cancelled = 2,
  38.          Failed = 3
  39.       }
  40.  
  41.       public Document Document
  42.       {
  43.          get
  44.          {
  45.             return this.doc;
  46.          }
  47.       }
  48.  
  49.       void commandWillStart( object sender, CommandEventArgs e )
  50.       {
  51.          if( OnCommandStarting( e.GlobalCommandName ) )
  52.          {
  53.             current = e.GlobalCommandName;
  54.             AddEndHandlers();
  55.          }
  56.       }
  57.  
  58.       void AddEndHandlers()
  59.       {
  60.          if( !this.handled )
  61.          {
  62.             doc.CommandEnded += doc_CommandEnded;
  63.             doc.CommandFailed += doc_CommandFailed;
  64.             doc.CommandCancelled += doc_CommandCancelled;
  65.             this.handled = true;
  66.          }
  67.       }
  68.  
  69.       void RemoveEndHandlers()
  70.       {
  71.          if( this.handled )
  72.          {
  73.             doc.CommandEnded -= doc_CommandEnded;
  74.             doc.CommandFailed -= doc_CommandFailed;
  75.             doc.CommandCancelled -= doc_CommandCancelled;
  76.             this.handled = false;
  77.             this.current = null;
  78.          }
  79.       }
  80.  
  81.       /// Overrides should return true to be notified
  82.       /// when a command ends (otherwise, there is no
  83.       /// end-of-command notification).
  84.  
  85.       protected virtual bool OnCommandStarting( string name )
  86.       {
  87.          return false;
  88.       }
  89.  
  90.       protected virtual void OnCommandEnded( string name, State state )
  91.       {
  92.       }
  93.  
  94.       void doc_CommandCancelled( object sender, CommandEventArgs e )
  95.       {
  96.          if( e.GlobalCommandName == current )
  97.          {
  98.             RemoveEndHandlers();
  99.             OnCommandEnded( current, State.Cancelled );
  100.          }
  101.       }
  102.  
  103.       void doc_CommandFailed( object sender, CommandEventArgs e )
  104.       {
  105.          if( e.GlobalCommandName == current )
  106.          {
  107.             RemoveEndHandlers();
  108.             OnCommandEnded( current, State.Failed );
  109.          }
  110.       }
  111.  
  112.       void doc_CommandEnded( object sender, CommandEventArgs e )
  113.       {
  114.          if( e.GlobalCommandName == current )
  115.          {
  116.             RemoveEndHandlers();
  117.             OnCommandEnded( current, State.Ended );
  118.          }
  119.       }
  120.  
  121.       protected virtual void Dispose( bool disposing )
  122.       {
  123.          if( !disposed )
  124.          {
  125.             doc.CommandWillStart -= this.commandWillStart;
  126.             RemoveEndHandlers();
  127.             disposed = true;
  128.          }
  129.       }
  130.  
  131.       public void Dispose()
  132.       {
  133.          Dispose( true );
  134.       }
  135.    }
  136.  
  137.  

Title: Re: Third Routine - Working Progress
Post by: TheMaster on February 10, 2013, 04:55:02 PM
(continued...)

That version is about 5 years old, and has come a long way since then. Just to show how much more involved it became in order to support efficient use of Overrules, here is the current working version pasted directly from the source code (it isn't going to work as-is because the dependencies aren't included - sorry).

Code - C#: [Select]
  1.  
  2. [DefaultBindingProperty( focusedProperty )]
  3. public abstract class CommandObserver : Switchable, INotifyPropertyChanged
  4. {
  5.    const string focusedProperty = "Focused";
  6.    const string activeProperty = "IsActive";
  7.    const string lispName = "*LISP*";
  8.    Document doc = null;
  9.    DocumentCollection docs = Application.DocumentManager;
  10.    bool handled = false;
  11.    string current = null;
  12.    string nested = null;
  13.    State state = State.None;
  14.    bool ignoreLisp = false;
  15.  
  16.    public CommandObserver( Document doc, bool enabled = true )
  17.       : base( false )
  18.    {
  19.       if( doc == null )
  20.          throw new ArgumentNullException( "doc" );
  21.       this.doc = doc;
  22.       base.Enabled = enabled;
  23.    }
  24.  
  25.    public enum Status
  26.    {
  27.       Starting = 0,
  28.       Ended = 1,
  29.       Cancelled = 2,
  30.       Failed = 3
  31.    }
  32.  
  33.    [Flags]
  34.    public enum State
  35.    {
  36.       None = 0,
  37.  
  38.       /// <summary>
  39.       /// The observed command has been
  40.       /// started and has not yet ended.
  41.       /// </summary>
  42.       Active = 1,
  43.  
  44.       /// <summary>
  45.       /// The observed command is active and
  46.       /// is in use in the active document.
  47.       /// </summary>
  48.       Focused = 2,
  49.  
  50.       /// <summary>
  51.       /// The observed command is suspended while
  52.       /// a transparently-issued command or LISP
  53.       /// expression is in progress.
  54.       /// </summary>
  55.       Modeless = 4,
  56.  
  57.       /// <summary>
  58.       /// The document in which an active observed
  59.       /// command was started, is currently not the
  60.       /// active document.
  61.       /// </summary>
  62.       Document = 8,
  63.  
  64.       /// <summary>
  65.       /// The observed command is suspended while
  66.       /// a LISP expression is being evaluated.
  67.       /// </summary>
  68.       Lisp = 16
  69.    }
  70.  
  71.    public Document Document
  72.    {
  73.       get
  74.       {
  75.          return this.doc;
  76.       }
  77.    }
  78.  
  79.    public State CurrentState
  80.    {
  81.       get
  82.       {
  83.          return state;
  84.       }
  85.    }
  86.  
  87.    /// <summary>
  88.    ///
  89.    /// True = the observed command has started, the Document
  90.    /// in which the observed command was started is active,
  91.    /// and the observed command is not currently suspended
  92.    /// while another transparently-issued command or LISP
  93.    /// expression is active.
  94.    ///
  95.    /// This property can change between the points when an
  96.    /// observed command starts and ends, depending on if
  97.    /// a transparently-issued command is in-progress, LISP
  98.    /// is running, and if the document in which the observed
  99.    /// command was started is currently active.
  100.    ///
  101.    /// </summary>
  102.  
  103.    [Bindable( true )]
  104.    public bool Focused
  105.    {
  106.       get
  107.       {
  108.          return state.HasFlag( State.Focused );
  109.          // return this.doc.IsActive && this.current != null && this.nested == null;
  110.       }
  111.    }
  112.  
  113.    /// <summary>
  114.    /// True = An observed command was started, but has not yet
  115.    /// ended. The command may not be focused (because the document
  116.    /// is not the active document, or because the observed command
  117.    /// was interrupted by another currently-active command or LISP
  118.    /// function that was issued transparently).
  119.    ///
  120.    /// Unlike the Focused property, this property's value remains
  121.    /// true between the point where an observed command is started
  122.    /// and the point where it ends.
  123.    ///
  124.    /// </summary>
  125.  
  126.    [Bindable( true )]
  127.    public bool IsActive
  128.    {
  129.       get
  130.       {
  131.          return this.current != null;
  132.       }
  133.    }
  134.  
  135.    /// <summary>
  136.    ///
  137.    /// The active observed command, or an
  138.    /// empty string if no observed command
  139.    /// is active.
  140.    ///
  141.    /// </summary>
  142.  
  143.    [Bindable( true )]
  144.    public string Current
  145.    {
  146.       get
  147.       {
  148.          return this.current ?? string.Empty;
  149.       }
  150.    }
  151.  
  152.    /// <summary>
  153.    ///
  154.    /// The name of the transparently-issued command
  155.    /// that has interrupted the observed command, while
  156.    /// the observed command is interrupted.
  157.    ///
  158.    /// The value of this property is not necessarily the
  159.    /// name of a currently active, transparently-issued
  160.    /// command. The names of nested transparently-issued
  161.    /// commands or LISP expressions that do not change
  162.    /// the Focused state of the observed command are not
  163.    /// returned in this property.
  164.    ///
  165.    /// A 'nested' transparently-issued command is a command
  166.    /// that was transparently issued while another transparently-
  167.    /// issued command was in progress.
  168.    ///
  169.    /// If the observed command was interrupted by a LISP
  170.    /// expression, the value of this property will be the
  171.    /// value of the LispName property
  172.    ///
  173.    /// </summary>
  174.  
  175.    public string Nested
  176.    {
  177.       get
  178.       {
  179.          return this.nested ?? string.Empty;
  180.       }
  181.    }
  182.  
  183.    /////////////////////////////////////////////////////////////
  184.    /// Overridables:
  185.  
  186.    /// <summary>
  187.    ///
  188.    /// <warning>
  189.    /// It is critically-necessary for overrides in
  190.    /// all derived types to supermessage this.
  191.    /// </warning>
  192.    ///
  193.    /// Called when the instance is enabled or disabled
  194.    /// by setting the Enabled property.
  195.    ///
  196.    /// When the instance is not enabled, all command-
  197.    /// related activity, including starting of commands,
  198.    /// is disabled.
  199.    ///
  200.    /// </summary>
  201.  
  202.    protected override void OnEnabledChanged( bool value )
  203.    {
  204.       if( value )
  205.       {
  206.          this.doc.CommandWillStart += commandWillStart;
  207.       }
  208.       else
  209.       {
  210.          this.doc.CommandWillStart -= commandWillStart;
  211.          RemoveHandlers();
  212.       }
  213.       NotifyPropertyChanged( "Enabled" );
  214.    }
  215.  
  216.    /// <summary>
  217.    ///
  218.    /// Indicates that a command is starting. overrides
  219.    /// should return true to indicate if the command
  220.    /// that is starting should be observed.
  221.    ///
  222.    /// If false is returned, the command's state is not
  223.    /// observed and all other notifications, including
  224.    /// when the command ends are not sent.
  225.    ///
  226.    /// </summary>
  227.    /// <param name="name">The global name of the command that is starting</param>
  228.    /// <returns>A value indicating if the command should be observed</returns>
  229.  
  230.    protected abstract bool OnCommandStarting( string name );
  231.  
  232.    /// <summary>
  233.    /// Indicates that an observed command has ended.
  234.    /// </summary>
  235.    /// <param name="name">The name of the command that has ended</param>
  236.    /// <param name="status">A value indicating how the command ended</param>
  237.  
  238.    protected virtual void OnCommandEnded( string name, Status status )
  239.    {
  240.    }
  241.  
  242.    /// <summary>
  243.    ///
  244.    /// Called when the 'focused' state of an observed
  245.    /// command changes.
  246.    ///
  247.    /// An observed command is in the focused state
  248.    /// when all of the following conditions are true:
  249.    ///
  250.    ///   - The observed command has been started,
  251.    ///     and has not yet ended.
  252.    ///  
  253.    ///   - The document in which the observed
  254.    ///     command was started is the active
  255.    ///     document.
  256.    ///  
  257.    ///   - The observed command is not currently
  258.    ///     suspended while another transparently-
  259.    ///     issued command or LISP expression is
  260.    ///     active or in-progress.
  261.    ///  
  262.    /// OnFocusdChanged() is called when a change
  263.    /// in any of the above conditions causes the
  264.    /// focused state of an observed command to
  265.    /// change.
  266.    ///
  267.    /// Specifically:
  268.    ///
  269.    /// - When the observed comand starts and ends.
  270.    ///
  271.    /// - When a transparent command starts or ends
  272.    ///   while the observed command is in progress,
  273.    ///   but not when additional commands are issued
  274.    ///   transparently while a transparently-issued
  275.    ///   command is active.
  276.    ///  
  277.    /// - When the document in which an observed command
  278.    ///   was started is activated or deactivated, while
  279.    ///   no transparently-issued command is active.
  280.    ///
  281.    ///   Nested, transparently-issued commands that
  282.    ///   are issued while other transparently-issued
  283.    ///   commands are active, do not alter the focused
  284.    ///   state of the observed command.
  285.    ///  
  286.    ///   Changes to the active document that occur
  287.    ///   while a transparently-issued command is in
  288.    ///   progress, do not alter the focused state of
  289.    ///   the observed command.
  290.    ///  
  291.    ///   observed is the name of the observed command.
  292.    ///  
  293.    ///   The focused parameter indicates if the observed
  294.    ///   command has become focused, as it is defined
  295.    ///   above. True means the command has entered the
  296.    ///   focused state. False indicates the command has
  297.    ///   left the focused state.
  298.    ///  
  299.    ///   state indicates the condition(s) that changed,
  300.    ///   resulting in a change to the the focused state,
  301.    ///   as follows:
  302.    ///  
  303.    ///   State.Active: The observed command started or ended
  304.    ///  
  305.    ///   State.Modeless: The observed command was interrupted
  306.    ///   when a transparently-issued command was started, or
  307.    ///   was resumed when a transparently-issued command ended.
  308.    ///  
  309.    ///   State.Lisp: The observed command was interrupted
  310.    ///   when a LISP expression was evaluated, or was resumed
  311.    ///   when the LISP expression's evaluation is completed.
  312.    ///   When this flag is present, the Modeless flag is also
  313.    ///   present.
  314.    ///  
  315.    ///   State.Document: The active state of the document in
  316.    ///   which the observed command was started has changed.
  317.    ///
  318.    /// </summary>
  319.  
  320.    protected virtual void OnFocusChanged( string observed, bool focused, State state )
  321.    {
  322.    }
  323.  
  324.    /// <summary>
  325.    ///
  326.    /// Called when an observed command that is currently
  327.    /// active, is suspended or interrupted as a result of
  328.    /// another command having been issued transparently,
  329.    /// or by evaluation of LISP.
  330.    ///
  331.    /// nested is the name of the interrupting, transparently-
  332.    /// issued command that is starting. If the interruption
  333.    /// is a result of a LISP expression, the value of this
  334.    /// parameter will be the value of the LispName property.
  335.    ///
  336.    /// </summary>
  337.  
  338.    protected virtual void OnSuspend( string current, string nested )
  339.    {
  340.    }
  341.  
  342.    /// <summary>
  343.    ///
  344.    /// Called when an observed command that is currently
  345.    /// in-progress and has been suspended or interrupted
  346.    /// by the use of a transparent command or LISP, is
  347.    /// resumed as a result of the transparent command or
  348.    /// LISP ending.
  349.    ///
  350.    /// The first argument is the name of the active observed
  351.    /// command.
  352.    ///
  353.    /// The second argument is the name of the transparently-
  354.    /// issued command that is ending, or the value of the
  355.    /// LispName property if the interruption was triggered
  356.    /// by evalution of a LISP expression.
  357.    ///
  358.    /// </summary>
  359.  
  360.    protected virtual void OnResume( string current, string nested )
  361.    {
  362.    }
  363.  
  364.    /// <summary>
  365.    ///
  366.    /// Called when a document containing the Focused, observed
  367.    /// command is deactivated, as a result of a change to the
  368.    /// active document. This notification is sent only if the
  369.    /// observed command is focused. If the observed command is
  370.    /// not currently focused (e.g., the command is suspended
  371.    /// while one or more transparently-issued commands are in
  372.    /// progress) the notification is not sent.
  373.    ///
  374.    /// Calls to this notification are followed by a call to
  375.    /// the OnDocumentActivated() method when the document is
  376.    /// subsequently activated again.
  377.    ///
  378.    /// </summary>
  379.  
  380.    protected virtual void OnDocumentDeactivated( string current )
  381.    {
  382.    }
  383.  
  384.    /// <summary>
  385.    ///
  386.    /// Called when a non-active document containing a Focused,
  387.    /// observed command is activated. This notification is sent
  388.    /// only if the observed command is focused. If the observed
  389.    /// command is not currently focused (e.g., the command is
  390.    /// suspended while one or more transparently-issued commands
  391.    /// are in progress) the notification is not sent.
  392.    ///
  393.    /// Calls to this notification are always preceded by a call
  394.    /// to the OnDocumentDeactivated() method.
  395.    ///
  396.    /// </summary>
  397.  
  398.    protected virtual void OnDocumentActivated( string current )
  399.    {
  400.    }
  401.  
  402.    /////////////////////////////////////////////////////////////
  403.    /// Non-public implementation
  404.  
  405.    void SetState( State flags, bool value = true )
  406.    {
  407.       if( value )
  408.          state |= flags;
  409.       else
  410.          state &= ~flags;
  411.    }
  412.  
  413.    void commandWillStart( object sender, CommandEventArgs e )
  414.    {
  415.       OnStarting( e.GlobalCommandName );
  416.    }
  417.  
  418.    void OnStarting( string name )
  419.    {
  420.       if( current == null ) // ! state.HasFlag( State.Active ) )
  421.       {
  422.          if( OnCommandStarting( name ) )
  423.          {
  424.             current = name;
  425.             AddHandlers();
  426.             state = State.Active;
  427.             StateChanged( true, State.Active );
  428.          }
  429.       }
  430.       else if( nested == null ) // ! state.HasFlag( State.Modeless ) )
  431.       {
  432.          nested = name;
  433.          OnSuspend( current, nested );
  434.          State changing = GetModelessState( name );
  435.          SetState( changing );
  436.          StateChanged( false, changing );
  437.       }
  438.    }
  439.  
  440.    void OnEnding( string name, Status status )
  441.    {
  442.       if( name == current )
  443.       {
  444.          try
  445.          {
  446.             RemoveHandlers();
  447.             StateChanged( false, State.Active );
  448.             state = State.None;
  449.             OnCommandEnded( name, status );
  450.          }
  451.          finally
  452.          {
  453.             current = null;
  454.          }
  455.       }
  456.       else if( name == nested )
  457.       {
  458.          try
  459.          {
  460.             State changing = GetModelessState( name );
  461.             SetState( changing, false );
  462.             StateChanged( true, changing );
  463.             OnResume( current, name );
  464.          }
  465.          finally
  466.          {
  467.             nested = null;
  468.          }
  469.       }
  470.    }
  471.  
  472.    State GetModelessState( string name )
  473.    {
  474.       return name == lispName ? State.Modeless | State.Lisp : State.Modeless;
  475.    }
  476.  
  477.    void documentActivated( object sender, DocumentCollectionEventArgs e )
  478.    {
  479.       if( e.Document == this.doc )
  480.       {
  481.          if( nested == null ) // ( state & ~ State.Document ) == State.Active )
  482.          {
  483.             OnDocumentActivated( current );
  484.             StateChanged( true, State.Document );
  485.          }
  486.          SetState( State.Document, false );
  487.       }
  488.    }
  489.  
  490.    void documentToBeDeactivated( object sender, DocumentCollectionEventArgs e )
  491.    {
  492.       if( e.Document == this.doc )
  493.       {
  494.          if( nested == null ) // state == State.Active ) // nested == null )
  495.          {
  496.             OnDocumentDeactivated( current );
  497.             StateChanged( false, State.Document );
  498.          }
  499.          SetState( State.Document );
  500.       }
  501.    }
  502.  
  503.    void StateChanged( bool focused, State changed )
  504.    {
  505.       SetState( State.Focused, focused );
  506.       OnFocusChanged( this.current, focused, changed );
  507.       if( PropertyChanged != null )
  508.       {
  509.          NotifyPropertyChanged( focusedProperty );
  510.          if( changed == State.Active )
  511.             NotifyPropertyChanged( activeProperty );
  512.       }
  513.       MyUtils.WriteDebugMessage( "*** OnFocusedChanged( {0}, {1}, {2} ) ***",
  514.          current, focused, changed );
  515.    }
  516.  
  517.    void NotifyPropertyChanged( string name )
  518.    {
  519.       if( PropertyChanged != null )
  520.          PropertyChanged( this, new PropertyChangedEventArgs( name ) );
  521.    }
  522.  
  523.    void AddHandlers()
  524.    {
  525.       if( !this.handled )
  526.       {
  527.          this.handled = true;
  528.          doc.CommandEnded += commandEnded;
  529.          doc.CommandFailed += commandFailed;
  530.          doc.CommandCancelled += commandCancelled;
  531.          docs.DocumentActivated += documentActivated;
  532.          docs.DocumentToBeDeactivated += documentToBeDeactivated;
  533.          if( ! ignoreLisp )
  534.          {
  535.             doc.LispWillStart += lispWillStart;
  536.             doc.LispEnded += lispEnded;
  537.             doc.LispCancelled += lispCancelled;
  538.          }
  539.       }
  540.    }
  541.  
  542.    void RemoveHandlers()
  543.    {
  544.       if( this.handled )
  545.       {
  546.          this.handled = false;
  547.          doc.CommandEnded -= commandEnded;
  548.          doc.CommandFailed -= commandFailed;
  549.          doc.CommandCancelled -= commandCancelled;
  550.          docs.DocumentToBeDeactivated -= documentToBeDeactivated;
  551.          docs.DocumentActivated -= documentActivated;
  552.          if( ! ignoreLisp )
  553.          {
  554.             doc.LispWillStart -= lispWillStart;
  555.             doc.LispEnded -= lispEnded;
  556.             doc.LispCancelled -= lispCancelled;
  557.          }
  558.       }
  559.    }
  560.  
  561.    public bool IgnoreLisp
  562.    {
  563.       get
  564.       {
  565.          return ignoreLisp;
  566.       }
  567.       set
  568.       {
  569.          ignoreLisp = value;
  570.       }
  571.    }
  572.  
  573.    public static string LispName
  574.    {
  575.       get
  576.       {
  577.          return lispName;
  578.       }
  579.    }
  580.  
  581.    public static bool IsLisp( string name )
  582.    {
  583.       return name == lispName;
  584.    }
  585.  
  586.    void lispWillStart( object sender, LispWillStartEventArgs e )
  587.    {
  588.       OnStarting( lispName );
  589.    }
  590.  
  591.    void lispCancelled( object sender, EventArgs e )
  592.    {
  593.       OnEnding( lispName, Status.Cancelled );
  594.    }
  595.  
  596.    void lispEnded( object sender, EventArgs e )
  597.    {
  598.       OnEnding( lispName, Status.Ended );
  599.    }
  600.  
  601.    void commandCancelled( object sender, CommandEventArgs e )
  602.    {
  603.       OnEnding( e.GlobalCommandName, Status.Cancelled );
  604.    }
  605.  
  606.    void commandFailed( object sender, CommandEventArgs e )
  607.    {
  608.       OnEnding( e.GlobalCommandName, Status.Failed );
  609.    }
  610.  
  611.    void commandEnded( object sender, CommandEventArgs e )
  612.    {
  613.       OnEnding( e.GlobalCommandName, Status.Ended );
  614.    }
  615.  
  616.    public event PropertyChangedEventHandler PropertyChanged;
  617. }
  618.  
  619.  
Title: Re: Third Routine - Working Progress
Post by: dgorsman on February 11, 2013, 10:29:44 AM
Like I said... the first thing programmers will tell me is go back to the basics lol.... couldn't resist could ya

Knowing the basics of object-oriented programming (classes,methods, properties, static/instance objects, exceptions and handling, and to a lesser extent things like inheritance, abstract/sealed/override) are essential.  Its boring, and repetitive, and takes a lot of work to get it straight, but like most things along those lines the payoff is worth it.  Trying to back into the subject through AutoCAD will raise a lot of questions of "Why do that?" or "Thats pretty vague", or "I don't understand when you say XXXX".  Like when you are asking why define this, then redefine as that, then redefine as something else - this may simply be a matter of a line being an entity being an object, but an object not necessarily being an entity, which is not necessarily a line (inheritance).

Plus, the basics are common across a lot of different samples in VB.NET, C#, C++, along with the host of support namespaces in dotNET like System.XML and System.Data.  A strong base allows you to figure out how to implement somethng even if its not exactly what you are looking for.

I'm not a programmer   ;-)