How to add toolbar to MailEditor for outlook?

  • Thread starter ryotyankou via OfficeKB.com
  • Start date
R

ryotyankou via OfficeKB.com

When we compose a new message, or "reply", "reply all", "forward" a message,
the message is in edit mode, i want to add a toolbar in this mode, and put
two buttons on the toolbar(just like the encrypt and signarture buttons),
then DispEventAdvise on the buttons. How could i do this? In OnConnection
method, i think i couldn't get the inspector to work with. May i add toolbar
and advise on button in NewInspector event? If i advise in NewInspector event,
when should i Unadvise on buttons?? Any tips or ideas? TIA.
 
R

ryotyankou via OfficeKB.com

I found my solution can work, but i don't know if it is safe or not. My
solution is as follow:
1.) In OnConnection method, DispEventAdvise on NewInspector event.
2.) In NewInspector advise function, Create the two buttons and
DispEventAdvise the two buttons and InspectorClose event.
3.) In InspectorClose event, UnAdvise InspectorClose event itself and two
buttons event.
4.) In OnDisconnection method, UnAdvise NewInspector event.
So everytime the user open mail or create mail, those codes should be
executed, i think this will cause low efficiency. Is there a common idea for
my purpose?
By the way, How could i judge if current inspector is in edit mode(new mail,
relpy, forward etc.) but read-only mode(user open an existing mail). I found
the "Sent" property of MailItem may do this work, if "Sent == false", it must
be in edit mode, otherwise in read-only mode, am i right?Thanks in advance.
 
K

Ken Slovak - [MVP - Outlook]

That's pretty much the routine you use, although you may want to store your
pointers in some sort of list or whatever to keep them alive and to be able
to handle multiple open Inspectors at a time.

To do that you need to provide a unique Tag value for any buttons you create
so Click() events don't fire in every open Inspector.

In general, you may also find that the new Inspector and its CurrentItem
object aren't fully populated (weak object reference) during NewInspector().
For example, even though it's always WordMail in Outlook 2007 you get
Inspector.IsWordMail() == false during that event. The only CurrentItem
properties you should work with then are .Class and .MessageClass.

I usually wait until the first Inspector.Activate() event fires on an
Inspector before I try to create UI or to access most of the Inspector and
CurrentItem properties.

A new item has a null EntryID value. Any item that pre-exists or has been
saved has an EntryID value.
 
R

ryotyankou via OfficeKB.com

Thank you for your reply, Ken.
Do you mean it is better to Create UI and DispEventAdvise in Activate event
than NewInspector event?
I think entryid is not very good to judge if it is in edit mode, consider
there is a draft mail, user already save it, it have an entry id, when user
open it again in draft box, it is still in edit mode. So i think get "Sent"
property should be better, draft is not sent yet, so it is in edit mode. My
purpose is simple:
If (1): New, reply, relpy all, forward == create buttons
(2): Open exist mail == no create buttons
Just like outlook's encrypt and signature button.
By the way, how do i change button style? click it, down, click again, up.
Like encrypt and signature button too.
That's pretty much the routine you use, although you may want to store your
pointers in some sort of list or whatever to keep them alive and to be able
to handle multiple open Inspectors at a time.

To do that you need to provide a unique Tag value for any buttons you create
so Click() events don't fire in every open Inspector.

In general, you may also find that the new Inspector and its CurrentItem
object aren't fully populated (weak object reference) during NewInspector().
For example, even though it's always WordMail in Outlook 2007 you get
Inspector.IsWordMail() == false during that event. The only CurrentItem
properties you should work with then are .Class and .MessageClass.

I usually wait until the first Inspector.Activate() event fires on an
Inspector before I try to create UI or to access most of the Inspector and
CurrentItem properties.

A new item has a null EntryID value. Any item that pre-exists or has been
saved has an EntryID value.
I found my solution can work, but i don't know if it is safe or not. My
solution is as follow:
[quoted text clipped - 16 lines]
be in edit mode, otherwise in read-only mode, am i right?Thanks in
advance.
 
R

ryotyankou via OfficeKB.com

Hi, Ken, i have a problem here.
About the multiple Inspector, How could i Unadvise inspector event and two
button click event correctly? Say there are two Inspector, therefore there
are 6 pointers were saved. But i don't know which Inspector the user close
first, that is i don't know which inspector pointer and buttons' pointer
should be passed to DispEventUnadvise. My application flow now is:
1.) In OnConnection Advise NewInspector event.
2.) In NewInspector if item is not mailitem return failed directly.
A.) if(it is not wordeditor) CreateUI
B.) if(item is wordeditor && item.wordeditor.activewindow.envolopevisible
is ture) CreateUI
C.) After CreateUI successfully, Advise InspectorClose and two button
click event and Save
pointers.
3.) In InspectorClose event////////////////////////<------------ Here i don't
know how to Unadvise correct
inspector. Could you help me please?
4.) In OnDisconnection UnAdvise NewInspector event.
 
R

ryotyankou via OfficeKB.com

I searched through the internet via google, found two code snippets, but
one's in VB another's in C#. I almost know nothing on both language.
c#: http://www.outlookcode.com/codedetail.aspx?id=797
I don't understand what does "XEventHandler"do in the code.
VB: http://outlookcode.com/codedetail.aspx?id=1344
and this, while KillInsp, What's happen on variable "_intID"? How could i do
this in C++?
In a word, my purpose is only one: When user close Inspector, i want to do
DispEventUnadvise correctly, so i must know correct inspector pointer for the
closing Inspector window.
Thanks for your help!!!
 
K

Ken Slovak - [MVP - Outlook]

Yes, it's better to create UI in Activate(), as I said.

I can't help you with any specific C++ code, I don't do C++. There are
plenty of examples, some from me, in VB6 or VB.NET or C#. You can search at
www.outlookcode.com and see if any C++ samples have been posted.

If EntryID is null then the item is new. That's all it tells you. Sent is
not a good way to tell if the item is in edit mode, you can edit a received
message or one you already sent, so it doesn't tell you that. It will tell
you if you are working with a draft and that's about it for reliable
information.

You can also check the ConversationIndex properties on an item. A new item,
not saved already, has a null ConversationIndex value. A reply or forward
will have a value for that property, each item in a conversation will
increment the length of that property by 1 date/time struct length.

Checking Subject for any prefixes such as RE: or FWD: can also be done.

A toggle button is set up as a normal CommandBarButton, you set the State
property to determine if the button appears to be pushed in. State ==
MsoButtonState.msoButtonDown looks pushed in, State ==
MsoButtonState.msoButtonUp looks not pushed in.
 
K

Ken Slovak - [MVP - Outlook]

If you handle the Inspector.Close() event you know exactly which Inspector
is being closed.

The general algorithm for an Inspector (or Explorer) wrapper is to keep an
int variable that is incremented for each new Inspector or Explorer that is
opened. That is the Key value for that object. When the item is opened and
you determine you want to handle it using wrappers you create a new instance
of a wrapper class. That is added to the list/collection/whatever with the
Key value as the key property of the list entry for that wrapper class.

Then you instantiate a class that handles the Inspector, its events and
buttons and so on. In that class is the code for Inspector.Close(),
Activate() and whatever other events you want, plus events for the item in
the Inspector. It also holds a reference to the Inspector and item and the
Key property. The UI code is called and the Key value is used to add to a
string or GUID to create a unique Tag property for each button.

The button handler code is also in that wrapper class, it gets called for
that Inspector instance only because of the unique Tag.

When Inspector.Close() fires you handle that in the wrapper class and in
your dispose code you remove the wrapper class from the list.
 
R

ryotyankou via OfficeKB.com

Ken said:
If you handle the Inspector.Close() event you know exactly which Inspector
is being closed.

I don't quite understand this, the Close event have no parameter, how could i
get the right Inspector pointer??
for instance:
typedef IDispEventSimpleImpl<1, CMyAddin, &__uuidof(Outlook::InspectorEvents)
InspCloseEvent;
void __stdcall CMyAddin::OnNewInspector(IDispatch * pInspector)
{
m_Inspector = pInspector;//m_Inspector is a Outlook::_InspectorPtr.
InspCloseEvent::DispEventAdvise(m_Inspector);
//other codes.
}

and close event is:
void __stdcall CMyAddin::OnCloseInspector()
{
InspCloseEvent::DispEventUnadvise(???);//What should i pass in here??
}
Every time i open a new inspector, m_Inspector is changed, when i close one
of opened inspector, how could i know which InspectorPtr should be use???Of
course, i should save m_Inspector in a list.
Thanks!
 
R

ryotyankou via OfficeKB.com

Ken said:
Yes, it's better to create UI in Activate(), as I said.
Every time the inspector is focused, this function is called, it may create
multiple toolbar. Do you mean use FindControl to find the toolbar created, if
exist then do not create again?
I can't help you with any specific C++ code, I don't do C++. There are
plenty of examples, some from me, in VB6 or VB.NET or C#. You can search at
www.outlookcode.com and see if any C++ samples have been posted.
OK, i will check it, 3Q.
If EntryID is null then the item is new. That's all it tells you. Sent is
not a good way to tell if the item is in edit mode, you can edit a received
message or one you already sent, so it doesn't tell you that. It will tell
you if you are working with a draft and that's about it for reliable
information.
I think Sent property can work for me, although a received message or sent
message can be edit, i needn't show my button for these case, my button is
just for setting our program before sent message.(just like encrypt and
signature button)
A toggle button is set up as a normal CommandBarButton, you set the State
property to determine if the button appears to be pushed in. State ==
MsoButtonState.msoButtonDown looks pushed in, State ==
MsoButtonState.msoButtonUp looks not pushed in.
OK, got it, thanks again.
 
K

Ken Slovak - [MVP - Outlook]

If you have a wrapper class that holds the Inspector, current item, Key,
etc., it also handles the Inspector and item events. I mentioned that. So
when Inspector.Close() fires it fires only in that one wrapper class
instance. The class instance knows who it is from the stored Key value. It
can access it's own UI that way, and access it's place in the list. So it
can also remove itself from the list.

As far as not creating multiple copies of the UI, that's what flags are for.
You set one flag in the wrapper class when the class is created, call it
_startup. You can also create another flag _uiCreated. You check the flag
logic to see if startup initializations and UI creation need to be done. You
can also combine both flags into one if you only need one flag.
 
R

ryotyankou via OfficeKB.com

Hi, ken, thank you for your help. I write a wrapper class, and flow is change
to:
1.) In OnConnection, NewInspector::DispEventAdvise.
2.) In NewInspector event: CInspWrapper * pInsp = new CInspWrapper(Outlook::
Inspector);
then add pInsp to a list, say "InspList".
3.) In CInspWrapper, CloseInspector::DispEventAdvise, and create UI. when
user click close,
UnAdvise current inspector and remove UI .(here i don't know how to
remove the killed
CInspWrapper in "InspList",
so i decide to free all memory in OnDisconnection method).
4.) User close outlook, then in OnDisconnection, UnAdvise NewInspector event,
and free all pointer
(hold memory) from new CInspWrapper.
If wordedit is not use, this work fine, else it is sees quite unexcept. From
your previous post, i got an idea, that's is wordedit is in use, i should
find the commandbar, if it is exist, just put it visiable, else create it, am
i right?
Here i have i little trouble, how to find exist CommandBar in CommandBars??
it seems that CommandBars only have FindControl method, Should i use
CommandBars::GetItem(index)??
Sorry for so much questions, and thanks very much for your help.
If you have a wrapper class that holds the Inspector, current item, Key,
etc., it also handles the Inspector and item events. I mentioned that. So
when Inspector.Close() fires it fires only in that one wrapper class
instance. The class instance knows who it is from the stored Key value. It
can access it's own UI that way, and access it's place in the list. So it
can also remove itself from the list.

As far as not creating multiple copies of the UI, that's what flags are for.
You set one flag in the wrapper class when the class is created, call it
_startup. You can also create another flag _uiCreated. You check the flag
logic to see if startup initializations and UI creation need to be done. You
can also combine both flags into one if you only need one flag.
Every time the inspector is focused, this function is called, it may
[quoted text clipped - 25 lines]
OK, got it, thanks again.
 
K

Ken Slovak - [MVP - Outlook]

If the list of wrapper classes is available globally to you in the code you
can access it from the Inspector wrapper class and find the related wrapper
entry using the Key value as the key for that list entry. That allows you to
remove the wrapper from the list from within the wrapper class.

The CommandBars.FindControl() method will find a top level control in that
CommandBars collection. If you are creating your own toolbar for your UI,
something that's necessary for WordMail, then you already know the name of
your toolbar and you can get it using CommandBars.FindControl().
 
R

ryotyankou via OfficeKB.com

Thanks very much, ken, Wished you had a good labor day. I make my solution
work now. In a word, outlook editor have multiple instance for the toolbar,
but the wordeditor have only one, right? Thanks again in advance.
If the list of wrapper classes is available globally to you in the code you
can access it from the Inspector wrapper class and find the related wrapper
entry using the Key value as the key for that list entry. That allows you to
remove the wrapper from the list from within the wrapper class.

The CommandBars.FindControl() method will find a top level control in that
CommandBars collection. If you are creating your own toolbar for your UI,
something that's necessary for WordMail, then you already know the name of
your toolbar and you can get it using CommandBars.FindControl().
Hi, ken, thank you for your help. I write a wrapper class, and flow is
change
[quoted text clipped - 25 lines]
CommandBars::GetItem(index)??
Sorry for so much questions, and thanks very much for your help.
 
K

Ken Slovak - [MVP - Outlook]

When using WordMail 2003 or earlier all toolbars you create are stored using
a CustomizationContext. If the CustomizationContext is the same, which is
usually is, they all will be there and have to be manipulated as to
visibility based on Envelope.Visible (email and not document), and on which
Inspector is currently the active one.
 

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