VSTO Outlook Toggle state - c#

I am working on a VSTO Outlook Addin for new mails. User can toggle image when he wants the mail saved.
Currently when the image is clicked i set a global bool to true.
private bool _state;
private void toggleBtn_Click(object sender, RibbonControlEventArgs e)
{
if (_state) {
_state = false;
} else {
_state = true;
}
}
Which works ok until the user opens another new mail window before sending the first one.
How can i store the state per new mail window only?
Thank so much.
Solution
There are two ways to solve this problem.
One : User Properties
Excellent blog on https://www.add-in-express.com/creating-addins-blog/2013/01/30/preserve-outlook-ribbon-controls-state/
Two: Use wrapper to store the state in classes
Read more on https://msdn.microsoft.com/en-us/library/office/ff973716(v=office.14).aspx

Not sure whether you can get the EntryId of the current mail being edited, if so, you can try to cache all the states into a dictionary, something like below
private Dictionary<string, bool> _states = new Dictionary<string, bool>();
private void toggleBtn_Click(object sender, RibbonControlEventArgs e)
{
MailItem ml;
// get current MailItem
// something like: MailItem ml = popupWindow.GetMail
// default false
if (!(_states.Keys.Contains(ml.EntryId))){
_states[ml.EntryId] = false;
}
// toggle the state
_states[ml.EntryId] = !_states[ml.EntryId];
}
however, you might want to handle the popup window closed event to remove the entry from the dictionary when user closes the popup (for example, email sent)

Solution
There are two ways to solve this problem.
One : User Properties Excellent blog on https://www.add-in-express.com/creating-addins-blog/2013/01/30/preserve-outlook-ribbon-controls-state/
Two: Use wrapper to store the state in classes Read more on https://msdn.microsoft.com/en-us/library/office/ff973716(v=office.14).aspx

Related

VSTO add-in to delete Outlook email after being read

I'm trying to write an Outlook Add-in that deletes an email after the email has been read completely.
The problem is that the read-flag turns true the second we click on it and it doesn't give us much time to read the email.
I tried to delete the email after closing it:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close += new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
}
}
void MailItem_Close(ref bool Cancel)
{
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Items inboxReadItems = inbox.Items.Restrict("[Unread]=false");
foreach (Outlook.MailItem mail in inboxReadItems)
{
mail.Delete();
}
}
This was the idea i came up with, sometimes it works but most of the times it ends with an error in the mail.Delete():
The Error: System.Runtime.InteropServices.COMException: 'The item’s properties and methods cannot be used inside this event procedure.'
First of all, your event handler may not be fired because the source object is declared in the methods and the scope of that object is limited by the method/function. After execution of the method/function ends, the garbage collector may swipe the heap (unpredictable) with your source object, so you may never get the event fired.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close += new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
}
}
Instead, you need to declare the sourceobject at the class level if you want to keep getting events fired, in your case maintain a list of objects (which is also inefficient when dealing with COM objects):
List<Outlook.MailItem> items = new List<Outlook.MailItem>();
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close += new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
list.Add(mail);
}
}
But in that case you may reach the limit of currently held and opened objects in Outlook with Exchange. So, the better solution is to handle the SelectionChange event of the Explorer class and deal with the currently selected items only. Every time the selection is changed, your source objects are renowned (old are released and new ones are subscribed).
But a better yet solution is to implement an item wrapper for Outlook items, so you could subscribe to the Close event or any other events in a convenient fashion. See Implement a wrapper for inspectors and track item-level events in each inspector for more information.
This is as inefficient as it gets - you set up event handlers on (potentially) thousands of emails in the Inbox folder. Do not do that.
Track the Explorer.SelectionChange event and set up event handlers only on those emails that are selected (and remove event handlers from the previous selections). If the item is unread, trap its read state change, and put its entry id in a list. When selection changes, delete the previously unread email(s) from that list.

Close MessageBox when incoming new message

i created simple application to send and receive message using C# and GsmComm Library. if there is a new incoming message my application will show messageBox that new message arrived. my problem is when i have many new message, messageBox will show so many messageBox notication. How can I just show the last single message box using code?
this mycode:
private void comm_MessageReceived(object sender, MessageReceivedEventArgs e)
{
var obj = e.IndicationObject;
if (obj is MemoryLocation)
{
var loc = (MemoryLocation)obj;
var msg = string.Format("New message received in storage \"{0}\", index {1}.",
loc.Storage, loc.Index);
MessageBox.Show(msg);
return;
}
}
i confuse to fix this, i tried to another way using form to show new incoming notif form1.showDialog(); but same problem first form show cannot be closed when new form opened. this my reference: https://stackoverflow.com/a/13445167/3319555
I really thanks if anyone can help me..thanks
If you're using your second solution of displaying a form with form.ShowDialog() you can store the forms in a list. Then, when a new form needs to be displayed, you can iterate through the list and close each open form with form.Close(). Assuming that your comm_MessageReceieved method is run on another thread, which I assume is driven via an IO completion port, then something like this perhaps?
List<MyForm> formList = new List<MyForm>();
readonly object formListLock = new object();
private void comm_MessageReceived(object sender, MessageReceivedEventArgs e)
{
/// you need to lock the List for thread safe access
lock (formListLock)
{
/// iterate over a copy of the list to avoid mutating the list under iteration
foreach (MyForm form in formList.ToList())
{
form.ThreadSafeClose();
}
}
string msg = "message";
using (MyForm form = new MyForm(msg))
{
lock (formListLock) { formList.Add(form); }
form.ShowDialog();
lock (formListLock) { formList.Remove(form); }
}
}
This was just off the top of my head but might be another possible direction you could take.
You will have to make a thread safe call to form.Close() so that it is run on the form's UI thread. Read about invoke here. There's a lot of information on SO about this topic. This could be as simple as adding something like the following method to your form class:
public void ThreadSafeClose()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(Close)); /// or BeginInvoke...
}
else
{
Close();
}
}
Read more about Lists here: https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx
Read more about the lock statement here: https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
Read more about thread synchronisation here: https://msdn.microsoft.com/en-us/library/ms173179.aspx
There are also numerous thread-safe collections that could possibly suit your needs, e.g. ConcurrentBag.

C# VSTO Outlook ItemSend Event execution order

I am using VSTO to create an event when an email is sent. The goal is change Attachments.
I already have other addins that run in the ItemSend event, but the problem is, I want my addin to run first. As I've read, there is no execution order for the Outlook addins sent event, but there must be some order even if only by name or guid...
I tried this solution (the problem is, if I have 2 mail windows open, the first window doesn´t run the event ... :( there is some overwrite event problem)
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.Inspectors.NewInspector += new InspectorsEvents_NewInspectorEventHandler(Custom_Inspector);
//This run in the end off all ItemSend Events.... :(
//this.Application.ItemSend += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(MyFunction2);
}
private void Custom_Inspector(Inspector Inspector)
{
if (Inspector != null && Inspector.CurrentItem != null && Inspector.CurrentItem is Outlook.MailItem)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem.EntryID == null)
{
((ItemEvents_10_Event)mailItem).Send += new ItemEvents_10_SendEventHandler(MyFunction);
}
}
}
void MyFunction(ref bool Cancel)
{
MailItem mailItemContext = ((Inspector)this.Application.ActiveWindow()).CurrentItem as MailItem;
if (mailItemContext != null)
{
//my custom code here
}
}
this.Application.Inspectors.NewInspector += new InspectorsEvents_NewInspectorEventHandler(Custom_Inspector);
To get the NewInspector event of the Inspectors class fired you need to keep the source object alive, i.e. prevent it from swiping by the garbage collector. So, I'd recommend declaring the an instance of the Inspectors class at the global scope - at the class level.
The Outlook object model doesn't provide anything for changing the order of events. From my experience add-ins are loaded based on the ProgID value (sorted in the alphabetical order) and events are fired in the reverse order, i.e. a LIFO queue.
Eugene 100000 thanks! in reality Outlook Order Plugin Events by alphabetical reverse.
But by the way, how set NewInspector in top class? i need to define inside class ThisAddIn a prop call:
public partial class ThisAddIn
{
public Microsoft.Office.Interop.Outlook.Inspectors _inspector;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
_inspector = this.Application.Inspectors;
_inspector.NewInspector += new InspectorsEvents_NewInspectorEventHandler(Custom_Inspector);
}
}

save and load Listbox Items locally and pass them to other pages

I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..

How to capture a Outlook Appointment delete event on custom folder?

I am developing an Outlook AddIn. One part of it that I organize appointments in a specific folder. I want to capture if an element gets deleted (in this case moving out of "my" folder counts as deleted).
I found the article https://stackoverflow.com/questions/10579240/how-to-capture-a-c-sharp-outlook-addin-appointment-delete-event and his/her solution helped a lot, but I have a huge problem: the event only fires in that "session", where my folder was created, not when I get the folder object from outlook.
My code looks like this:
private Outlook.MAPIFolder _CalendarMAPIFolder = null;
private Outlook.MAPIFolderEvents_12_Event _CalendarFolder = null;
private Outlook.Items _CalendarItems = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Outlook.MAPIFolder calendarFolder =
this.Application.GetNamespace("mapi").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
// get my-Folder (if not found, create it)
try
{
_CalendarMAPIFolder = calendarFolder.Folders["my-Folder"];
}
catch
{
_CalendarMAPIFolder = calendarFolder.Folders.Add("my-Folder");
}
_CalendarItems = _CalendarMAPIFolder.Items;
_CalendarFolder = _CalendarMAPIFolder as Outlook.MAPIFolderEvents_12_Event;
if (_CalendarFolder == null)
{
MessageBox.Show("can not cast MAPIFolder to Folder");
}
_CalendarFolder.BeforeItemMove += new Outlook.MAPIFolderEvents_12_BeforeItemMoveEventHandler(Folder_BeforeItemMove);
Debug.Print("events registered");
}
public void Folder_BeforeItemMove(
Object Item,
Outlook.MAPIFolder MoveTo,
ref bool Cancel)
{
Outlook.AppointmentItem aitem = Item as Outlook.AppointmentItem;
string s = "";
if (aitem != null) s = aitem.Subject;
//Cancel = false;
MessageBox.Show("Test! " + s);
}
Does anyone have a solution?
Thank you ;)
Edit: I still have no clue :(((((((
By definition, BeforeItemMove only fires when the user (Outlook client) initiates an Item be moved. It will not fire for synchronization events (i.e. Exchange sync).
If you are connecting Outlook to an Exchange Server, you should look at EWS (Exchange Web Services) if you wish to be notified of folder change events outside the client application (i.e. session). EWS offers push, pull, or streaming notification options. You would attach a notification to "Item deletion" operation.
It was all Microsofts fault! It was a Bug, I just needed to Update Outlook!

Categories

Resources