Command event in MdiActiveDocument

Command event in MdiActiveDocument

Anonymous
Not applicable
2,484 Views
16 Replies
Message 1 of 17

Command event in MdiActiveDocument

Anonymous
Not applicable
R2006 VS 2005 Express,net2.0

In the ToDo section of my initializeComponent() call for a modeless dialog, I add two event handlers:
AcadApp.DocumentManager.DocumentActivated += new DocumentCollectionEventHandler(DocumentManager_DocumentActivated);

and:

AcadApp.DocumentManager.MdiActiveDocument.CommandEnded += new CommandEventHandler(MdiActiveDocument_CommandEnded);

When I change drawings in the editor (SDI=1), the documentActivated event fires everytime refreshing the dialog with the info in the newly activated drawing.
But the commandEnded event only fires in the initial drawing (I use it for the "INSERT" command) that is open when I netload the dialog.dll.

Do I have to create a new event handler each time a document is made the MDIActiveDocument?
Or is ther another way to do this?

TIA

Bill Message was edited by: BillZ
0 Likes
2,485 Views
16 Replies
Replies (16)
Message 2 of 17

Anonymous
Not applicable
You handle the DocumentAdded event and in the handler,
you add the handler for the CommandEnded (and any other
Document- and Editor-specific events).

The CommandEnded and other events of the Document object
only fire for the document that you've added a handler for.

Also, if your assembly may not be loaded at startup, then
when it does load, you should also iterate over all existing
Documents in the DocumentCollection, and add handlers for
Document events to those as well.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6016279@discussion.autodesk.com...
R2006 VS 2005 Express,net2.0

In the ToDo section of my initializeComponent() call for a modeless dialog, I add two event handlers:
AcadApp.DocumentManager.DocumentActivated += new DocumentCollectionEventHandler(DocumentManager_DocumentActivated);

and:

AcadApp.DocumentManager.MdiActiveDocument.CommandEnded += new CommandEventHandler(MdiActiveDocument_CommandEnded);

When I change drawings in the editor (SDI=1), the documentActivated event fires everytime refreshing the dialog with the info in the newly activated drawing.
But the commandEnded event only fires in the initial drawing (I use it for the "INSERT" command) that is open when I netload the dialog.dll.

Do I have to create a new event handler each time a document is made the MDIActiveDocument?
Or is ther another way to do this?

TIA

Bill

Message was edited by: BillZ
0 Likes
Message 3 of 17

Anonymous
Not applicable
Thanks Tony, that was fast!

I had a suspicion the commandEnded event was that way.

This app is loaded at startup for the users.

Bill
0 Likes
Message 4 of 17

Anonymous
Not applicable
Tony,

It appears that it may still be good for me to iterate existing documents, because although my app.dll is loaded at startup,
the event handlers are not constructed until the InitializeComponent call, that means the command method must be called by the user to initiate the event handlers and there may already be drawings opened by that time.

Two questions:

1.) Is there a place I can construct the event handlers , so they will be initiated when the app.dll is loaded and the command method has not yet been called?

2.) If not, you say in this post:
http://discussion.autodesk.com/thread.jspa?messageID=5739408
"then you also
have to iterate over the collection of currently open
documents, and add document events handlers to
each of them.
There's sample code showing how to do this on my
website, but its in C#. "

I looked but could not find the C# sample on the caddzone site.


Bill
0 Likes
Message 5 of 17

Anonymous
Not applicable
Bill,

I don't know about the first question you had, but Tony helped me on the second when I had the same question. Here is the code I use.

[code]
if (FirstLoad) {
foreach (Document Doc in AcadApp.DocumentManager) {
Doc.Editor.SelectionAdded += new SelectionAddedEventHandler(UserControl1.SelectionMade);
}
AcadApp.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(UserControl1.onDocCreated);
AcadApp.DocumentManager.DocumentToBeDestroyed += new DocumentCollectionEventHandler(UserControl1.onDocToBeDestroyed);
AcadApp.DocumentManager.DocumentBecameCurrent += new DocumentCollectionEventHandler(UserControl1.onDocBecameCurrent);
FirstLoad = false;
}

public static void onDocCreated (object sender, DocumentCollectionEventArgs e) {
Document newDoc = e.Document;
newDoc.Editor.SelectionAdded += new SelectionAddedEventHandler(SelectionMade);
}

public static void onDocToBeDestroyed (object sender, DocumentCollectionEventArgs e) {
Document Doc = e.Document;
Doc.Editor.SelectionAdded -= new SelectionAddedEventHandler(SelectionMade);
}

public static void onDocBecameCurrent (object sender, DocumentCollectionEventArgs e) {
BlockInForm = ObjectId.Null;
if (!ps.Visible || AcadApp.GetSystemVariable("CmdActive").Equals((short)0)) return;
PromptSelectionResult psr = e.Document.Editor.SelectImplied() as PromptSelectionResult;
if (psr == null) return;
SelectionSet ss = psr.Value as SelectionSet;
if (ss != null && ss.Count > 0) UserControl1.FillInControl(ss.GetObjectIds());
else if (ps.Count > 0) ps.Remove(0);
}
[/code]
0 Likes
Message 6 of 17

Anonymous
Not applicable
Thanks Tim.

What you posted will do rather nicely. 🙂


Bill
0 Likes
Message 7 of 17

Anonymous
Not applicable
You're welcome Bill. I'm looking, and I guess you don't need the code for the OnDocBecamesCurrent, as that is code to run my program. But I'm glad it could help.
0 Likes
Message 8 of 17

Anonymous
Not applicable
I just needed to know how to add the event handler to the drawings that were already open, without any redunancy in the operation.
I already have a checker that tells me if the command method had created an object reference to the dialog (or not), so I'll try to set a value to the "FirstLoad" from there.

Bill
0 Likes
Message 9 of 17

Anonymous
Not applicable
>> It appears that it may still be good for me to iterate existing documents,
>> because although my app.dll is loaded at startup, the event handlers are
>> not constructed until the InitializeComponent call,

Bill, you have to add the DocumentCreated event handler from the code that runs when your DLL loads (e.g., IExtensionApplication.Initialize). If your event handler is a member of a form, that's a mistake. It should be a seperate class that has no dependence on any forms or other controls.

If you're not familar with running code at startup via IExtensionApplication, there should be plenty of examples of using it in this newsgroup.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6017058@discussion.autodesk.com...
Tony,

that means the command method must be called by the user to initiate the event handlers and there may already be drawings opened by that time.

Two questions:

1.) Is there a place I can construct the event handlers , so they will be initiated when the app.dll is loaded and the command method has not yet been called?

2.) If not, you say in this post:
http://discussion.autodesk.com/thread.jspa?messageID=5739408
"then you also
have to iterate over the collection of currently open
documents, and add document events handlers to
each of them.
There's sample code showing how to do this on my
website, but its in C#. "

I looked but could not find the C# sample on the caddzone site.


Bill
0 Likes
Message 10 of 17

Anonymous
Not applicable
Ah, Okay.
I did run across the IExtensionApplication.Initialize in my reasearch here and have downloaded the ObjectARX docs so I'll be looking into that.

For now, this has become a balancing act
between making it work and doing it right.

Nice to have someone like you that can point out the
basic mechanics (nuts & bolts) of what to do.


Thanks Again Tony.



Bill
0 Likes
Message 11 of 17

Anonymous
Not applicable
>> wrote

>> I just needed to know how to add the event handler
>> to the drawings that were already open, without any
>> redunancy in the operation.

Bill - Sorry, I don't see what point there is to the 'FirstLoad' flag that Tim's code uses.

The constructor of your form only runs once. It can't run more than once, hence there's no need to check a flag to see if it already ran. So, the constructor is once place where you can do things that must only be done once.

If you have code that must run only once, but is dependent on your form being visible, you can have the code run only once after your form is shown the first time, by overriding the OnShown() method:

public class MyForm : Form
{
protected override void OnShown(EventArgs e)
{
MessageBox.Show("You will not see this message again");
}
}

Since Tim doesn't show where the code he posted is called from, I can only guess that the need for the 'FirstLoad' flag is because he does not have the code where it should be.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm
0 Likes
Message 12 of 17

Anonymous
Not applicable
Okay,

This is how I did the event handling:

public class PartManager1

: Autodesk.AutoCAD.Runtime.IExtensionApplication
{
public void Initialize()
{
InitiateDocEventHandlers InitEventHandlers = new InitiateDocEventHandlers();
InitEventHandlers.AddEventHandlersToDocsOpen();
}

public void Terminate()
{
Console.WriteLine("Cleaning up...");
}

[CommandMethod("Partmanager", CommandFlags.Session)]
public static void ShowModelessDialog()
{
if (MainDialog == null)
MainDialog = new DialogMain();

if(!MainDialog.Visible)
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(MainDialog);

}
private static DialogMain MainDialog = null;
}

}

//***This is what the InitiateEventHandlers looks like:***

namespace PartMan
{
public class InitiateDocEventHandlers
{
public void AddEventHandlersToDocsOpen()
{
try
{
DocumentCollection docCol = AcadApp.DocumentManager;
IEnumerator docEnum = docCol.GetEnumerator();
while (docEnum.MoveNext())
{
Document doc = (Document)docEnum.Current;
doc.CommandEnded += new CommandEventHandler(doc_CommandEnded);
}
AcadApp.DocumentManager.DocumentActivated += new DocumentCollectionEventHandler(DocumentManager_DocumentActivated);
AcadApp.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
}
catch (System.Exception e)
{
MessageBox.Show("\n EventHandlers kaputt! " + e);
}
}
//Methods for event handlers here....
void doc_CommandEnded(object sender, CommandEventArgs e)
{
if (e.GlobalCommandName.Equals("INSERT"))
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListAfterInsert();

}

}
void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
{
AcadApp.DocumentManager.MdiActiveDocument.CommandEnded += new CommandEventHandler(MdiActiveDocument_CommandEnded);
}
void MdiActiveDocument_CommandEnded(object sender, CommandEventArgs e)
{
if (e.GlobalCommandName.Equals("INSERT"))
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListAfterInsert();

}
}
public void DocumentManager_DocumentActivated(object sender, DocumentCollectionEventArgs e)
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListWhenDrawingActivated();

}
}
}

i'm sure there are still 100 things wrong with this so if anyone can see some glaring deficiencies, keep it short. 🙂

Thanks again Tony!

Bill
0 Likes
Message 13 of 17

Anonymous
Not applicable
public class MyForm : Form
{
protected override void OnShown(EventArgs e)
{
MessageBox.Show("You will not see this message again");
}
}

Thanks Tony.

This is good stuff!

I tried doing some things that happen in my constructor using this method and it worked about the same for my uses.

FWIW:
I had to add a check for null value in my event methods that access the form controls but was tickled to finally figure out how to manipulate things in the form from another class.
If you hadn't of brought up the correct way to add events, I probably wouldn't have looked into doing that.

if (dlgmn != null)
dlgmn.RefreshListAfterInsert();

Again thanks

Bill
0 Likes
Message 14 of 17

Anonymous
Not applicable
Sorry, I didn't realize that you were using a modeless form. In that case, you can dispense with the seperate class for the events, but only if you do not allow your form to Dispose itself when the user closes it (I showed how to do this in another post here, but here it is again):

protected override void OnFormClosing( FormClosingEventArgs e )
{
base.OnFormClosing( e );
e.Cancel = true;
this.Visible = false;
}

This will ensure that when the user closes your form it will not be disposed, and you can reference the same instance of the form for the life of your application. In that case, you should check the form's Visible property and avoid doing things that you shouldn't or don't need to do if the form is not currently visible.

Given that your form is modeless and a singleton, there's nothing wrong with making the event handlers methods of the form. So you should probably dispense with the use of a seperate class, and make the event handlers members of your form, and call the code to add the DocumentCollection event handlers from an override of your form's OnShown() method (just move the methods of your InitiateDocEventHandlers class to the form):

public class MyForm : Form
{
proctected override OnShown( EventArgs e )
{
base.OnShown( e );
AddEventHandlersToDocsOpen(); // make this a member of your form
}

// ......
}

Your form is effectively a 'singleton' (Wiki the 'Singleton pattern' for more about what a singleton is). When you need to reference a singleton object from the outside, you use the singleton pattern, which generally involves exposing the single instance of the class through a static property, like this:

public class MyForm : Form
{
private static MyForm instance = null;

public static MyForm Instance
{
get
{
if( instance == null )
instance = new MyForm();
return instance;
}
}
}

With the above, if you need to reference your form from another class, you can do that with this:

MyForm myForm = MyForm.Instance;

And so, the CommandMethod that shows the form only needs to do this:

(using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;)

AcadApp.ShowModelessDialog( MyForm.Instance );

BTW, using 'System.Windows.Forms.Application.OpenForms["DialogMain"]' to reference a form is definitely not a good idea (it may not necessarily return the same instance of the form).

Lastly, consider not needlessly complicating your code with the direct use of IEnumerable methods. You generlly don't use IEnumerable directly, instead you just use foreach().

The only other question I have, is why use a modeless form, rather than a PaletteSet ? PaletteSets have some big advantages over modeless forms.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6018233@discussion.autodesk.com...
Okay,

This is how I did the event handling:

public class PartManager1

: Autodesk.AutoCAD.Runtime.IExtensionApplication
{
public void Initialize()
{
InitiateDocEventHandlers InitEventHandlers = new InitiateDocEventHandlers();
InitEventHandlers.AddEventHandlersToDocsOpen();
}

public void Terminate()
{
Console.WriteLine("Cleaning up...");
}

[CommandMethod("Partmanager", CommandFlags.Session)]
public static void ShowModelessDialog()
{
if (MainDialog == null)
MainDialog = new DialogMain();

if(!MainDialog.Visible)
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(MainDialog);

}
private static DialogMain MainDialog = null;
}

}

//***This is what the InitiateEventHandlers looks like:***

namespace PartMan
{
public class InitiateDocEventHandlers
{
public void AddEventHandlersToDocsOpen()
{
try
{
DocumentCollection docCol = AcadApp.DocumentManager;
IEnumerator docEnum = docCol.GetEnumerator();
while (docEnum.MoveNext())
{
Document doc = (Document)docEnum.Current;
doc.CommandEnded += new CommandEventHandler(doc_CommandEnded);
}
AcadApp.DocumentManager.DocumentActivated += new DocumentCollectionEventHandler(DocumentManager_DocumentActivated);
AcadApp.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
}
catch (System.Exception e)
{
MessageBox.Show("\n EventHandlers kaputt! " + e);
}
}
//Methods for event handlers here....
void doc_CommandEnded(object sender, CommandEventArgs e)
{
if (e.GlobalCommandName.Equals("INSERT"))
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListAfterInsert();

}

}
void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
{
AcadApp.DocumentManager.MdiActiveDocument.CommandEnded += new CommandEventHandler(MdiActiveDocument_CommandEnded);
}
void MdiActiveDocument_CommandEnded(object sender, CommandEventArgs e)
{
if (e.GlobalCommandName.Equals("INSERT"))
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListAfterInsert();

}
}
public void DocumentManager_DocumentActivated(object sender, DocumentCollectionEventArgs e)
{
DialogMain dlgmn = (DialogMain)System.Windows.Forms.Application.OpenForms["DialogMain"];
dlgmn.RefreshListWhenDrawingActivated();

}
}
}

i'm sure there are still 100 things wrong with this so if anyone can see some glaring deficiencies, keep it short. 🙂

Thanks again Tony!

Bill
0 Likes
Message 15 of 17

Anonymous
Not applicable
Tony,

Yes modeless form.

I already have this in my form class as you suggested awhile back. I think it works the same as the OnFormClosing?

protected override void OnClosing(CancelEventArgs e)
{ base.OnClosing(e);
e.Cancel = true; this.Visible = false;
}

My Event handlers are now part of my form class.
IEnumerable has been replaced with foreach.
Created the Instance method in the form and now my command method looks like this: (Could this actually be in the form class also?)

using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace PartMan
{
///
/// This contians the command method for "PartManager".
///

///
public class PartManager1
{
[CommandMethod("Partmanager", CommandFlags.Session)]

public static void ShowModelessDialog()
{
AcadApp.ShowModelessDialog(DialogMain.Instance);
}
}
}

>>>The only other question I have, is why use a modeless form, rather than a PaletteSet ?<<<

I started with a modeless dialog per user request (our old app was in DCL/Vlisp and the users wanted the dialog to remain on the second screen while they are working
in AutoCAD). So I guess it was a newbie decision to go modeless.
I'll have to take your word that pallette sets are better and may look into that one too as time permits.

I developed a major app in Autolisp/Vlisp, that we use a lot here at the plant, over the last 10 or 12 years. When this new project started, I just figured it was time to further my horizons and learn some new/better code (as I'm sure I'll be working on this for awhile).
It'll work out better for the company as it will be managed code so if I can retire 8^) in the next 8 to 10 years, it should be easier for them to get someone else (if they so choose) .

Anyway, thanks for all the tips and expert critique.

Here's what things look like so far (see attached):



Bill
0 Likes
Message 16 of 17

Anonymous
Not applicable
I think OnClosing and OnFormClosing are the functionally the same, except that the latter provides more detail on why the form is closing, so you can use either.

I generally avoid using methods of UI controls and forms as command methods, but as long as the method is static, it can be a member of the form. One good reason to put all command methods in a single class, is because that allows you to use the [CommandClass] attribute. Applying that attribute to the classes that have command methods allows the runtine to find and register them without having to search every type in your assembly, which can slow down loading of your assembly if it is large.

Lastly, you might want to test your code to see what it does when you start the INSERT command in more than one document (e.g., with the INSERT comand running in one document, switch to another document and start the same command then switch back to the original document and complete the INSERT command there). You may find that to be one case where your code might get confused. There's ways of dealing with that, but I'll leave that for another time.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6022141@discussion.autodesk.com...
Tony,

Yes modeless form.

I already have this in my form class as you suggested awhile back. I think it works the same as the OnFormClosing?

protected override void OnClosing(CancelEventArgs e)
{ base.OnClosing(e);
e.Cancel = true; this.Visible = false;
}

My Event handlers are now part of my form class.
IEnumerable has been replaced with foreach.
Created the Instance method in the form and now my command method looks like this: (Could this act
ually be in the form class also?)

using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace PartMan
{
///
/// This contians the command method for "PartManager".
///

///
public class PartManager1
{
[CommandMethod("Partmanager", CommandFlags.Session)]

public static void ShowModelessDialog()
{
AcadApp.ShowModelessDialog(DialogMain.Instance);
}
}
}

>>>The only
other question I have, is why use a modeless form, rather than a PaletteSet ?<<<

I started with a modeless dialog per user request (our old app was in DCL/Vlisp and the users wanted the dialog to remain on the second screen while they are working
in AutoCAD). So I guess it was a newbie decision to go modeless.
I'll have to take your word that pallette sets are better and may look into that one too as time permits.

I developed a major app in Autolisp/Vlisp, that we use a lot here at the
plant, over the last 10 or 12 years. When this new project started, I just figured it was time to further my horizons and learn some new/better code (as I'm sure I'll be working on this for awhile).
It'll work out better for the company as it will be managed code so if I can retire 8^) in the next 8 to 10 years, it should be easier for them to get someone else (if they so choose) .

Anyway, thanks for all the tips and expert critique.

Here's what things look like so far (see attached
):



Bill
0 Likes
Message 17 of 17

Anonymous
Not applicable
Thank you so much, again.

I tested with SDI=0 and found it difficult to switch between drawings when in the insert command.
But after much trying, I did manage to pull a fatal error.
If it happens here, I'll know they are working at it and need a talking too. 🙂



Bill
0 Likes