catch events in c#

S

Stephan Steiner

Hi

I'm trying to put a couple of buttons in the button list of an Outlook
contact. I can add buttons to the main Outlook window just fine, but it
seems that I need to catch "open contact" evens and then add my button to
the button list of the contact window, yet I have no clue as how to do this.

Does anybody has a few basic examples on how to do this? A reference on
Oulook events and how to catch them (in C#.. I've seen it in VB.NET but I'm
quite unable to translate that as I've never written a line of VB).

Thanks in advance
Stephan
 
S

Stephan Steiner

Ken

Unfortunately, it doesn't, I've already spent half my morning there. I've
now managed to catch the event where a contact is opened, but adding buttons
to a contact and catching events they fire is still a mystery to me.

Stephan
 
K

Ken Slovak - [MVP - Outlook]

Unfortunately the best template for Outlook COM addins is a VB 6 one called
ItemsCB. It's on the Resources page at www.microeye.com.

Generally you would use an Inspector wrapper class and collection for
Inspectors since there can be more than one open. Within the class module
you would instantiate each object you want to handle events for, such as the
Inspector events and events for Inspector.CurrentItem (like for MailItem or
ContactItem). In VB terminology they would be declared WithEvents so their
events are exposed. Inside the class module, usually on the .Open event for
the item in the Inspector you would check for any existing buttons and
delete them and set up new ones. For each new one you would make sure it has
a unique Tag property so only one wrapper class will handle the Click event
for the button. The ItemsCB sample shows an Explorer wrapper that can be
used as a model for an Inspector wrapper and I've posted Inspector wrapper
code (in VB 6) on the newsgroups a few times.

However, since I don't use .NET languages for Outlook addins I can't help
further in the C# specific things.
 
S

Stephan Steiner

Ken

Thanks for the tips. I've downloaded the code and had a look at it (as good
as someone with no VB experience can..)

In the meantime, I have managed to add my buttons where I want to have them,
thanks to a VB.NET example I found at MSDN.
Generally you would use an Inspector wrapper class and collection for
Inspectors since there can be more than one open. Within the class module
you would instantiate each object you want to handle events for, such as the
Inspector events and events for Inspector.CurrentItem (like for MailItem or
ContactItem). In VB terminology they would be declared WithEvents so their
events are exposed. Inside the class module, usually on the .Open event for
the item in the Inspector you would check for any existing buttons and
delete them and set up new ones.

That's as far as I've come now.
For each new one you would make sure it has
a unique Tag property so only one wrapper class will handle the Click event
for the button. The ItemsCB sample shows an Explorer wrapper that can be
used as a model for an Inspector wrapper and I've posted Inspector wrapper
code (in VB 6) on the newsgroups a few times.

Do you remember when and to which group you posted the wrapper code? The
explorer wrapper code in ItemsCB does not look suitable for my needs (since
you can get events using WithEvents, things are quite different.. but I've
managed to get an Inspector up and running. I'm curious about the tag you
mention. Where would that be located? One way I can think of is keeping a
table of the various objects that the Inspector has seen, but I presume
there's a more convenient way.

Last but not least, I'm still looking for a way to unregister the event
listeners. Currently, I set up an Inspector in OnStartupComplete:

m_Inspectors = applicationObject.Inspectors;
m_Inspectors.NewInspector += new
Outlook.InspectorsEvents_NewInspectorEventHandler
(this.Inspectors_NewInspector);

where Inspectors_NewInspector registers an eventListener on ContactItem.Open
for the event Outlook.ItemEvents_10_OpenEventHandler. This works fine and is
executed each time I open a contact. However, when I close a contact, I have
to deregister the listener, but there's neither a ContactItem.close event,
nor will my Inspector register that the window has been closed (it also
applies to other items than contacts). Any ideas how to work around this?

Stephan
 
K

Ken Slovak - [MVP - Outlook]

I use code something like the following. This example only shows a MailItem
declared WithEvents, to handle other item types you'd add additional
declarations. In the code that adds the class to the wrapper collection you
would check for the type of the item in the new Inspector
(Inspector.CurrentItem) and add or not add the class based on that.

Any item type does have a Close event. Whether you can handle it in C# or
how I haven't the foggiest idea. The following code is a skeleton for an
Inspector class module, that gets put into a collection when NewInspector
fires and is removed from the collection when the Inspector is closed.
Handling the collection is similar to how it's done in the Explorer wrapper
code in ItemsCB.

VB 6 code:

Private WithEvents m_objInsp As Outlook.Inspector
Private WithEvents m_objMail As Outlook.MailItem

Private m_lngID As Long

Private Sub Class_Initialize()
On Error Resume Next

Set m_objInsp = Nothing
Set m_objMail = Nothing
End Sub

Private Sub Class_Terminate()
On Error Resume Next

Set m_objInsp = Nothing
Set m_objMail = Nothing
End Sub

Public Property Let Inspector(objInspector As Outlook.Inspector)
On Error Resume Next

Set m_objInsp = objInspector
Set m_objMail = objInspector.CurrentItem
End Property

Public Property Get Inspector() As Outlook.Inspector
On Error Resume Next

Set Inspector = m_objInsp
End Property

Public Property Let Key(lngID As Long)
On Error Resume Next

m_lngID = lngID
End Property

Public Property Get Key() As Long
On Error Resume Next

Key = m_lngID
End Property

'*********************************************************************
'Event Procedure: m_objInsp_Close()
'Destroy Inspector object in InspWrap
'*********************************************************************
Private Sub m_objInsp_Close()
On Error Resume Next

basOutlInsp.KillInsp m_lngID, Me
Set m_objInsp = Nothing

If Err <> 0 Then
AddInErr Err, "clsInspWrap", "m_objInsp_Close"
End If
End Sub

You can now add event handlers for any event available for a MailItem,
including the Close event.
 
S

Stephan Steiner

Ken
I use code something like the following. This example only shows a MailItem
declared WithEvents, to handle other item types you'd add additional
declarations. In the code that adds the class to the wrapper collection you
would check for the type of the item in the new Inspector
(Inspector.CurrentItem) and add or not add the class based on that.

I think I'm slowly getting there (your code is simple enough so that even a
VB n00b can understand it).

So basically, I catch the Inspectors.NewInspector even (triggered when I
open a contact, and other windows), then I get the new Inspector, get its
CurrentItem, and if it's a contact I add my button listener(s). So far so
good (that I have already working - and btw.. contactitem doesn't have a
close event.. but the inspector has). In order to only catch the button
click event from the appropriate contact, I vary the Button.Tag before
adding the eventlistener for Button.Click.

What I still don't get is how the tag is useful to ensure it only triggers
the event once. When the button is clicked, I get an event which I catch.
The event method has two parameters, the button and a boolean reference:

private void Add_Click(CommandBarButton btn, ref bool CancelDefault)
{
MessageBox.Show("Add Clicked");
}

Using btn, I can get the tag - but how do I associate it to the appropriate
ContactItem? If I had the contactItem, I could get its inspector, and from
that the button bars and eventually the button including the tag, compare
the two, and if they match I do whatever it is I have to do. Similarly, if I
had knew the inspector for the contactitem where the button was clicked, I'd
also get there.
But, I don't see a way to get from a triggered Add_Click to the
Inspector/ContactItem that the button belongs to. I'm sure there is, but I'm
just too thick to see it right now. How have you solved this problem in the
past?

Regards
Stephan
 
K

Ken Slovak - [MVP - Outlook]

Use the Object Browser if you want to see what properties, methods or events
are available for any object. In Outlook VBA's Object Browser there most
definitely is a Close event for a contact item, it's there in every Outlook
version I've ever worked with. Whether it's exposed in C# I can't say, but I
use it all the time in VBA/VB 6 code.

If you have multiple Inspectors open and each one has a button and a click
event handler for that button the handler will be called when the button is
clicked. If each button has the same Tag property then every button click
handler for that button will fire when any of the buttons is clicked. If the
Tag property is different for each one then only the expected click handler
will fire.

If you looked at the ItemsCB code for the Explorer wrapper you saw how each
new Explorer wrapper class is added to the Explorer wrapper collection. One
of the properties set in the class is a Key property. That is unique for any
Outlook session. Using the same modus for an Inspector wrapper you can use a
fixed string and append the Key property to that to generate a guaranteed
unique Tag property for each Inspector's button.

The reason for the wrappers is a user can have more than 1 Inspector open at
a time and the wrapper collection lets you handle events for the item in the
Inspector and any buttons in it separately and individually for each
Inspector and its CurrentItem.

Another thing you have to be aware of is that in certain versions and builds
of Outlook you may or may not get a NewInspector event when the Next or
Previous buttons are pressed in an open Inspector. A new item is opened in
the existing Inspector if NewInspector isn't fired so the Close event (and
Open) are valuable for seeing if CurrentItem has changed. If it has you need
to re-instantiate the item in the Inspector so the event handlers will fire
for that new item.

If you use the Inspector wrapper you don't have to bother with associating
the correct item to the correct Inspector and the correct button that was
clicked. It's all automatic and each wrapper class is completely independent
of any other instance of the class and all the events are automatically
correctly associated with the proper objects. Pretty cool.
 
S

Stephan Steiner

Hi
Use the Object Browser if you want to see what properties, methods or events
are available for any object. In Outlook VBA's Object Browser there most
definitely is a Close event for a contact item, it's there in every Outlook
version I've ever worked with. Whether it's exposed in C# I can't say, but I
use it all the time in VBA/VB 6 code.

It's not exposed. However, I've managed to catch the inspector closing event
so that's at le something. And thanks to your tag tricks, a button clicked
only triggers one event now :)

For the rest I think we have to step through some code (please bear with
me). I had like 10 looks through the example and here's what I get so far:
ExplWrap.cls is the wrapper class. It contains an Outlook.Explorer object
and registers for its events. Mod OutlExpl.bas is something - don't know
what VB coders call it - that has a Collection (I suppose that would be a
vector in an OO language), storing an Outlook.Explorer (I suppose that's the
main object of Outlook) with a unique key. The Outlook Explorer is removed
from the collection, when a close event is fired from the object and if the
last explorer object has been removed, UninitHandler is called to clean up
the remaining add-in resources. Connect.drs is the entry point for the
add-in (dsr,cls,bas.. my head is spinning ;), which launches the main
Class - OutAddin.cls, and that one adds the Outlook object to the collection
when it's started, when a "new explorer" event has been fired, or when an
"outlook startup" event has been fired.


Translated to my requirements that would be: Each time a new Inspector is
created, I create a new Inspector Wrapper and add it to a storage structure.
The inspector wrapper contains a copy of my ContactItem, and registers a
listener for its Inspector closing, and for my buttons. When the inspector
closes, the wrapper is removed from storage and all its events are
unregistered, then the inspector and buttons are removed.

Did I finally get that right?
Another thing you have to be aware of is that in certain versions and builds
of Outlook you may or may not get a NewInspector event when the Next or
Previous buttons are pressed in an open Inspector. A new item is opened in
the existing Inspector if NewInspector isn't fired so the Close event (and
Open) are valuable for seeing if CurrentItem has changed. If it has you need
to re-instantiate the item in the Inspector so the event handlers will fire
for that new item.

I already came across that testing your tag idea, but now I know what
exactly to look out for. Thanks

Regards
Stephan
 
K

Ken Slovak - [MVP - Outlook]

Yes, that sounds just about right.

A collection is a collection of objects that are the same type. It's an OO
concept, maybe foreign to C# but it is. Why you can't get or see the Close
event in C# is a mystery to me. Either it's a deficiency in C# or in how the
Outlook object model is handled by C# or .NET or in some step you are
missing. I'm not sure since I'd never use a .NET language to develop for
Outlook (and won't until Outlook 12 is released). I have to support multiple
versions of Outlook in my code and don't feel like doing all the insanity
I'd have to perform to make my code work in .NET for everything from Outlook
2000 to Outlook 2003.
 
S

Stephan Steiner

Ken
A collection is a collection of objects that are the same type. It's an OO
concept, maybe foreign to C# but it is. Why you can't get or see the Close
event in C# is a mystery to me. Either it's a deficiency in C# or in how the
Outlook object model is handled by C# or .NET or in some step you are

It was the last option. Turns out I used the wrong type of object. Now the
close event is exposed (though through ItemEvent_10_EventClose) but who
cares about the name.
missing. I'm not sure since I'd never use a .NET language to develop for
Outlook (and won't until Outlook 12 is released). I have to support
multiple

Why version 12? Is that where Microsoft is dropping support for old style VB
(aka VB <=6 and VBA)?

One last thing, regarding your last message:
Another thing you have to be aware of is that in certain versions and builds
of Outlook you may or may not get a NewInspector event when the Next or
Previous buttons are pressed in an open Inspector. A new item is opened in
the existing Inspector if NewInspector isn't fired so the Close event (and
Open) are valuable for seeing if CurrentItem has changed. If it has you need
to re-instantiate the item in the Inspector so the event handlers will fire
for that new item.

In your experience, are the open and close events firing consistently with
logic? What I'm experiencing right now seems rather random so I'm having a
hard time writing code that works in all cases. It works as long as I open a
contact, then close it again, or have multiple contacts open, but when
switching between contacts, not all the events I'm expecting are fired.

Regards
Stephan
 
K

Ken Slovak - [MVP - Outlook]

As far as Outlook 12 everyone expects that MS will continue its path towards
managed code. Obviously it's way too early to know what Office 12 will do or
if it will drop support for unmanaged code but at the moment there's no
story for VBA.NET for Outlook and support for multiple versions of Outlook
with .NET requires reflection of PIA's, framework incompatibilities,
requirements for downloading the framework for those that don't have it
installed yet (a majority) and slower speed than COM solutions. So for the
moment at least I choose to continue only COM development for Outlook.

A properly constructed Inspector wrapper should enable handling all the
things you've mentioned. The big problem is handling Next/Previous in
versions or builds that don't fire NewInspector. You get multiple
Inspector.Activate and Deactivate events in different orders depending on
whether Next or Previous was pressed and if the item had been opened in that
Inspector previously. Handling the Item.Open and Close events has always
worked for me in those cases.

Switching between open items should be a non-factor. Each wrapper class will
only respond to events for itself and for its CurrentItem. The Activate
event handles which Inspector has become the ActiveInspector, although it's
nearly impossible to debug because it activates and deactivates as you
switch to the debugger window and then back to Outlook.
 
S

Stephan Steiner

Hi
As far as Outlook 12 everyone expects that MS will continue its path towards
managed code. Obviously it's way too early to know what Office 12 will do or
if it will drop support for unmanaged code

I wished they would. .NET is really neat, it's interoperability that gives
most people headaches. I was working on a C# only project for a while and
that was truly sweet. But I'm getting off subject here..
A properly constructed Inspector wrapper should enable handling all the
things you've mentioned. The big problem is handling Next/Previous in
versions or builds that don't fire NewInspector. You get multiple
Inspector.Activate and Deactivate events in different orders depending on
whether Next or Previous was pressed and if the item had been opened in that
Inspector previously. Handling the Item.Open and Close events has always
worked for me in those cases.

Is there any consistent order in the way those are fired? I've done a lot of
testing, and basically as long as I have one contact open and do the
previous/next thing, and have a messagebox tell me about what is happening,
the events are fired in the following order:
Inspectors.NewInspector
ContactItem.Open
ContactItem.Close

That's already somewhat problematic because logically the close should
become before anything else. But, if I remove those messageboxes and write
debug code (using the Debug object), then not all those events are fired all
the time anymore, and that's where my problem began. Now suddenly, I once
again received two button click events, one for the old item and one for the
new (and it wasn't as if a click had been list.. clicking the new item
effecitvely triggered two clicks in the new Inspector). I catched that by
running a sanity check in the click handler (using the button tags.. having
them unique does come in very handy there), but it bothers me a bit that I
do get more events than I should, and sometimes less than I expect. Also,
when I press next and there's no next contact, the contact window closes,
and while I do get a contact.close, the inspector isn't closed. This only
happens when I close Outlook (upon which I get all the inspector.close
events that have not been registered before).

I've also addes some sanity checking in the ContactItem.Open code, checking
if my current ContactItem is the one that belongs to the Inspector wrapper,
and if not, updating it so I don't see many more options to deal with all
this stuff. I think I've got it the way I need it to work, but I'd like to
think it should be possible to have these events in a predictable manner all
the time.

Regards
Stephan
 
K

Ken Slovak - [MVP - Outlook]

Since I don't use C# and don't program addins using .NET I really don't know
if what you are seeing is due to interop problems or how you are handling
things or something else. I do know that in Outlook 2000 populating
NewInspector with CurrentItem happens at a different time than in Outlook
2002 or 2003 but I've never had the problems you are describing when doing
things in VB 6 code.

If MS just abandons unmanaged code in Office they might have a huge problem
in getting people to upgrade. There are a lot of mission critical apps out
there that are unmanaged code and most businesses don't want to rewrite all
their mission critical Office apps with all that testing, down time, man
hours and cost) just to upgrade an Office version or for the often dubious
benefits of managed code. Add to that the fact that many computers don't
have the framework loaded and aren't capable due to hardware of running the
latest and greatest and the costs of upgrading software and you'd have a
real no-win situation for sales of Office.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top