In Outlook we would need to disable editing of the contacts which were imported from external source (as part of our add-in). To distinguish such contacts we've introduced user-defined property, which we are setting during the import (e.g. ExternalId).
But how can you in the code disable editing functionality for such contacts?
There is no way to do that programmatically.
I'd say there is no trivial way to disable editing contacts in Outlook.
But! You can develop a custom UI which can prevent contacts from editing in most cases - replace the default UI in Inspectors and Explorers. So, users will get your custom UI where you can allow edit contacts or not. See Creating Outlook Form Regions for more information.
You may also find Advanced Outlook view and form regions helpful.
Sure you can disable it!
You can either disable showing the dialog with the contact details (key hint: hook to Application.ItemLoad, there you can cancel the event in case it is a contact you don't want to edit)
Or, preferrably, you can refuse saving of the changes. See sample code.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.Inspectors.NewInspector += Inspectors_NewInspector;
}
private void Inspectors_NewInspector(Outlook.Inspector Inspector)
{
Outlook.ContactItem contact = Inspector.CurrentItem as Outlook.ContactItem;
if (null != contact)
{
if (Utils.IsNonEditableContact(contact))
{
contact.Write += CancelSaveEventForContact;
}
}
}
private void CancelSaveEventForCrmContact(ref bool Cancel)
{
MessageBox.Show("Editing of the this contacts in Outlook is not allowed.", "I'm sorry!", MessageBoxButtons.OK, MessageBoxIcon.Error);
Cancel = true;
}
Related
What I am trying to achieve is to also handle drag and drop events on the calendar properly using the AppProperty Change event on the Inspector:
I update the currentAppointmentItem whenever the user interacts with the interface (Explorer.SelectionChange, NewInspector, CloseInspector, etc.)
I update the currentInspector whenever the user interacts with the interface (SelectionChange, NewInspector, CloseInspector)
Update means that I try to set/unset the event handlers appropriately and to Marshal.ReleaseComObject accordingly. Finally to null the reference.
But when the user just clicks on an AppointmentItem in the calendar no Inspector window is created. Thus I wouldn't be able to catch AppPropertyChange Events. So I decided to call GetInspector on the selected AppointmentItem in case it is not null. I try to use this to receive changes on the AppProperty Event so I can handle drag and drop events on the calendar properly
The problem: From the Microsoft documentation I understand whenever you lose a reference to currentAppointmentItem you should also use Marshal.ReleaseComObject otherwise you risk other problems.
Now I experience exceptions which I cannot catch: RaceOnRCWCleanup ... it seems that I try to release a COM object which is still in use (probably by Outlook). How can I avoid that? Is it correct to Marshal.ReleaseComObject(currentAppointmentItem)
I registered on the SelectionChange Event on the Outlook.Explorer. In there I try to register the currentAppointment with:
[...]
log.Info("Selection_Change");
if (currentExplorer == null)
{
return;
}
try
{
log.Info("Selection_Change: " + currentExplorer.Caption);
Outlook.MAPIFolder selectedFolder = currentExplorer.CurrentFolder;
if (currentExplorer.Selection.Count > 0)
{
Object selObject = currentExplorer.Selection[1];
if (selObject is Outlook.AppointmentItem)
{
currentAppointmentItem = (Outlook.AppointmentItem)selObject;
Inspectors_NewInspector(currentAppointmentItem.GetInspector);
}
[...]
Please Note: INspectors_NewInspector is also called on the Inspectors Collection.
The code of NewInspector is like
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
try
{
log.Info("Inspectors_NewInspector");
// This function (apparently) gets kicked off whenever a user opens a new or existing item
// in Outlook (Calendar appointment, Email, etc).
// We can intercept it, modify it's properties, before letting our Ribbon know about its existance.
//
if (Inspector != null)
{
log.Info("Inspectors_NewInspector: " + Inspector.Caption);
unregisterCurrentInspector();
currentInspector = Inspector;
object item = Inspector.CurrentItem;
if (item == null)
return;
if (!(item is Outlook.AppointmentItem))
return;
unregisterCurrentAppointmentItem();
currentAppointmentItem = (Outlook.AppointmentItem)item;
currentAppointmentItem.PropertyChange += AppPropertyChanged; // Handle situations where the
// user tries to convert an appointment w/ an agreedo protocol to a recurring appointment.
// This needs to be avoided .
currentAppointmentItem.CustomPropertyChange += AppPropertyChanged;
}
((Microsoft.Office.Interop.Outlook.InspectorEvents_10_Event)Inspector).Close += Inspector_Close;
} catch (Exception ex)
{
log.Error(ex.Message);
}
}
the unregisterCurrentApppointmentItem :
private void unregisterCurrentAppointmentItem()
{
try
{
log.Info("unregisterCurrentAppointmentItem");
if (currentAppointmentItem != null)
{
currentAppointmentItem.PropertyChange -= AppPropertyChanged; // Handle situations where the
currentAppointmentItem.CustomPropertyChange -= AppPropertyChanged;
Marshal.ReleaseComObject(currentAppointmentItem);
currentAppointmentItem = null;
}
} catch (Exception ex)
{
log.Error(ex.Message);
}
}
the unregisterCurrentInspector:
private void unregisterCurrentInspector()
{
log.Info("unregisterCurrentInspector");
if (currentInspector != null)
{
((Microsoft.Office.Interop.Outlook.InspectorEvents_10_Event)currentInspector).Close -= Inspector_Close;
Marshal.ReleaseComObject(currentInspector);
currentInspector = null;
}
}
Any advice on this?
What I already tried / taken into account:
Outlook Addin: Moving Appointment in Calendar does not reflect new date/time in AppointmentItem (catch Calendar.ItemChange)
VSTO Outlook Plugin: Cannot get AppointmentItem in Item_Change event when recurring appointment is dragged and dropped by user
First of all, there is no need to simulate the NewInspector event. Instead, you need to set up event handlers correctly. It seems you just need to implement an inspector or explorer wrappers. See Implement a wrapper for inspectors and track item-level events in each inspector for more information.
it seems that I try to release a COM object which is still in use (probably by Outlook). How can I avoid that? Is it correct to Marshal.ReleaseComObject(currentAppointmentItem)
Yes, it is. But you should really use this method against objects retrieved in your code by calling properties and methods. You SHOULD NOT release objects passed as parameters by the Office applications. Take a look a the When to release COM objects in Office add-ins developed in .NET article which explains possible pitfalls and give answers to the most widely spread questions.
Why do you even need the Inspector object? Are you only using the Inspector.Close event?
Use AppointmentItem.Close / Write events.
On my development machine I have the FormInitializing and FormShowing events firing before RibbonLoad. I created a setup package in VS 2010 and installed on a vanilla Windows 7 Ultimate with Outlook 2010 installed.
The addin wasn't appearing on my meeting request form. So I setup remote debugger and to my astonishment the RibbonLoad is firing before the two form events mentioned above. A null exception is being throw b\c the code in the RibbonLoad relies on the FormRegion already being loaded. Can anyone offer any insight?
There is no defined order for certain Outlook events - the Ribbon UI and the Inspector UI are completely different components, despite them both being display in the same window. The Outlook runtime may trigger Ribbon and Inspector events in different orders. It would be your job to synchronize the two events (RibbonLoad and FormInitializing) if you need some initialization done. You cannot assume that the ordering will always be the same.
I notice this same behavior when ThisAddIn.Startup fires before ThisAddIn.CreateRibbonExtensibilityObject, but sometimes after depending on how Outlook triggers the sequencing. You can just use a static variable with sync locking to ensure your initialization code is only triggered once.
Here is an example I used to synchronize the Startup event with the RibbonLoad event:
public partial class ThisAddIn
{
static bool formInitialized = false;
static readonly object padLock = new Object();
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
lock(padLock) { if (!formInitialized ) { InitializeForm(); } }
// startup code
}
private void InitializeForm()
{
// init code
formInitialized = true;
}
protected override IRibbonExtensibility CreateRibbonExtensibilityObject()
{
lock(padLock) { if (!formInitialized) InitializeForm(); }
// Create ribbon UI
}
}
In WP application we need to provide user option to lock app with password.
As I understand WP app lifecycle, I need to put navigation to LockPage in App.Application_Activated, App.Application_Deactivated and start page, but I can not use NavigationService in App class...
I do not want to put navigation code to lock page in each other pages, or there is no other options?
I writed own solution, but may be it is not so elegant as it could be.
App locking logic: User enable app locking with password, we handling Application_Deactivated and Application_Closing events in App class and marking app as locked if user enabled this option. Then, on each page we should put check: is app currently locked and if it is, we should navigate to AppLockedWithPasswordPage. On AppLockedWithPasswordPage we need to check user`s password, if it is correct call NavigationService.GoBack().
So we need to do 6 steps:
You should choose where to save IsAppCurrentlyLocked (bool flag), AppLockPassword (string) and IsUserEnabledAppLockWithPassword (bool flag). I had chosen IsolatedStorageSettings
Create AppLockedWithPassword page, where you need to show TextBox and Button, do not forget to provide option for user to reset AppLock of course with deleting app data
AppLockedWithPasswordPage should prevent BackButton navigation, so preventing it:
// AppLockedWithPasswordPage
protected override void OnBackKeyPress(CancelEventArgs e)
{
// Preventing back key navigation
e.Cancel = true;
}
Check password on button click
// AppLockedWithPasswordPage
private void UnlockAppButton_Click(object sender, RoutedEventArgs e)
{
if (PasswordBox.Password.Equals(IsolatedStorageSettings["AppLockPassword"]))
{
NavigationService.GoBack();
}
else
{
// Say user, that password incorrect, etc...
}
}
In App class find Application_Deactivated (to handle app minimizing (windows button)) and Application_Closing (to handle when user closing app) methods, we should mark app as locked if user enabled this option when this events happens
private void SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking()
{
if ((bool)IsolatedStorageSettings["IsUserEnabledAppLockWithPassword"])
{
IsolatedStorageSettings["IsAppCurrentlyLocked"] = true;
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
SetIsAppCurrentlyLockedFlagIfUserEnabledAppLocking();
}
And final step, on all pages you want to lock you should add check in OnNavigatedTo method which will navigate to AppLockedWithPasswordPage if app is currently locked
// Create some class, like PagesUtils or so on with check method
private static Uri uriToAppLockedWithPasswordPage = new Uri("pathToAppLockedWithPasswordPage", UriKind.Relative);
public static void NavigateToAppLockedWithPasswordPageIfAppLocked(PhoneApplicationPage page)
{
if ((bool)IsolatedStorageSettings["IsAppCurrentlyLocked"])
{
page.NavigationService.Navigate(uriToAppLockedWithPasswordPage);
}
}
// In each page you want to lock add
protected override void OnNavigatedTo(NavigationEventArgs e)
{
PagesUtils.NavigateToAppLockedWithPasswordPageIfAppLocked();
base.OnNavigatedTo();
}
P.S. of course real code is much better, this is just simple example, I hope it will help you
You should add the check in the Application_Launching and Application_Activated events.
The launching event for when the app is first opened and the activated one for when the user returns to the app after having left to do something else.
Have these events both set a flag and have the base page that all your pages inherit from check for this flag when navigated to. The check should be for if the flag is set, if it is, show the login prompt and then clear the flag after successful password entry.
This approach will handle FAS, FAR & deep linking, in addition to starting the app normally.
Beware Some choosers will trigger the activated event when they return to the app. Add extra handling for these as appropriate / if necessary.
Why not create a start page where the passwords is entered?
For instances you have your MainPage.xaml, create a InsertPasswordPage.xaml reference it on WMAppManifest as the start page:
<DefaultTask Name="_default" NavigationPage="InsertPasswordPage.xaml" />
And insert all the password logic on the InsertPasswordPage.xaml, when the user successfully logins just navigate to your main page ;)
EDIT: As Gambit said if the user pressed the back button he will return to the insert password page, but you can solve this by removing from the backstack the page after the user logged in.
Initial Situation:
We are developing an Add-in for Outlook 2010 in C# with VS.NET 2010 based on Framework 4.0, VSTO, DevExpress WinForm Controls. In Designer we have a Ribbon with a RibbonTab, then a RibbonGroup then a RibbonButton. We're consuming WebServices from within this Outlook Add-in.
Objective:
We need to enable/disable the RibbonButtons when the WebService is available/unavailable (from/out of the code)
we've found the following links:
Links
Ribbon Object Model Overview: http://msdn.microsoft.com/en-us/library/bb608623.aspx
Ribbon Overview: http://msdn.microsoft.com/en-us/library/bb386097.aspx
Walkthrough: Updating the Controls on a Ribbon at Run Time: http://msdn.microsoft.com/en-us/library/bb608628.aspx
After hours of trying to figure out how to implement this we deciced to post/ask this question here on SO. Does anybody have a sample code? We tried the IRibbonExtensibility and CreateRibbonExtensibilityObject => we added the RibbonTab, Group and Button and added a subscription to the Click Event => The Event is fired but not handled (in button_Click(...) => System.Diagnostics.Debugger.Break() is not breaking the code execution)
Thanks!
Christian
You'll want to invalidate the Ribbon at a fairly frequent rate in order to refresh the visibility of each tab/button. You can do this by subscribing to the Click event (as you've done) and then calling RibbonObject.Invalidate();. Then add a getEnabled="yourTestFunction" parameter to each button, with public bool yourTestFunction(Office.IRibbonControl control) (Defined in the Ribbon.cs file) returning whether the web service is available or not.
Keep in mind if the web service is down, each click could hang your application for the amount of time you set on your timeout in the web service check
Edit:
Just realized the _Click event isn't mapped in the Excel COM library, so here's a bit of code that will run each time the cell selection is changed (not as frequent as every click, but hopefully good enough).
ThisAddIn.cs:
public static Excel.Application e_application;
public static Office.IRibbonUI e_ribbon;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
e_application = this.Application;
e_application.SheetSelectionChange += new Excel.AppEvents_SheetSelectionChangeEventHandler(e_application_SheetSelectionChange);
}
void e_application_SheetSelectionChange(object Sh, Excel.Range Target)
{
e_ribbon.Invalidate();
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
e_application.SheetSelectionChange -= new Excel.AppEvents_SheetSelectionChangeEventHandler(e_application_SheetSelectionChange);
e_application = null;
}
Ribbon1.cs:
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
{
this.ribbon = ribbonUI;
ThisAddIn.e_ribbon = ribbonUI; //Add this line
}
and
public bool getEnabledTest(Office.IRibbonControl control)
{
//Whatever you use to test your Web Service
//return false;
}
Ribbon1.xml:
<button id="WebService" label="Use The Web Service" onAction="executeWebService" getEnabled="getEnabledTest" />
The following article titled Adding Custom Dynamic Menus to the Office Fluent User Interface will point you in the right direction.
The below is an example of a dynamically created menu, you can modify the tutorial to fit your particular need.
ok, thanks for the tips. Finally i solved it like this:
i declared a static ribbon object like:
public static RibbonIet ribbon {get; set; }
in the load event of the Ribbon i assign the Ribbon (this) like:
Session.Common.ribbon = this;
now i can control the RibbonButton like:
Session.Common.ribbon.buttonCreateIncident.Enabled = true;
Since the webService call is running in a seperate thread, i had to use a MethodInvoker to change enable/disable the buttons. it goes like this:
If (InvokeRequired)
{
Invoke(new MethodInvoker(() => Session.Common.ribbon.buttonCreateIncident.Enabled = true));
}
maybe this is of help for someone else.
I'm making a windows phone app which displays where the closest campus shuttles are (among other things). Windows Phone requires apps to allow the users to turn off location services within the app.
So, I added a toggle for it on a settings page, but it doesn't seem to do anything.
Here's the viewmodel where I declared the geocoordinatewatcher.
public MainViewModel()
{
geoWatcher = new GeoCoordinateWatcher();
if (geoWatcher.TryStart(false, TimeSpan.FromSeconds(30) )==false )
{ MessageBox.Show("The location services are disabled for this app. We can't detect the nearby stops. To turn location services back on, go to the settings page.", "Warning", MessageBoxButton.OK); }
}
private GeoCoordinateWatcher geoWatcher;
public GeoCoordinateWatcher GeoWatcher
{
get
{
return geoWatcher;
}
set
{
if (geoWatcher != value)
{
geoWatcher = value;
NotifyPropertyChanged("GeoWatcher");
}
if(geoWatcher.Status== GeoPositionStatus.Disabled)
{
geoWatcher.Stop();
}
}
}
and here's the bulk of the settings page
public SettingsPage()
{
InitializeComponent();
if (App.ViewModel.GeoWatcher.Status == GeoPositionStatus.Ready)
{
locToggle.IsChecked = true;
locToggle.Content = "On";
}
else
{
locToggle.IsChecked = false;
locToggle.Content = "Off";
}
}
private void toggleChecked(object sender, RoutedEventArgs e)
{
locToggle.Content = "On";
App.ViewModel.GeoWatcher.Start();
MessageBox.Show("this is the status " + App.ViewModel.GeoWatcher.Status.ToString(), "Info", MessageBoxButton.OK); //for debugging
}
private void toggleUnchecked(object sender, RoutedEventArgs e)
{
locToggle.Content = "Off";
App.ViewModel.GeoWatcher.Stop();
MessageBox.Show("this is the status " + App.ViewModel.GeoWatcher.Status.ToString(), "Info", MessageBoxButton.OK); //for debugging
}
When i turn the toggle off and click away from the Settings page, and go back to it, the toggle is re-enabled again.
I tried putting in a message box on the functions to debug but the status always says "Ready" and my app still uses the location services, even when I turn the toggle to "off".
Is there something I should be adding to the code so that the toggle will properly make my app stop using location services across my app if it's disabled on the settings page? Or should I be checking something else besides GeoPositionStatus? I couldn't figure out a way to make my app actually change the location services permissions or PositionStatus.
I looked at this page here, but am still confused since I followed the example at the bottom of the page, to no avail. I searched StackOverflow but I couldn't seem to find a similar question with WP. I also posted this on the AppHub forums though.
Thanks!
In your MainViewModel you need to check if they have allowed location services before you use the geocoordinatewatcher.
if(settings.LocationAllowed)
{all your code for using location}
You should probably take into account a few factors/points, most of which, you have. Anyway, you might find these helpful.
Your application settings toggle should only show when the location service is switched on on the device
GeoPositionStatus is just an Enum which contains the types of statuses.
StatusChanged is the event which is to be handled to check for changes in the device settings. See this.
You cannot change the device settings from the application.
Add event handlers before you call start on the watcher.