Meeting Notice Being Sent Even Though I Have Set Cancel in Item_Write - outlook

I'm setting the Cancel check in the Item_Write event handler for a meeting request because if I set the Cancel to true in Item_Send, the meeting window closes. What I'm trying to do is prompt the user that they have recipients that would normally get processed using a custom button in the ribbon (button details not important for my question). Anyway, when I try to Cancel the Item_Write event a meeting notice is sent regardless. No matter what I try, I cannot stop the meeting notice from going out even though I'm setting the ref Cancel to true. If I cancel on Item_Send then the window closes, which is not what the users want. The only thing I can think of at this point is perhaps the requestDetailsRegion.SaveRequestDetailsToLocalOutlookItemProperties(), which saves custom form region field properties to defined custom properties on the AppointmentItem, is triggering the Application_Item_Load event and this somehow is prompting Outlook to send the meeting invite notice, as if it's pending. I'm running this in Outlook 2010. Thank you in advance.
Here's some example code:
void Item_Write(ref bool Cancel)
{
Cancel = CancelInvite(Cancel);
}
private bool CancelInvite(bool Cancel)
{
Inspector activeInspector = Globals.ThisAddIn.Application.ActiveInspector();
WindowFormRegionCollection formRegions = Globals.FormRegions[activeInspector];
RequestDetailsFormRegion requestDetailsRegion = formRegions.RequestDetailsForm;
// Add request form details to meeting item.
requestDetailsRegion.SaveRequestDetailsToLocalOutlookItemProperties();
// Checking if there's an incomplete form request pending submission
if (requestDetailsRegion.txtFileName_Hidden.TextLength == 0 &&
appointmentItem != null && appointmentItem.MeetingStatus != OlMeetingStatus.olMeetingCanceled)
{
Persons meetingRecipients = new Persons();
foreach (Outlook.Recipient recipient in appointmentItem.Recipients)
if (recipient.Address != activeInspector.Session.CurrentUser.Address)
meetingRecipients.Add(new MeetingRecipient(recipient));
if (meetingRecipients.ContainsSpecialRecipients)
{
CustomDialog customDialog = new CustomDialog();
customDialog.OkButtonText = messageBoxTextStatusOK;
customDialog.CancelButtonText = messageBoxTextStatusCancel;
DialogResult dialogResult = customDialog.ShowDialog();
if (dialogResult == DialogResult.Cancel)
{
Cancel = true;
}
else
{
Cancel = false;
}
customDialog.Close();
}
}
return Cancel;
}
public void SaveRequestDetailsToLocalOutlookItemProperties()
{
if (this.OutlookItem is Outlook.AppointmentItem)
{
Outlook.AppointmentItem appointmentItem = (Outlook.AppointmentItem)this.OutlookItem;
// Checking if organizer already cancelled meeting. If so, then no need to update apppointment item properties.
if (appointmentItem.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled)
{
appointmentItem.ItemProperties[RequestSubmitted].Value = chkSubmitted.Checked; // Hidden
appointmentItem.ItemProperties[Filename].Value = txtFileName_Hidden.Text; // Hidden
appointmentItem.ItemProperties[ReasonForVisit].Value = txtReason.Text;
// SPARING SIMILAR LINES OF CODE
...
chkSaved.Checked = true;
}
}

You never set the (ref) Cancel parameter to true
void Item_Write(ref bool Cancel)
{
this.cancelInviteResult = CancelInvite(Cancel);
Cancel = this.cancelInviteResult;
}

Related

Is there any way to call send method on email opened in inline reply based on custom send button click

I have found a similar post by another user.
https://stackoverflow.com/questions/61183940/is-there-any-way-to-call-send-method-on-composed-mail-opened-in-inline-reply-bas
which is answered, But the page which has answer is no longer active
https://stackoverflow.com/questions/61142613/using-activeinlineresponse-in-vsto-add-in-send-mailitem-after-modifying-header/61144021#comment108226887_61144021
Tried noting as such
Edit : I did call the Item.Send in the Inspector Activate event.. and new inspector event where i call the activate method.
But the window does not disappear unless I click on the window icon in the task bar [to bring it to focus.].
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Base objBase = new Base();
dynamic mailItem = objBase.GetItemObject(Inspector.CurrentItem);
var inspector = Inspector as Outlook.InspectorEvents_10_Event;
if (inspector != null)
{
inspector.Close += Inspectors_CloseInspector;
}
if (mailItem != null)
{
//if (mailItem.EntryID == null)
//{
if (mailItem is Outlook.MailItem)
{
if (IsInlineItem)
{
_MailItem = mailItem;
((Outlook.InspectorEvents_Event)Inspector).Activate += new Outlook.InspectorEvents_ActivateEventHandler(InspectorActivate);
((Outlook.InspectorEvents_Event)Inspector).Deactivate += new Outlook.InspectorEvents_DeactivateEventHandler(InspectorDeactivate);
((Outlook.InspectorEvents_Event)Inspector).Close += new Outlook.InspectorEvents_CloseEventHandler(Inspectors_CloseInspector);
Inspector.Activate();
}
else if (mailItem.Sent == false)
{ _MailItem = mailItem; }
else
{
Marshal.ReleaseComObject(mailItem);
}
}
else
{
_MailItem = mailItem;
Marshal.ReleaseComObject(mailItem);
}
//}
}
objBase.WriteError("new Inspector");
}
private void InspectorActivate()
{
Base objBase = new Base();
Outlook.Inspector ActiveInspector = Application.ActiveInspector();
if (ActiveInspector is null)
{
}
else
{
//Base objBase = new Base();
dynamic selectedItem = objBase.GetItemObject(ActiveInspector.CurrentItem);
if (selectedItem != null && _MailItem.Subject == selectedItem.Subject && selectedItem.Sent == false)
{
try
{
selectedItem.Send();
}
catch (Exception e)
{
}
finally
{
Marshal.ReleaseComObject(selectedItem);
Marshal.ReleaseComObject(ActiveInspector);
}
}
}
That question got deleted. I will repost the answer:
Yes, OOM blocks some methods for the inline response. The best you can do is simulate a click on the Send button using the accessibility API.
If using Redemption is an option (I am its author), it exposes SafeExplorer.ActiveInlineResponseSend method:
SafeExplorer sExplorer = new Redemption.SafeExplorer();
sExplorer.Item = Application.ActiveExplorer;
sExplorer.ActiveInlineResponseSend();
The Explorer.ActiveInlineResponse property returns an item object representing the active inline response item in the explorer reading pane. You ca use the same properties and methods of the MailItem object on this item, except for the following:
MailItem.Actions property
MailItem.Close method
MailItem.Copy method
MailItem.Delete method
MailItem.Forward method
MailItem.Move method
MailItem.Reply method
MailItem.ReplyAll method
MailItem.Send method
As you can see the Send method is not supported on the inline response items in Outlook.
As a possible workaround you may call the Display method which can open the item in a new inspector window where you could get the instance and call the Send method. The NewInspector event is fired whenever a new inspector window is opened, either as a result of user action or through program code. But I'd suggest getting the Inspector.Activate event fired to get the MailItem instance and call the Send method.

Xamarin.IOS UILocalNotification.UserInfo is null after closing application

Local notification works as it should when application is opened. But there are cases when local notification remains in notification center after closing application. And then clicking on notification starts application and notification data should be passed in options in AppDelegate.FinishedLaunching method. Options contains key UIApplicationLaunchOptionsLocalNotificationKey which has value of type UIKit.UILocalNotification. This value contains UserInfo property which should be filled with notification data. But this UserInfo is null.
Another problem is when local notification remains in notification center and application is restarted. Clicking on notification causes application to start again and it stops immediately.
Have you had such problem? Is it problem with Xamarin? How to handle such scenarios?
Creating notification:
public void DisplayNotification(MessageInfo info)
{
var notificationCenter = UNUserNotificationCenter.Current;
var content = new UNMutableNotificationContent();
content.Title = info.Title;
content.Body = info.Body;
content.UserInfo = IosStaticMethods.CreateNsDictFromMessageInfo(info);
UNNotificationTrigger trigger;
trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.1, false);
var id = (++_LastNotificationId).ToString();
var request = UNNotificationRequest.FromIdentifier(id, content, trigger);
notificationCenter.Delegate = new UserNotificationCenterDelegate();
notificationCenter.AddNotificationRequest(request, (error) =>
{
//handle error
});
}
internal class UserNotificationCenterDelegate : UNUserNotificationCenterDelegate
{
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
completionHandler(UNNotificationPresentationOptions.Alert);
}
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
//do something
}
}
Handling notification in AppDelegate.FinishedLaunching
if (options != null)
{
var notification = options["UIApplicationLaunchOptionsLocalNotificationKey"] as UIKit.UILocalNotification;
var userInfo = notification.UserInfo;//the userInfo is null
}
It seems NSDictionary should not contain NSNull values. After removing NSNull values everything is OK even if NSDictionary documentation https://developer.apple.com/documentation/foundation/nsdictionary says NSNull is allowed.

VSTO How to hide FormRegion in Reply email [in InlineResponse also]?

So, as the subject says...what is the easiest way to hide FormRegion if email is in Reply mode whether its in a new window or with InlineResponse?
Just set the FormRegion.Visible property which returns a Boolean value that indicates whether the form region is visible or hidden.
Went back to test the approach in my answer after the question from #EugeneAstafiev and -- of course -- this was more complicated than I first thought... but I did get it working with some additional code.
The issue is that when the user clicks "Reply", it opens a new inspector window with a new instance of the FormRegion. So setting the Visible property to false in the event handler sets it only on the current "Read" mode inspector window -- rather than the new "Compose" mode inspector window that gets opened. So, instead the code samples below set up a bool flag property in ThisAddIn called LoadFormRegion that can be toggled to "false" when the Reply or ReplyAll event is fired.
Also, I noticed that setting Visible to false on the FormRegion still draws the area of the FormRegion on the inspector window, just with nothing in it. To completely prevent the FormRegion from loading, you can test for "Compose" mode and then the ThisAddin.LoadFormRegion flag in the FormRegionInitializing event handler located inside of the "Form Region Factory" at the top of the code page -- which is usually folded away from view. In that code block, setting "e.Cancel = true" will prevent the FormRegion from loading at all.
Here is the revised code for the FormRegion, which now subscribes to all three email button click events (Reply, ReplyAll, Forward), and sets the ThisAddIn.LoadFormRegion flag accordingly:
namespace TESTINGOutlookAddInVSTO
{
partial class FormRegion1
{
#region Form Region Factory
[Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Note)]
[Microsoft.Office.Tools.Outlook.FormRegionName("TESTINGOutlookAddInVSTO.FormRegion1")]
public partial class FormRegion1Factory
{
// Occurs before the form region is initialized.
// To prevent the form region from appearing, set e.Cancel to true.
// Use e.OutlookItem to get a reference to the current Outlook item.
private void FormRegion1Factory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
{
if (e.FormRegionMode == Outlook.OlFormRegionMode.olFormRegionCompose)
{
var myAddIn = Globals.ThisAddIn;
if (myAddIn.LoadFormRegion == false)
{
e.Cancel = true;
}
}
}
}
#endregion
// Occurs before the form region is displayed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
// Reset ThisAddIn.LoadFormRegion flag to true (in case user starts
// composing email from scratch without clicking Reply, ReplyAll or Forward)
var myAddin = Globals.ThisAddIn;
myAddin.LoadFormRegion = true;
var myMailItem = this.OutlookItem as Outlook.MailItem;
// Track these events to set the ThisAddIn.FormRegionShowing flag
((Outlook.ItemEvents_10_Event)myMailItem).Reply += myMailItem_Reply;
((Outlook.ItemEvents_10_Event)myMailItem).ReplyAll += myMailItem_ReplyAll;
((Outlook.ItemEvents_10_Event)myMailItem).Forward += myMailItem_Forward;
}
// Sets FormRegionShowing flag on ThisAddin
private void SetFormRegionShowing(bool show)
{
var myAddIn = Globals.ThisAddIn;
myAddIn.LoadFormRegion = show;
}
private void myMailItem_Forward(object Forward, ref bool Cancel)
{
SetFormRegionShowing(true);
}
private void myMailItem_ReplyAll(object Response, ref bool Cancel)
{
SetFormRegionShowing(false);
}
private void myMailItem_Reply(object Response, ref bool Cancel)
{
SetFormRegionShowing(false);
}
// Occurs when the form region is closed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegion1_FormRegionClosed(object sender, System.EventArgs e)
{
}
}
}
And here's the new code for ThisAddIn to setup the LoadFormRegion property flag:
namespace TESTINGOutlookAddInVSTO
{
public partial class ThisAddIn
{
private bool loadFormRegion;
public bool LoadFormRegion { get => loadFormRegion; set => loadFormRegion = value; }
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
LoadFormRegion = true;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
// Note: Outlook no longer raises this event. If you have code that
// must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
Tested the above and works nearly perfectly... I noticed that if there is a "Draft" reply in the user's Inbox created before the add-in was loaded, clicking on that email to continue editing in the Preview window or a new inspector window will cause some wonky display issues with the FormRegion. However, once I closed out that draft, then everything seemed to return to normal.

Should I be thinking differently about cancelling the back button in Xamarin Forms

I have a Prism based Xamarin Forms app that contains an edit page that is wrapped in a Navigation page so there is a back button at top left on both Android and iOS. To avoid the user accidentally losing an edit in progress by accidentally clicking the back button (in particular on Android) we want to prompt them to confirm that they definitely want to cancel.
Thing is, this seems like something that is not baked in to Xamarin forms. You can override OnBackButtonPressed in a navigation page, but that only gets called for the hardware/software back button on Android. There are articles detailing techniques to intercept the actual arrow button at the top left on Android (involving overriding OnOptionsItemSelected in the Android MainActivity), but on iOS I'm not sure it is even possible.
So I can't help but wonder if I am going about this the wrong way? Should I not be intercepting the top left / hardware / software back button in this way? Seems like a pretty common thing to do (e.g. press back when editing a new contact in the android built in Contacts app and you get a prompt) but it really feels like I am fighting the system here somehow.
There are previous questions around this, most relevant appears to be How to intercept Navigation Bar Back Button Clicked in Xamarin Forms? - but I am looking for some broad brush suggestions for an approach here. My objective is to show the user a page with the <- arrow at top left for Android, "Cancel" for iOS but I would like to get some views about the best way to go about it that does not involve me fighting against prism / navigation pages / xamarin forms and (where possible) not breaking the various "best practices" on Android and iOS.
After going down the same path as you and being told not to prevent users from going back, I decided on showing an alert after they tap the back button (within ContentPage.OnDisappearing()) that says something like Would you like to save your work?.
If you go with this approach, be sure to use Application.MainPage.DisplayAlert() instead of just this.DisplayAlert() since your ContentPage might not be visible at that point.
Here is how I currently handle saving work when they click the back button (I consolidated a good bit of code and changed some things):
protected override async void OnDisappearing() {
base.OnDisappearing();
// At this point the page is gone or is disappearing, but all properties are still available
#region Auto-save Check and Execution
/*
* Checks to see if any edits have been made and if a save is not in progress, if both are true, it asks if they want to save, if yes, it checks for validation errors.
* If it finds them, it marks it as such in the model before saving the model to the DB and showing an alert stating what was done
*/
if(!_viewModel.WorkIsEdited || _viewModel.SaveInProgress) { //WorkIsEdited changes if they enter/change data or focus on certain elements such as a Picker
return;
}
if(!await Application.Current.MainPage.DisplayAlert("ALERT", "You have unsaved work! Would you like to save now?", "Yes", "No")) {
return;
}
if(await _viewModel.SaveClaimErrorsOrNotAsync()) { //The return value is whether validation succeeds or not, but it gets saved either way
App.SuccessToastConfig.Message = "Work saved successfully. Try saving it yourself next time!";
UserDialogs.Instance.Toast(App.SuccessToastConfig);
} else if(await Application.Current.MainPage.DisplayAlert("ERROR", "Work saved successfully but errors were detected. Tap the button to go back to your work.", "To Work Entry", "OK")) {
await Task.Delay(200); //BUG: On Android, the alert above could still be displayed when the page below is pushed, which prevents the page from displaying //BUG: On iOS 10+ currently the alerts are not fully removed from the view hierarchy when execution returns (a fix is in the works)
await Application.Current.MainPage.Navigation.PushAsync(new WorkPage(_viewModel.SavedWork));
}
#endregion
}
What you ask for is not possible. The back button tap cannot be canceled on iOS even in native apps. You can do some other tricks like having a custom 'back' button, but in general you shouldn't do that - you should instead have a modal dialog with the Done and Cancel buttons (or something similar).
If you use xamarin forms that code it is work.
CrossPlatform source
public class CoolContentPage : ContentPage
{
public Action CustomBackButtonAction { get; set; }
public static readonly BindableProperty EnableBackButtonOverrideProperty =
BindableProperty.Create(nameof(EnableBackButtonOverride), typeof(bool), typeof(CoolContentPage), false);
public bool EnableBackButtonOverride{
get { return (bool)GetValue(EnableBackButtonOverrideProperty); }
set { SetValue(EnableBackButtonOverrideProperty, value); }
}
}
}
Android source
public override bool OnOptionsItemSelected(IMenuItem item)
{
if (item.ItemId == 16908332)
{
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
return false;
}
return base.OnOptionsItemSelected(item);
}
else
{
return base.OnOptionsItemSelected(item);
}
}
public override void OnBackPressed()
{
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
}
else
{
base.OnBackPressed();
}
}
iOS source
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (((CoolContentPage)Element).EnableBackButtonOverride)
{
SetCustomBackButton();
}
}
private void SetCustomBackButton()
{
var backBtnImage = UIImage.FromBundle("iosbackarrow.png");
backBtnImage = backBtnImage.ImageWithRenderingMode
(UIImageRenderingMode.AlwaysTemplate);
var backBtn = new UIButton(UIButtonType.Custom)
{
HorizontalAlignment =
UIControlContentHorizontalAlignment.Left,
TitleEdgeInsets =
new UIEdgeInsets(11.5f, 15f, 10f, 0f),
ImageEdgeInsets =
new UIEdgeInsets(1f, 8f, 0f, 0f)
};
backBtn.SetTitle("Back", UIControlState.Normal);
backBtn.SetTitleColor(UIColor.White, UIControlState.Normal);
backBtn.SetTitleColor(UIColor.LightGray, UIControlState.Highlighted);
backBtn.Font = UIFont.FromName("HelveticaNeue", (nfloat)17);
backBtn.SetImage(backBtnImage, UIControlState.Normal);
backBtn.SizeToFit();
backBtn.TouchDown += (sender, e) =>
{
// Whatever your custom back button click handling
if(((CoolContentPage)Element)?.
CustomBackButtonAction != null)
{
((CoolContentPage)Element)?.
CustomBackButtonAction.Invoke();
}
};
backBtn.Frame = new CGRect(
0,
0,
UIScreen.MainScreen.Bounds.Width / 4,
NavigationController.NavigationBar.Frame.Height);
var btnContainer = new UIView(
new CGRect(0, 0,
backBtn.Frame.Width, backBtn.Frame.Height));
btnContainer.AddSubview(backBtn);
var fixedSpace =
new UIBarButtonItem(UIBarButtonSystemItem.FixedSpace)
{
Width = -16f
};
var backButtonItem = new UIBarButtonItem("",
UIBarButtonItemStyle.Plain, null)
{
CustomView = backBtn
};
NavigationController.TopViewController.NavigationItem.LeftBarButtonItems = new[] { fixedSpace, backButtonItem };
}
using in xamarin forms
public Page2()
{
InitializeComponent();
if (EnableBackButtonOverride)
{
this.CustomBackButtonAction = async () =>
{
var result = await this.DisplayAlert(null, "Go back?" Yes go back", "Nope");
if (result)
{
await Navigation.PopAsync(true);
}
};
}
}

Outlook Event is not getting fired on click of custom button

I am developing a Microsoft Outlook Add-in, where I have added one button in Add-In tab name OPENISMS. I could see the button, however on click the event is not getting fired. I have no clue why it is behaving in this manner. Please find below are code for adding button and attaching event to it. Any help will be highly appreciated.
private void AddButtonToNewDropdown()
{
Office.CommandBar commandBar = this.Application.ActiveExplorer().CommandBars["Standard"];
Office.CommandBarControl ctl = commandBar.Controls["&New"];
if (ctl is Office.CommandBarPopup)
{
Office.CommandBarButton commandBarButton;
Office.CommandBarPopup newpopup = (Office.CommandBarPopup)ctl;
commandBarButton = (Office.CommandBarButton)newpopup.Controls.Add(1, missing, missing, missing, true);
commandBarButton.Caption = "OpenISMS";
commandBarButton.Tag = "OpenISMS";
commandBarButton.FaceId = 6000;
//commandBarButton.Enabled = false;
commandBarButton.OnAction = "OpenISMSThruMail.ThisAddIn.ContextMenuItemClicked";
commandBarButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(ContextMenuItemClicked);
}
}
private void ContextMenuItemClicked(CommandBarButton Ctrl, ref bool CancelDefault)
{
if (currentExplorer.Selection.Count > 0)
{
object selObject = currentExplorer.Selection[1];
if (selObject is MailItem)
{
// do your stuff with the selected message here
MailItem mail = selObject as MailItem;
MessageBox.Show("Message Subject: " + mail.Subject);
}
}
}
I am calling AddButtonToNewDropdown() method from ThisAddIn_Startup event.
You need to make the CommandBarButton a class-member variable in scope - otherwise it will be garbage collected and the event will not fire as you've observed.
public class ThisAddIn
{
Office.CommandBarButton commandBarButton;
private void AddButtonToNewDropdown()
{
// ...
}
}
See related SO post regarding similar issue.

Resources